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 update: FreeBSD and DragonFly no longer uses the prefix so don't add it.
307 OpenBSD /dev/rcd0c, it needs to be the raw device
308 NetBSD /dev/rcd0[d|c|..] d for x86, c (for non x86), perhaps others
309 Darwin /dev/rdisk0, it needs to be the raw device
310 BSD/OS /dev/sr0c (if not mounted) or /dev/rsr0c ('c' any letter will do)
311 returns a string allocated with strdup. It should be freed when no longer
313 static char *bsd_block2char( const char *path
)
315 #if defined(__FreeBSD__) || defined(__DragonFly__)
316 return (char *) strdup( path
);
320 /* If it doesn't start with "/dev/" or does start with "/dev/r" exit */
321 if( !strncmp( path
, "/dev/", 5 ) || strncmp( path
, "/dev/r", 6 ) )
322 return (char *) strdup( path
);
324 /* Replace "/dev/" with "/dev/r" */
325 new_path
= malloc( strlen(path
) + 2 );
326 strcpy( new_path
, "/dev/r" );
327 strcat( new_path
, path
+ strlen( "/dev/" ) );
330 #endif /* __FreeBSD__ || __DragonFly__ */
335 dvd_reader_t
*DVDOpen( const char *ppath
)
337 struct stat fileinfo
;
338 int ret
, have_css
, retval
, cdir
= -1;
339 dvd_reader_t
*ret_val
= NULL
;
340 char *dev_name
= NULL
;
341 char *path
= NULL
, *new_path
= NULL
, *path_copy
= NULL
;
350 path
= strdup(ppath
);
354 /* Try to open libdvdcss or fall back to standard functions */
355 have_css
= dvdinput_setup();
358 /* Strip off the trailing \ if it is not a drive */
361 (path
[len
- 1] == '\\') &&
362 (path
[len
- 2] != ':'))
368 ret
= stat( path
, &fileinfo
);
372 /* maybe "host:port" url? try opening it with acCeSS library */
373 if( strchr(path
,':') ) {
374 ret_val
= DVDOpenImageFile( path
, have_css
);
379 /* If we can't stat the file, give up */
380 fprintf( stderr
, "libdvdread: Can't stat %s\n", path
);
385 /* First check if this is a block/char device or a file*/
386 if( S_ISBLK( fileinfo
.st_mode
) ||
387 S_ISCHR( fileinfo
.st_mode
) ||
388 S_ISREG( fileinfo
.st_mode
) ) {
391 * Block devices and regular files are assumed to be DVD-Video images.
393 dvd_reader_t
*dvd
= NULL
;
395 dev_name
= sun_block2char( path
);
396 #elif defined(SYS_BSD)
397 dev_name
= bsd_block2char( path
);
399 dev_name
= strdup( path
);
401 dvd
= DVDOpenImageFile( dev_name
, have_css
);
405 } else if( S_ISDIR( fileinfo
.st_mode
) ) {
406 dvd_reader_t
*auth_drive
= 0;
409 #elif defined(__sun) || defined(__linux__)
413 /* XXX: We should scream real loud here. */
414 if( !(path_copy
= strdup( path
) ) )
417 #ifndef WIN32 /* don't have fchdir, and getcwd( NULL, ... ) is strange */
418 /* Also WIN32 does not have symlinks, so we don't need this bit of code. */
420 /* Resolve any symlinks and get the absolute dir name. */
422 if( ( cdir
= open( ".", O_RDONLY
) ) >= 0 ) {
423 if( chdir( path_copy
) == -1 ) {
426 new_path
= malloc(PATH_MAX
+1);
430 if( getcwd( new_path
, PATH_MAX
) == NULL
) {
433 retval
= fchdir( cdir
);
439 path_copy
= new_path
;
446 * If we're being asked to open a directory, check if that directory
447 * is the mount point for a DVD-ROM which we can use instead.
450 if( strlen( path_copy
) > 1 ) {
451 if( path_copy
[ strlen( path_copy
) - 1 ] == '/' ) {
452 path_copy
[ strlen( path_copy
) - 1 ] = '\0';
456 if( strlen( path_copy
) > TITLES_MAX
) {
457 if( !strcasecmp( &(path_copy
[ strlen( path_copy
) - TITLES_MAX
]),
459 path_copy
[ strlen( path_copy
) - TITLES_MAX
] = '\0';
463 if(path_copy
[0] == '\0') {
469 if( ( fe
= getfsfile( path_copy
) ) ) {
470 dev_name
= bsd_block2char( fe
->fs_spec
);
472 "libdvdread: Attempting to use device %s"
473 " mounted on %s for CSS authentication\n",
476 auth_drive
= DVDOpenImageFile( dev_name
, have_css
);
479 mntfile
= fopen( MNTTAB
, "r" );
484 while( ( res
= getmntent( mntfile
, &mp
) ) != -1 ) {
485 if( res
== 0 && !strcmp( mp
.mnt_mountp
, path_copy
) ) {
486 dev_name
= sun_block2char( mp
.mnt_special
);
488 "libdvdread: Attempting to use device %s"
489 " mounted on %s for CSS authentication\n",
492 auth_drive
= DVDOpenImageFile( dev_name
, have_css
);
498 #elif defined(__linux__)
499 mntfile
= fopen( MOUNTED
, "r" );
503 while( ( me
= getmntent( mntfile
) ) ) {
504 if( !strcmp( me
->mnt_dir
, path_copy
) ) {
506 "libdvdread: Attempting to use device %s"
507 " mounted on %s for CSS authentication\n",
510 auth_drive
= DVDOpenImageFile( me
->mnt_fsname
, have_css
);
511 dev_name
= strdup(me
->mnt_fsname
);
517 #elif defined(_WIN32) || defined(__OS2__)
518 auth_drive
= DVDOpenImageFile( path
, have_css
);
521 #if !defined(_WIN32) && !defined(__OS2__)
523 fprintf( stderr
, "libdvdread: Couldn't find device name.\n" );
524 } else if( !auth_drive
) {
525 fprintf( stderr
, "libdvdread: Device %s inaccessible, "
526 "CSS authentication not available.\n", dev_name
);
530 fprintf( stderr
, "libdvdread: Device %s inaccessible, "
531 "CSS authentication not available.\n", dev_name
);
541 * If we've opened a drive, just use that.
548 * Otherwise, we now try to open the directory tree instead.
550 ret_val
= DVDOpenPath( path
);
556 /* If it's none of the above, screw it. */
557 fprintf( stderr
, "libdvdread: Could not open %s\n", path
);
560 if ( path_copy
!= NULL
)
564 if ( new_path
!= NULL
)
569 void DVDClose( dvd_reader_t
*dvd
)
572 if( dvd
->dev
) dvdinput_close( dvd
->dev
);
573 if( dvd
->path_root
) free( dvd
->path_root
);
574 if( dvd
->udfcache
) FreeUDFCache( dvd
->udfcache
);
580 * Open an unencrypted file on a DVD image file.
582 static dvd_file_t
*DVDOpenFileUDF( dvd_reader_t
*dvd
, char *filename
)
585 dvd_file_t
*dvd_file
;
587 start
= UDFFindFile( dvd
, filename
, &len
);
589 fprintf( stderr
, "libdvdnav:DVDOpenFileUDF:UDFFindFile %s failed\n", filename
);
593 dvd_file
= (dvd_file_t
*) malloc( sizeof( dvd_file_t
) );
595 fprintf( stderr
, "libdvdnav:DVDOpenFileUDF:malloc failed\n" );
599 dvd_file
->lb_start
= start
;
600 dvd_file
->seek_pos
= 0;
601 memset( dvd_file
->title_sizes
, 0, sizeof( dvd_file
->title_sizes
) );
602 memset( dvd_file
->title_devs
, 0, sizeof( dvd_file
->title_devs
) );
603 dvd_file
->filesize
= len
/ DVD_VIDEO_LB_LEN
;
609 * Searches for <file> in directory <path>, ignoring case.
610 * Returns 0 and full filename in <filename>.
611 * or -1 on file not found.
612 * or -2 on path not found.
614 static int findDirFile( const char *path
, const char *file
, char *filename
)
619 dir
= opendir( path
);
620 if( !dir
) return -2;
622 while( ( ent
= readdir( dir
) ) != NULL
) {
623 if( !strcasecmp( ent
->d_name
, file
) ) {
624 sprintf( filename
, "%s%s%s", path
,
625 ( ( path
[ strlen( path
) - 1 ] == '/' ) ? "" : "/" ),
635 static int findDVDFile( dvd_reader_t
*dvd
, const char *file
, char *filename
)
637 char video_path
[ PATH_MAX
+ 1 ];
638 const char *nodirfile
;
641 /* Strip off the directory for our search */
642 if( !strncasecmp( "/VIDEO_TS/", file
, 10 ) ) {
643 nodirfile
= &(file
[ 10 ]);
648 ret
= findDirFile( dvd
->path_root
, nodirfile
, filename
);
650 /* Try also with adding the path, just in case. */
651 sprintf( video_path
, "%s/VIDEO_TS/", dvd
->path_root
);
652 ret
= findDirFile( video_path
, nodirfile
, filename
);
654 /* Try with the path, but in lower case. */
655 sprintf( video_path
, "%s/video_ts/", dvd
->path_root
);
656 ret
= findDirFile( video_path
, nodirfile
, filename
);
667 * Open an unencrypted file from a DVD directory tree.
669 static dvd_file_t
*DVDOpenFilePath( dvd_reader_t
*dvd
, char *filename
)
671 char full_path
[ PATH_MAX
+ 1 ];
672 dvd_file_t
*dvd_file
;
673 struct stat fileinfo
;
676 /* Get the full path of the file. */
677 if( !findDVDFile( dvd
, filename
, full_path
) ) {
678 fprintf( stderr
, "libdvdnav:DVDOpenFilePath:findDVDFile %s failed\n", filename
);
682 dev
= dvdinput_open( full_path
);
684 fprintf( stderr
, "libdvdnav:DVDOpenFilePath:dvdinput_open %s failed\n", full_path
);
688 dvd_file
= (dvd_file_t
*) malloc( sizeof( dvd_file_t
) );
690 fprintf( stderr
, "libdvdnav:DVDOpenFilePath:dvd_file malloc failed\n" );
695 dvd_file
->lb_start
= 0;
696 dvd_file
->seek_pos
= 0;
697 memset( dvd_file
->title_sizes
, 0, sizeof( dvd_file
->title_sizes
) );
698 memset( dvd_file
->title_devs
, 0, sizeof( dvd_file
->title_devs
) );
699 dvd_file
->filesize
= 0;
701 if( stat( full_path
, &fileinfo
) < 0 ) {
702 fprintf( stderr
, "libdvdread: Can't stat() %s.\n", filename
);
706 dvd_file
->title_sizes
[ 0 ] = fileinfo
.st_size
/ DVD_VIDEO_LB_LEN
;
707 dvd_file
->title_devs
[ 0 ] = dev
;
708 dvd_file
->filesize
= dvd_file
->title_sizes
[ 0 ];
713 static dvd_file_t
*DVDOpenVOBUDF( dvd_reader_t
*dvd
, int title
, int menu
)
715 char filename
[ MAX_UDF_FILE_NAME_LEN
];
717 dvd_file_t
*dvd_file
;
720 sprintf( filename
, "/VIDEO_TS/VIDEO_TS.VOB" );
722 sprintf( filename
, "/VIDEO_TS/VTS_%02d_%d.VOB", title
, menu
? 0 : 1 );
724 start
= UDFFindFile( dvd
, filename
, &len
);
725 if( start
== 0 ) return NULL
;
727 dvd_file
= (dvd_file_t
*) malloc( sizeof( dvd_file_t
) );
728 if( !dvd_file
) return NULL
;
730 /*Hack*/ dvd_file
->css_title
= title
<< 1 | menu
;
731 dvd_file
->lb_start
= start
;
732 dvd_file
->seek_pos
= 0;
733 memset( dvd_file
->title_sizes
, 0, sizeof( dvd_file
->title_sizes
) );
734 memset( dvd_file
->title_devs
, 0, sizeof( dvd_file
->title_devs
) );
735 dvd_file
->filesize
= len
/ DVD_VIDEO_LB_LEN
;
737 /* Calculate the complete file size for every file in the VOBS */
741 for( cur
= 2; cur
< 10; cur
++ ) {
742 sprintf( filename
, "/VIDEO_TS/VTS_%02d_%d.VOB", title
, cur
);
743 if( !UDFFindFile( dvd
, filename
, &len
) ) break;
744 dvd_file
->filesize
+= len
/ DVD_VIDEO_LB_LEN
;
748 if( dvd
->css_state
== 1 /* Need key init */ ) {
749 initAllCSSKeys( dvd
);
753 if( dvdinput_title( dvd_file->dvd->dev, (int)start ) < 0 ) {
754 fprintf( stderr, "libdvdread: Error cracking CSS key for %s\n",
762 static dvd_file_t
*DVDOpenVOBPath( dvd_reader_t
*dvd
, int title
, int menu
)
764 char filename
[ MAX_UDF_FILE_NAME_LEN
];
765 char full_path
[ PATH_MAX
+ 1 ];
766 struct stat fileinfo
;
767 dvd_file_t
*dvd_file
;
770 dvd_file
= (dvd_file_t
*) malloc( sizeof( dvd_file_t
) );
771 if( !dvd_file
) return NULL
;
773 /*Hack*/ dvd_file
->css_title
= title
<< 1 | menu
;
774 dvd_file
->lb_start
= 0;
775 dvd_file
->seek_pos
= 0;
776 memset( dvd_file
->title_sizes
, 0, sizeof( dvd_file
->title_sizes
) );
777 memset( dvd_file
->title_devs
, 0, sizeof( dvd_file
->title_devs
) );
778 dvd_file
->filesize
= 0;
784 sprintf( filename
, "VIDEO_TS.VOB" );
786 sprintf( filename
, "VTS_%02i_0.VOB", title
);
788 if( !findDVDFile( dvd
, filename
, full_path
) ) {
793 dev
= dvdinput_open( full_path
);
799 if( stat( full_path
, &fileinfo
) < 0 ) {
800 fprintf( stderr
, "libdvdread: Can't stat() %s.\n", filename
);
805 dvd_file
->title_sizes
[ 0 ] = fileinfo
.st_size
/ DVD_VIDEO_LB_LEN
;
806 dvd_file
->title_devs
[ 0 ] = dev
;
807 dvdinput_title( dvd_file
->title_devs
[0], 0);
808 dvd_file
->filesize
= dvd_file
->title_sizes
[ 0 ];
811 for( i
= 0; i
< TITLES_MAX
; ++i
) {
813 sprintf( filename
, "VTS_%02i_%i.VOB", title
, i
+ 1 );
814 if( !findDVDFile( dvd
, filename
, full_path
) ) {
818 if( stat( full_path
, &fileinfo
) < 0 ) {
819 fprintf( stderr
, "libdvdread: Can't stat() %s.\n", filename
);
823 dvd_file
->title_sizes
[ i
] = fileinfo
.st_size
/ DVD_VIDEO_LB_LEN
;
824 dvd_file
->title_devs
[ i
] = dvdinput_open( full_path
);
825 dvdinput_title( dvd_file
->title_devs
[ i
], 0 );
826 dvd_file
->filesize
+= dvd_file
->title_sizes
[ i
];
828 if( !dvd_file
->title_devs
[ 0 ] ) {
837 dvd_file_t
*DVDOpenFile( dvd_reader_t
*dvd
, int titlenum
,
838 dvd_read_domain_t domain
)
840 char filename
[ MAX_UDF_FILE_NAME_LEN
];
842 /* Check arguments. */
843 if( dvd
== NULL
|| titlenum
< 0 )
847 case DVD_READ_INFO_FILE
:
848 if( titlenum
== 0 ) {
849 sprintf( filename
, "/VIDEO_TS/VIDEO_TS.IFO" );
851 sprintf( filename
, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum
);
854 case DVD_READ_INFO_BACKUP_FILE
:
855 if( titlenum
== 0 ) {
856 sprintf( filename
, "/VIDEO_TS/VIDEO_TS.BUP" );
858 sprintf( filename
, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum
);
861 case DVD_READ_MENU_VOBS
:
862 if( dvd
->isImageFile
) {
863 return DVDOpenVOBUDF( dvd
, titlenum
, 1 );
865 return DVDOpenVOBPath( dvd
, titlenum
, 1 );
868 case DVD_READ_TITLE_VOBS
:
869 if( titlenum
== 0 ) return 0;
870 if( dvd
->isImageFile
) {
871 return DVDOpenVOBUDF( dvd
, titlenum
, 0 );
873 return DVDOpenVOBPath( dvd
, titlenum
, 0 );
877 fprintf( stderr
, "libdvdread: Invalid domain for file open.\n" );
881 if( dvd
->isImageFile
) {
882 return DVDOpenFileUDF( dvd
, filename
);
884 return DVDOpenFilePath( dvd
, filename
);
888 void DVDCloseFile( dvd_file_t
*dvd_file
)
893 if( !dvd_file
->dvd
->isImageFile
) {
894 for( i
= 0; i
< TITLES_MAX
; ++i
) {
895 if( dvd_file
->title_devs
[ i
] ) {
896 dvdinput_close( dvd_file
->title_devs
[i
] );
906 static int DVDFileStatVOBUDF( dvd_reader_t
*dvd
, int title
,
907 int menu
, dvd_stat_t
*statbuf
)
909 char filename
[ MAX_UDF_FILE_NAME_LEN
];
912 off_t parts_size
[ 9 ];
917 sprintf( filename
, "/VIDEO_TS/VIDEO_TS.VOB" );
919 sprintf( filename
, "/VIDEO_TS/VTS_%02d_%d.VOB", title
, menu
? 0 : 1 );
921 if( !UDFFindFile( dvd
, filename
, &size
) )
926 parts_size
[ 0 ] = size
;
931 for( cur
= 2; cur
< 10; cur
++ ) {
932 sprintf( filename
, "/VIDEO_TS/VTS_%02d_%d.VOB", title
, cur
);
933 if( !UDFFindFile( dvd
, filename
, &size
) )
936 parts_size
[ nr_parts
] = size
;
942 statbuf
->size
= tot_size
;
943 statbuf
->nr_parts
= nr_parts
;
944 for( n
= 0; n
< nr_parts
; n
++ )
945 statbuf
->parts_size
[ n
] = parts_size
[ n
];
951 static int DVDFileStatVOBPath( dvd_reader_t
*dvd
, int title
,
952 int menu
, dvd_stat_t
*statbuf
)
954 char filename
[ MAX_UDF_FILE_NAME_LEN
];
955 char full_path
[ PATH_MAX
+ 1 ];
956 struct stat fileinfo
;
958 off_t parts_size
[ 9 ];
963 sprintf( filename
, "VIDEO_TS.VOB" );
965 sprintf( filename
, "VTS_%02d_%d.VOB", title
, menu
? 0 : 1 );
967 if( !findDVDFile( dvd
, filename
, full_path
) )
970 if( stat( full_path
, &fileinfo
) < 0 ) {
971 fprintf( stderr
, "libdvdread: Can't stat() %s.\n", filename
);
975 tot_size
= fileinfo
.st_size
;
977 parts_size
[ 0 ] = fileinfo
.st_size
;
981 for( cur
= 2; cur
< 10; cur
++ ) {
982 sprintf( filename
, "VTS_%02d_%d.VOB", title
, cur
);
983 if( !findDVDFile( dvd
, filename
, full_path
) )
986 if( stat( full_path
, &fileinfo
) < 0 ) {
987 fprintf( stderr
, "libdvdread: Can't stat() %s.\n", filename
);
991 parts_size
[ nr_parts
] = fileinfo
.st_size
;
992 tot_size
+= parts_size
[ nr_parts
];
997 statbuf
->size
= tot_size
;
998 statbuf
->nr_parts
= nr_parts
;
999 for( n
= 0; n
< nr_parts
; n
++ )
1000 statbuf
->parts_size
[ n
] = parts_size
[ n
];
1006 int DVDFileStat( dvd_reader_t
*dvd
, int titlenum
,
1007 dvd_read_domain_t domain
, dvd_stat_t
*statbuf
)
1009 char filename
[ MAX_UDF_FILE_NAME_LEN
];
1010 char full_path
[ PATH_MAX
+ 1 ];
1011 struct stat fileinfo
;
1014 /* Check arguments. */
1015 if( dvd
== NULL
|| titlenum
< 0 ) {
1021 case DVD_READ_INFO_FILE
:
1023 sprintf( filename
, "/VIDEO_TS/VIDEO_TS.IFO" );
1025 sprintf( filename
, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum
);
1028 case DVD_READ_INFO_BACKUP_FILE
:
1030 sprintf( filename
, "/VIDEO_TS/VIDEO_TS.BUP" );
1032 sprintf( filename
, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum
);
1035 case DVD_READ_MENU_VOBS
:
1036 if( dvd
->isImageFile
)
1037 return DVDFileStatVOBUDF( dvd
, titlenum
, 1, statbuf
);
1039 return DVDFileStatVOBPath( dvd
, titlenum
, 1, statbuf
);
1042 case DVD_READ_TITLE_VOBS
:
1046 if( dvd
->isImageFile
)
1047 return DVDFileStatVOBUDF( dvd
, titlenum
, 0, statbuf
);
1049 return DVDFileStatVOBPath( dvd
, titlenum
, 0, statbuf
);
1053 fprintf( stderr
, "libdvdread: Invalid domain for file stat.\n" );
1058 if( dvd
->isImageFile
) {
1059 if( UDFFindFile( dvd
, filename
, &size
) ) {
1060 statbuf
->size
= size
;
1061 statbuf
->nr_parts
= 1;
1062 statbuf
->parts_size
[ 0 ] = size
;
1066 if( findDVDFile( dvd
, filename
, full_path
) ) {
1067 if( stat( full_path
, &fileinfo
) < 0 )
1068 fprintf( stderr
, "libdvdread: Can't stat() %s.\n", filename
);
1070 statbuf
->size
= fileinfo
.st_size
;
1071 statbuf
->nr_parts
= 1;
1072 statbuf
->parts_size
[ 0 ] = statbuf
->size
;
1080 /* Internal, but used from dvd_udf.c */
1081 int UDFReadBlocksRaw( dvd_reader_t
*device
, uint32_t lb_number
,
1082 size_t block_count
, unsigned char *data
,
1087 if( !device
->dev
) {
1088 fprintf( stderr
, "libdvdread: Fatal error in block read.\n" );
1092 ret
= dvdinput_seek( device
->dev
, (int) lb_number
);
1093 if( ret
!= (int) lb_number
) {
1094 fprintf( stderr
, "libdvdread: Can't seek to block %u\n", lb_number
);
1098 ret
= dvdinput_read( device
->dev
, (char *) data
,
1099 (int) block_count
, encrypted
);
1103 /* This is using a single input and starting from 'dvd_file->lb_start' offset.
1105 * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
1106 * into the buffer located at 'data' and if 'encrypted' is set
1107 * descramble the data if it's encrypted. Returning either an
1108 * negative error or the number of blocks read. */
1109 static int DVDReadBlocksUDF( dvd_file_t
*dvd_file
, uint32_t offset
,
1110 size_t block_count
, unsigned char *data
,
1113 return UDFReadBlocksRaw( dvd_file
->dvd
, dvd_file
->lb_start
+ offset
,
1114 block_count
, data
, encrypted
);
1117 /* This is using possibly several inputs and starting from an offset of '0'.
1119 * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
1120 * into the buffer located at 'data' and if 'encrypted' is set
1121 * descramble the data if it's encrypted. Returning either an
1122 * negative error or the number of blocks read. */
1123 static int DVDReadBlocksPath( dvd_file_t
*dvd_file
, unsigned int offset
,
1124 size_t block_count
, unsigned char *data
,
1132 for( i
= 0; i
< TITLES_MAX
; ++i
) {
1133 if( !dvd_file
->title_sizes
[ i
] ) return 0; /* Past end of file */
1135 if( offset
< dvd_file
->title_sizes
[ i
] ) {
1136 if( ( offset
+ block_count
) <= dvd_file
->title_sizes
[ i
] ) {
1137 off
= dvdinput_seek( dvd_file
->title_devs
[ i
], (int)offset
);
1138 if( off
< 0 || off
!= (int)offset
) {
1139 fprintf( stderr
, "libdvdread: Can't seek to block %d\n",
1141 return off
< 0 ? off
: 0;
1143 ret
= dvdinput_read( dvd_file
->title_devs
[ i
], data
,
1144 (int)block_count
, encrypted
);
1147 size_t part1_size
= dvd_file
->title_sizes
[ i
] - offset
;
1148 /* FIXME: Really needs to be a while loop.
1149 * (This is only true if you try and read >1GB at a time) */
1152 off
= dvdinput_seek( dvd_file
->title_devs
[ i
], (int)offset
);
1153 if( off
< 0 || off
!= (int)offset
) {
1154 fprintf( stderr
, "libdvdread: Can't seek to block %d\n",
1156 return off
< 0 ? off
: 0;
1158 ret
= dvdinput_read( dvd_file
->title_devs
[ i
], data
,
1159 (int)part1_size
, encrypted
);
1160 if( ret
< 0 ) return ret
;
1161 /* FIXME: This is wrong if i is the last file in the set.
1162 * also error from this read will not show in ret. */
1164 /* Does the next part exist? If not then return now. */
1165 if( i
+ 1 >= TITLES_MAX
|| !dvd_file
->title_devs
[ i
+ 1 ] )
1169 off
= dvdinput_seek( dvd_file
->title_devs
[ i
+ 1 ], 0 );
1170 if( off
< 0 || off
!= 0 ) {
1171 fprintf( stderr
, "libdvdread: Can't seek to block %d\n",
1173 return off
< 0 ? off
: 0;
1175 ret2
= dvdinput_read( dvd_file
->title_devs
[ i
+ 1 ],
1177 * (int64_t)DVD_VIDEO_LB_LEN
),
1178 (int)(block_count
- part1_size
),
1180 if( ret2
< 0 ) return ret2
;
1184 offset
-= dvd_file
->title_sizes
[ i
];
1191 /* This is broken reading more than 2Gb at a time is ssize_t is 32-bit. */
1192 ssize_t
DVDReadBlocks( dvd_file_t
*dvd_file
, int offset
,
1193 size_t block_count
, unsigned char *data
)
1197 /* Check arguments. */
1198 if( dvd_file
== NULL
|| offset
< 0 || data
== NULL
)
1201 /* Hack, and it will still fail for multiple opens in a threaded app ! */
1202 if( dvd_file
->dvd
->css_title
!= dvd_file
->css_title
) {
1203 dvd_file
->dvd
->css_title
= dvd_file
->css_title
;
1204 if( dvd_file
->dvd
->isImageFile
) {
1205 dvdinput_title( dvd_file
->dvd
->dev
, (int)dvd_file
->lb_start
);
1207 /* Here each vobu has it's own dvdcss handle, so no need to update
1209 dvdinput_title( dvd_file->title_devs[ 0 ], (int)dvd_file->lb_start );
1213 if( dvd_file
->dvd
->isImageFile
) {
1214 ret
= DVDReadBlocksUDF( dvd_file
, (uint32_t)offset
,
1215 block_count
, data
, DVDINPUT_READ_DECRYPT
);
1217 ret
= DVDReadBlocksPath( dvd_file
, (unsigned int)offset
,
1218 block_count
, data
, DVDINPUT_READ_DECRYPT
);
1221 return (ssize_t
)ret
;
1224 int32_t DVDFileSeek( dvd_file_t
*dvd_file
, int32_t offset
)
1226 /* Check arguments. */
1227 if( dvd_file
== NULL
|| offset
< 0 )
1230 if( offset
> dvd_file
->filesize
* DVD_VIDEO_LB_LEN
) {
1233 dvd_file
->seek_pos
= (uint32_t) offset
;
1237 int DVDFileSeekForce(dvd_file_t
*dvd_file
, int offset
, int force_size
)
1239 /* Check arguments. */
1240 if( dvd_file
== NULL
|| offset
<= 0 )
1243 if( dvd_file
->dvd
->isImageFile
) {
1244 if( force_size
< 0 )
1245 force_size
= (offset
- 1) / DVD_VIDEO_LB_LEN
+ 1;
1246 if( dvd_file
->filesize
< force_size
) {
1247 dvd_file
->filesize
= force_size
;
1248 fprintf(stderr
, "libdvdread: Ignored size of file indicated in UDF.\n");
1252 if( offset
> dvd_file
->filesize
* DVD_VIDEO_LB_LEN
)
1255 dvd_file
->seek_pos
= (uint32_t) offset
;
1259 ssize_t
DVDReadBytes( dvd_file_t
*dvd_file
, void *data
, size_t byte_size
)
1261 unsigned char *secbuf_base
, *secbuf
;
1262 unsigned int numsec
, seek_sector
, seek_byte
;
1265 /* Check arguments. */
1266 if( dvd_file
== NULL
|| data
== NULL
)
1269 seek_sector
= dvd_file
->seek_pos
/ DVD_VIDEO_LB_LEN
;
1270 seek_byte
= dvd_file
->seek_pos
% DVD_VIDEO_LB_LEN
;
1272 numsec
= ( ( seek_byte
+ byte_size
) / DVD_VIDEO_LB_LEN
) +
1273 ( ( ( seek_byte
+ byte_size
) % DVD_VIDEO_LB_LEN
) ? 1 : 0 );
1275 secbuf_base
= (unsigned char *) malloc( numsec
* DVD_VIDEO_LB_LEN
+ 2048 );
1276 secbuf
= (unsigned char *)(((uintptr_t)secbuf_base
& ~((uintptr_t)2047)) + 2048);
1277 if( !secbuf_base
) {
1278 fprintf( stderr
, "libdvdread: Can't allocate memory "
1279 "for file read!\n" );
1283 if( dvd_file
->dvd
->isImageFile
) {
1284 ret
= DVDReadBlocksUDF( dvd_file
, (uint32_t) seek_sector
,
1285 (size_t) numsec
, secbuf
, DVDINPUT_NOFLAGS
);
1287 ret
= DVDReadBlocksPath( dvd_file
, seek_sector
,
1288 (size_t) numsec
, secbuf
, DVDINPUT_NOFLAGS
);
1291 if( ret
!= (int) numsec
) {
1292 free( secbuf_base
);
1293 return ret
< 0 ? ret
: 0;
1296 memcpy( data
, &(secbuf
[ seek_byte
]), byte_size
);
1297 free( secbuf_base
);
1299 DVDFileSeekForce(dvd_file
, dvd_file
->seek_pos
+ byte_size
, -1);
1303 ssize_t
DVDFileSize( dvd_file_t
*dvd_file
)
1305 /* Check arguments. */
1306 if( dvd_file
== NULL
)
1309 return dvd_file
->filesize
;
1312 int DVDDiscID( dvd_reader_t
*dvd
, unsigned char *discid
)
1316 int nr_of_files
= 0;
1318 /* Check arguments. */
1319 if( dvd
== NULL
|| discid
== NULL
)
1322 /* Go through the first 10 IFO:s, in order,
1323 * and md5sum them, i.e VIDEO_TS.IFO and VTS_0?_0.IFO */
1324 md5_init_ctx( &ctx
);
1325 for( title
= 0; title
< 10; title
++ ) {
1326 dvd_file_t
*dvd_file
= DVDOpenFile( dvd
, title
, DVD_READ_INFO_FILE
);
1327 if( dvd_file
!= NULL
) {
1329 size_t file_size
= dvd_file
->filesize
* DVD_VIDEO_LB_LEN
;
1330 char *buffer_base
= malloc( file_size
+ 2048 );
1331 char *buffer
= (char *)(((uintptr_t)buffer_base
& ~((uintptr_t)2047)) + 2048);
1333 if( buffer_base
== NULL
) {
1334 DVDCloseFile( dvd_file
);
1335 fprintf( stderr
, "libdvdread: DVDDiscId, failed to "
1336 "allocate memory for file read!\n" );
1340 bytes_read
= DVDReadBytes( dvd_file
, buffer
, file_size
);
1341 if( bytes_read
!= file_size
) {
1342 fprintf( stderr
, "libdvdread: DVDDiscId read returned %zd bytes"
1343 ", wanted %zd\n", bytes_read
, file_size
);
1344 DVDCloseFile( dvd_file
);
1345 free( buffer_base
);
1349 md5_process_bytes( buffer
, file_size
, &ctx
);
1351 DVDCloseFile( dvd_file
);
1352 free( buffer_base
);
1356 md5_finish_ctx( &ctx
, discid
);
1364 int DVDISOVolumeInfo( dvd_reader_t
*dvd
,
1365 char *volid
, unsigned int volid_size
,
1366 unsigned char *volsetid
, unsigned int volsetid_size
)
1368 unsigned char *buffer
, *buffer_base
;
1371 /* Check arguments. */
1375 if( dvd
->dev
== NULL
) {
1376 /* No block access, so no ISO... */
1380 buffer_base
= malloc( DVD_VIDEO_LB_LEN
+ 2048 );
1381 buffer
= (unsigned char *)(((uintptr_t)buffer_base
& ~((uintptr_t)2047)) + 2048);
1383 if( buffer_base
== NULL
) {
1384 fprintf( stderr
, "libdvdread: DVDISOVolumeInfo, failed to "
1385 "allocate memory for file read!\n" );
1389 ret
= UDFReadBlocksRaw( dvd
, 16, 1, buffer
, 0 );
1391 fprintf( stderr
, "libdvdread: DVDISOVolumeInfo, failed to "
1392 "read ISO9660 Primary Volume Descriptor!\n" );
1393 free( buffer_base
);
1397 if( (volid
!= NULL
) && (volid_size
> 0) ) {
1399 for(n
= 0; n
< 32; n
++) {
1400 if(buffer
[40+n
] == 0x20) {
1405 if(volid_size
> n
+1) {
1409 memcpy(volid
, &buffer
[40], volid_size
-1);
1410 volid
[volid_size
-1] = '\0';
1413 if( (volsetid
!= NULL
) && (volsetid_size
> 0) ) {
1414 if(volsetid_size
> 128) {
1415 volsetid_size
= 128;
1417 memcpy(volsetid
, &buffer
[190], volsetid_size
);
1419 free( buffer_base
);
1424 int DVDUDFVolumeInfo( dvd_reader_t
*dvd
,
1425 char *volid
, unsigned int volid_size
,
1426 unsigned char *volsetid
, unsigned int volsetid_size
)
1429 /* Check arguments. */
1433 if( dvd
->dev
== NULL
) {
1434 /* No block access, so no UDF VolumeSet Identifier */
1438 if( (volid
!= NULL
) && (volid_size
> 0) ) {
1439 ret
= UDFGetVolumeIdentifier(dvd
, volid
, volid_size
);
1444 if( (volsetid
!= NULL
) && (volsetid_size
> 0) ) {
1445 ret
= UDFGetVolumeSetIdentifier(dvd
, volsetid
, volsetid_size
);