comparison dvdread/dvd_reader.c @ 20981:22cb9d5f1e21

Rename libdvdread to dvdread. We really only include only the dvdread subdirectory of libdvdread. This will also allow getting rid of some local modifications.
author diego
date Sat, 18 Nov 2006 00:33:01 +0000
parents libdvdread/dvd_reader.c@11731a2cbc11
children de28f9e8cb00
comparison
equal deleted inserted replaced
20980:70ca50bcc4a8 20981:22cb9d5f1e21
1 /*
2 * Copyright (C) 2001, 2002, 2003 Billy Biggs <vektor@dumbterm.net>,
3 * Håkan Hjort <d95hjort@dtek.chalmers.se>,
4 * Björn Englund <d4bjorn@dtek.chalmers.se>
5 *
6 * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
7 * detailed changelog at http://svn.mplayerhq.hu/mplayer/trunk/
8 * $Id$
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or (at
13 * your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23 */
24
25 #include "config.h"
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/time.h> /* For the timing of dvdcss_title crack. */
30 #include <fcntl.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <limits.h>
37 #include <dirent.h>
38
39 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__)|| defined(__DARWIN__) || defined(__DragonFly__)
40 #define SYS_BSD 1
41 #endif
42
43 #if defined(__sun)
44 #include <sys/mnttab.h>
45 #elif defined(hpux)
46 #include </usr/conf/h/mnttab.h>
47 #elif defined(SYS_BSD)
48 #include <fstab.h>
49 #elif defined(__linux__) || defined(__CYGWIN__)
50 #include <mntent.h>
51 #endif
52
53 #if defined(__MINGW32__) && (__MINGW32_MAJOR_VERSION <= 3) && (__MINGW32_MINOR_VERSION < 10)
54 #include <sys/timeb.h>
55 static void gettimeofday(struct timeval* t,void* timezone){
56 struct timeb timebuffer;
57 ftime( &timebuffer );
58 t->tv_sec=timebuffer.time;
59 t->tv_usec=1000*timebuffer.millitm;
60 }
61 #endif
62
63 #include "dvd_udf.h"
64 #include "dvd_input.h"
65 #include "dvd_reader.h"
66 #include "md5.h"
67
68 #define DEFAULT_UDF_CACHE_LEVEL 0
69
70 struct dvd_reader_s {
71 /* Basic information. */
72 int isImageFile;
73
74 /* Hack for keeping track of the css status.
75 * 0: no css, 1: perhaps (need init of keys), 2: have done init */
76 int css_state;
77 int css_title; /* Last title that we have called dvdinpute_title for. */
78
79 /* Information required for an image file. */
80 dvd_input_t dev;
81
82 /* Information required for a directory path drive. */
83 char *path_root;
84
85 /* Filesystem cache */
86 int udfcache_level; /* 0 - turned off, 1 - on */
87 void *udfcache;
88 };
89
90 struct dvd_file_s {
91 /* Basic information. */
92 dvd_reader_t *dvd;
93
94 /* Hack for selecting the right css title. */
95 int css_title;
96
97 /* Information required for an image file. */
98 uint32_t lb_start;
99 uint32_t seek_pos;
100
101 /* Information required for a directory path drive. */
102 size_t title_sizes[ 9 ];
103 dvd_input_t title_devs[ 9 ];
104
105 /* Calculated at open-time, size in blocks. */
106 ssize_t filesize;
107 };
108
109 /**
110 * Set the level of caching on udf
111 * level = 0 (no caching)
112 * level = 1 (caching filesystem info)
113 */
114 int DVDUDFCacheLevel(dvd_reader_t *device, int level)
115 {
116 struct dvd_reader_s *dev = (struct dvd_reader_s *)device;
117
118 if(level > 0) {
119 level = 1;
120 } else if(level < 0) {
121 return dev->udfcache_level;
122 }
123
124 dev->udfcache_level = level;
125
126 return level;
127 }
128
129 void *GetUDFCacheHandle(dvd_reader_t *device)
130 {
131 struct dvd_reader_s *dev = (struct dvd_reader_s *)device;
132
133 return dev->udfcache;
134 }
135
136 void SetUDFCacheHandle(dvd_reader_t *device, void *cache)
137 {
138 struct dvd_reader_s *dev = (struct dvd_reader_s *)device;
139
140 dev->udfcache = cache;
141 }
142
143
144
145 /* Loop over all titles and call dvdcss_title to crack the keys. */
146 static int initAllCSSKeys( dvd_reader_t *dvd )
147 {
148 struct timeval all_s, all_e;
149 struct timeval t_s, t_e;
150 char filename[ MAX_UDF_FILE_NAME_LEN ];
151 uint32_t start, len;
152 int title;
153
154 char *nokeys_str = getenv("DVDREAD_NOKEYS");
155 if(nokeys_str != NULL)
156 return 0;
157
158 fprintf( stderr, "\n" );
159 fprintf( stderr, "libdvdread: Attempting to retrieve all CSS keys\n" );
160 fprintf( stderr, "libdvdread: This can take a _long_ time, "
161 "please be patient\n\n" );
162
163 gettimeofday(&all_s, NULL);
164
165 for( title = 0; title < 100; title++ ) {
166 gettimeofday( &t_s, NULL );
167 if( title == 0 ) {
168 sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
169 } else {
170 sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 0 );
171 }
172 start = UDFFindFile( dvd, filename, &len );
173 if( start != 0 && len != 0 ) {
174 /* Perform CSS key cracking for this title. */
175 fprintf( stderr, "libdvdread: Get key for %s at 0x%08x\n",
176 filename, start );
177 if( dvdinput_title( dvd->dev, (int)start ) < 0 ) {
178 fprintf( stderr, "libdvdread: Error cracking CSS key for %s (0x%08x)\n", filename, start);
179 }
180 gettimeofday( &t_e, NULL );
181 fprintf( stderr, "libdvdread: Elapsed time %ld\n",
182 (long int) t_e.tv_sec - t_s.tv_sec );
183 }
184
185 if( title == 0 ) continue;
186
187 gettimeofday( &t_s, NULL );
188 sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 1 );
189 start = UDFFindFile( dvd, filename, &len );
190 if( start == 0 || len == 0 ) break;
191
192 /* Perform CSS key cracking for this title. */
193 fprintf( stderr, "libdvdread: Get key for %s at 0x%08x\n",
194 filename, start );
195 if( dvdinput_title( dvd->dev, (int)start ) < 0 ) {
196 fprintf( stderr, "libdvdread: Error cracking CSS key for %s (0x%08x)!!\n", filename, start);
197 }
198 gettimeofday( &t_e, NULL );
199 fprintf( stderr, "libdvdread: Elapsed time %ld\n",
200 (long int) t_e.tv_sec - t_s.tv_sec );
201 }
202 title--;
203
204 fprintf( stderr, "libdvdread: Found %d VTS's\n", title );
205 gettimeofday(&all_e, NULL);
206 fprintf( stderr, "libdvdread: Elapsed time %ld\n",
207 (long int) all_e.tv_sec - all_s.tv_sec );
208
209 return 0;
210 }
211
212
213
214 /**
215 * Open a DVD image or block device file.
216 */
217 static dvd_reader_t *DVDOpenImageFile( const char *location, int have_css )
218 {
219 dvd_reader_t *dvd;
220 dvd_input_t dev;
221
222 dev = dvdinput_open( location );
223 if( !dev ) {
224 fprintf( stderr, "libdvdread: Can't open %s for reading\n", location );
225 return 0;
226 }
227
228 dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
229 if( !dvd ) return 0;
230 dvd->isImageFile = 1;
231 dvd->dev = dev;
232 dvd->path_root = 0;
233
234 dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
235 dvd->udfcache = NULL;
236
237 if( have_css ) {
238 /* Only if DVDCSS_METHOD = title, a bit if it's disc or if
239 * DVDCSS_METHOD = key but region missmatch. Unfortunaly we
240 * don't have that information. */
241
242 dvd->css_state = 1; /* Need key init. */
243 }
244 dvd->css_title = 0;
245
246 return dvd;
247 }
248
249 static dvd_reader_t *DVDOpenPath( const char *path_root )
250 {
251 dvd_reader_t *dvd;
252
253 dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
254 if( !dvd ) return 0;
255 dvd->isImageFile = 0;
256 dvd->dev = 0;
257 dvd->path_root = strdup( path_root );
258
259 dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
260 dvd->udfcache = NULL;
261
262 dvd->css_state = 0; /* Only used in the UDF path */
263 dvd->css_title = 0; /* Only matters in the UDF path */
264
265 return dvd;
266 }
267
268 #if defined(__sun)
269 /* /dev/rdsk/c0t6d0s0 (link to /devices/...)
270 /vol/dev/rdsk/c0t6d0/??
271 /vol/rdsk/<name> */
272 static char *sun_block2char( const char *path )
273 {
274 char *new_path;
275
276 /* Must contain "/dsk/" */
277 if( !strstr( path, "/dsk/" ) ) return (char *) strdup( path );
278
279 /* Replace "/dsk/" with "/rdsk/" */
280 new_path = malloc( strlen(path) + 2 );
281 strcpy( new_path, path );
282 strcpy( strstr( new_path, "/dsk/" ), "" );
283 strcat( new_path, "/rdsk/" );
284 strcat( new_path, strstr( path, "/dsk/" ) + strlen( "/dsk/" ) );
285
286 return new_path;
287 }
288 #endif
289
290 #if defined(SYS_BSD)
291 /* FreeBSD /dev/(r)(a)cd0c (a is for atapi), recomended to _not_ use r
292 OpenBSD /dev/rcd0c, it needs to be the raw device
293 NetBSD /dev/rcd0[d|c|..] d for x86, c (for non x86), perhaps others
294 Darwin /dev/rdisk0, it needs to be the raw device
295 BSD/OS /dev/sr0c (if not mounted) or /dev/rsr0c ('c' any letter will do) */
296 static char *bsd_block2char( const char *path )
297 #if defined(__FreeBSD__)
298 {
299 return (char *) strdup( path );
300 }
301 #else
302 {
303 char *new_path;
304
305 /* If it doesn't start with "/dev/" or does start with "/dev/r" exit */
306 if( strncmp( path, "/dev/", 5 ) || !strncmp( path, "/dev/r", 6 ) )
307 return (char *) strdup( path );
308
309 /* Replace "/dev/" with "/dev/r" */
310 new_path = malloc( strlen(path) + 2 );
311 strcpy( new_path, "/dev/r" );
312 strcat( new_path, path + strlen( "/dev/" ) );
313
314 return new_path;
315 }
316 #endif /* __FreeBSD__ */
317 #endif
318
319 dvd_reader_t *DVDOpen( const char *path )
320 {
321 struct stat fileinfo;
322 int ret, have_css;
323 char *dev_name = 0;
324
325 if( path == NULL )
326 return 0;
327
328 #ifdef WIN32
329 /* Stat doesn't work on devices under mingwin/cygwin. */
330 if( path[0] && path[1] == ':' && path[2] == '\0' )
331 {
332 /* Don't try to stat the file */
333 fileinfo.st_mode = S_IFBLK;
334 }
335 else
336 #endif
337 {
338 ret = stat( path, &fileinfo );
339 if( ret < 0 ) {
340 /* If we can't stat the file, give up */
341 fprintf( stderr, "libdvdread: Can't stat %s\n", path );
342 perror("");
343 return 0;
344 }
345 }
346
347 /* Try to open libdvdcss or fall back to standard functions */
348 have_css = dvdinput_setup();
349
350 /* First check if this is a block/char device or a file*/
351 if( S_ISBLK( fileinfo.st_mode ) ||
352 S_ISCHR( fileinfo.st_mode ) ||
353 S_ISREG( fileinfo.st_mode ) ) {
354
355 /**
356 * Block devices and regular files are assumed to be DVD-Video images.
357 */
358 #if defined(__sun)
359 return DVDOpenImageFile( sun_block2char( path ), have_css );
360 #elif defined(SYS_BSD)
361 return DVDOpenImageFile( bsd_block2char( path ), have_css );
362 #else
363 return DVDOpenImageFile( path, have_css );
364 #endif
365
366 } else if( S_ISDIR( fileinfo.st_mode ) ) {
367 dvd_reader_t *auth_drive = 0;
368 char *path_copy;
369 #if defined(SYS_BSD)
370 struct fstab* fe;
371 #elif defined(__sun) || defined(__linux__) || defined(__CYGWIN__)
372 FILE *mntfile;
373 #endif
374
375 /* XXX: We should scream real loud here. */
376 if( !(path_copy = strdup( path ) ) ) return 0;
377
378 /* Resolve any symlinks and get the absolut dir name. */
379 {
380 char *new_path;
381 int cdir = open( ".", O_RDONLY );
382
383 if( cdir >= 0 ) {
384 chdir( path_copy );
385 new_path = getcwd( NULL, PATH_MAX );
386 #ifndef __MINGW32__
387 fchdir( cdir );
388 #endif
389 close( cdir );
390 if( new_path ) {
391 free( path_copy );
392 path_copy = new_path;
393 }
394 }
395 }
396
397 /**
398 * If we're being asked to open a directory, check if that directory
399 * is the mountpoint for a DVD-ROM which we can use instead.
400 */
401
402 if( strlen( path_copy ) > 1 ) {
403 if( path_copy[ strlen( path_copy ) - 1 ] == '/' )
404 path_copy[ strlen( path_copy ) - 1 ] = '\0';
405 }
406
407 if( strlen( path_copy ) > 9 ) {
408 if( !strcasecmp( &(path_copy[ strlen( path_copy ) - 9 ]),
409 "/video_ts" ) ) {
410 path_copy[ strlen( path_copy ) - 9 ] = '\0';
411 }
412 }
413
414 #if defined(SYS_BSD)
415 if( ( fe = getfsfile( path_copy ) ) ) {
416 dev_name = bsd_block2char( fe->fs_spec );
417 fprintf( stderr,
418 "libdvdread: Attempting to use device %s"
419 " mounted on %s for CSS authentication\n",
420 dev_name,
421 fe->fs_file );
422 auth_drive = DVDOpenImageFile( dev_name, have_css );
423 }
424 #elif defined(__sun)
425 mntfile = fopen( MNTTAB, "r" );
426 if( mntfile ) {
427 struct mnttab mp;
428 int res;
429
430 while( ( res = getmntent( mntfile, &mp ) ) != -1 ) {
431 if( res == 0 && !strcmp( mp.mnt_mountp, path_copy ) ) {
432 dev_name = sun_block2char( mp.mnt_special );
433 fprintf( stderr,
434 "libdvdread: Attempting to use device %s"
435 " mounted on %s for CSS authentication\n",
436 dev_name,
437 mp.mnt_mountp );
438 auth_drive = DVDOpenImageFile( dev_name, have_css );
439 break;
440 }
441 }
442 fclose( mntfile );
443 }
444 #elif defined(__linux__) || defined(__CYGWIN__)
445 mntfile = fopen( MOUNTED, "r" );
446 if( mntfile ) {
447 struct mntent *me;
448
449 while( ( me = getmntent( mntfile ) ) ) {
450 if( !strcmp( me->mnt_dir, path_copy ) ) {
451 fprintf( stderr,
452 "libdvdread: Attempting to use device %s"
453 " mounted on %s for CSS authentication\n",
454 me->mnt_fsname,
455 me->mnt_dir );
456 auth_drive = DVDOpenImageFile( me->mnt_fsname, have_css );
457 dev_name = strdup(me->mnt_fsname);
458 break;
459 }
460 }
461 fclose( mntfile );
462 }
463 #elif defined(__MINGW32__)
464 dev_name = strdup(path);
465 auth_drive = DVDOpenImageFile( path, have_css );
466 #endif
467 if( !dev_name ) {
468 fprintf( stderr, "libdvdread: Couldn't find device name.\n" );
469 } else if( !auth_drive ) {
470 fprintf( stderr, "libdvdread: Device %s inaccessible, "
471 "CSS authentication not available.\n", dev_name );
472 }
473
474 free( dev_name );
475 free( path_copy );
476
477 /**
478 * If we've opened a drive, just use that.
479 */
480 if( auth_drive ) return auth_drive;
481
482 /**
483 * Otherwise, we now try to open the directory tree instead.
484 */
485 return DVDOpenPath( path );
486 }
487
488 /* If it's none of the above, screw it. */
489 fprintf( stderr, "libdvdread: Could not open %s\n", path );
490 return 0;
491 }
492
493 void DVDClose( dvd_reader_t *dvd )
494 {
495 if( dvd ) {
496 if( dvd->dev ) dvdinput_close( dvd->dev );
497 if( dvd->path_root ) free( dvd->path_root );
498 if( dvd->udfcache ) FreeUDFCache( dvd->udfcache );
499 free( dvd );
500 }
501 }
502
503 /**
504 * Open an unencrypted file on a DVD image file.
505 */
506 static dvd_file_t *DVDOpenFileUDF( dvd_reader_t *dvd, char *filename )
507 {
508 uint32_t start, len;
509 dvd_file_t *dvd_file;
510
511 start = UDFFindFile( dvd, filename, &len );
512 if( !start ) return 0;
513
514 dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
515 if( !dvd_file ) return 0;
516 dvd_file->dvd = dvd;
517 dvd_file->lb_start = start;
518 dvd_file->seek_pos = 0;
519 memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
520 memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
521 dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
522
523 return dvd_file;
524 }
525
526 /**
527 * Searches for <file> in directory <path>, ignoring case.
528 * Returns 0 and full filename in <filename>.
529 * or -1 on file not found.
530 * or -2 on path not found.
531 */
532 static int findDirFile( const char *path, const char *file, char *filename )
533 {
534 DIR *dir;
535 struct dirent *ent;
536
537 dir = opendir( path );
538 if( !dir ) return -2;
539
540 while( ( ent = readdir( dir ) ) != NULL ) {
541 if( !strcasecmp( ent->d_name, file ) ) {
542 sprintf( filename, "%s%s%s", path,
543 ( ( path[ strlen( path ) - 1 ] == '/' ) ? "" : "/" ),
544 ent->d_name );
545 return 0;
546 }
547 }
548
549 return -1;
550 }
551
552 static int findDVDFile( dvd_reader_t *dvd, const char *file, char *filename )
553 {
554 char video_path[ PATH_MAX + 1 ];
555 const char *nodirfile;
556 int ret;
557
558 /* Strip off the directory for our search */
559 if( !strncasecmp( "/VIDEO_TS/", file, 10 ) ) {
560 nodirfile = &(file[ 10 ]);
561 } else {
562 nodirfile = file;
563 }
564
565 ret = findDirFile( dvd->path_root, nodirfile, filename );
566 if( ret < 0 ) {
567 /* Try also with adding the path, just in case. */
568 sprintf( video_path, "%s/VIDEO_TS/", dvd->path_root );
569 ret = findDirFile( video_path, nodirfile, filename );
570 if( ret < 0 ) {
571 /* Try with the path, but in lower case. */
572 sprintf( video_path, "%s/video_ts/", dvd->path_root );
573 ret = findDirFile( video_path, nodirfile, filename );
574 if( ret < 0 ) {
575 return 0;
576 }
577 }
578 }
579
580 return 1;
581 }
582
583 /**
584 * Open an unencrypted file from a DVD directory tree.
585 */
586 static dvd_file_t *DVDOpenFilePath( dvd_reader_t *dvd, char *filename )
587 {
588 char full_path[ PATH_MAX + 1 ];
589 dvd_file_t *dvd_file;
590 struct stat fileinfo;
591 dvd_input_t dev;
592
593 /* Get the full path of the file. */
594 if( !findDVDFile( dvd, filename, full_path ) ) return 0;
595
596 dev = dvdinput_open( full_path );
597 if( !dev ) return 0;
598
599 dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
600 if( !dvd_file ) return 0;
601 dvd_file->dvd = dvd;
602 dvd_file->lb_start = 0;
603 dvd_file->seek_pos = 0;
604 memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
605 memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
606 dvd_file->filesize = 0;
607
608 if( stat( full_path, &fileinfo ) < 0 ) {
609 fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
610 free( dvd_file );
611 return 0;
612 }
613 dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
614 dvd_file->title_devs[ 0 ] = dev;
615 dvd_file->filesize = dvd_file->title_sizes[ 0 ];
616
617 return dvd_file;
618 }
619
620 static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *dvd, int title, int menu )
621 {
622 char filename[ MAX_UDF_FILE_NAME_LEN ];
623 uint32_t start, len;
624 dvd_file_t *dvd_file;
625
626 if( title == 0 ) {
627 sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
628 } else {
629 sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
630 }
631 start = UDFFindFile( dvd, filename, &len );
632 if( start == 0 ) return 0;
633
634 dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
635 if( !dvd_file ) return 0;
636 dvd_file->dvd = dvd;
637 /*Hack*/ dvd_file->css_title = title << 1 | menu;
638 dvd_file->lb_start = start;
639 dvd_file->seek_pos = 0;
640 memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
641 memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
642 dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
643
644 /* Calculate the complete file size for every file in the VOBS */
645 if( !menu ) {
646 int cur;
647
648 for( cur = 2; cur < 10; cur++ ) {
649 sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur );
650 if( !UDFFindFile( dvd, filename, &len ) ) break;
651 dvd_file->filesize += len / DVD_VIDEO_LB_LEN;
652 }
653 }
654
655 if( dvd->css_state == 1 /* Need key init */ ) {
656 // initAllCSSKeys( dvd );
657 // dvd->css_state = 2;
658 }
659 /*
660 if( dvdinput_title( dvd_file->dvd->dev, (int)start ) < 0 ) {
661 fprintf( stderr, "libdvdread: Error cracking CSS key for %s\n",
662 filename );
663 }
664 */
665
666 return dvd_file;
667 }
668
669 static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *dvd, int title, int menu )
670 {
671 char filename[ MAX_UDF_FILE_NAME_LEN ];
672 char full_path[ PATH_MAX + 1 ];
673 struct stat fileinfo;
674 dvd_file_t *dvd_file;
675 int i;
676
677 dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
678 if( !dvd_file ) return 0;
679 dvd_file->dvd = dvd;
680 /*Hack*/ dvd_file->css_title = title << 1 | menu;
681 dvd_file->lb_start = 0;
682 dvd_file->seek_pos = 0;
683 memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
684 memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
685 dvd_file->filesize = 0;
686
687 if( menu ) {
688 dvd_input_t dev;
689
690 if( title == 0 ) {
691 sprintf( filename, "VIDEO_TS.VOB" );
692 } else {
693 sprintf( filename, "VTS_%02i_0.VOB", title );
694 }
695 if( !findDVDFile( dvd, filename, full_path ) ) {
696 free( dvd_file );
697 return 0;
698 }
699
700 dev = dvdinput_open( full_path );
701 if( dev == NULL ) {
702 free( dvd_file );
703 return 0;
704 }
705
706 if( stat( full_path, &fileinfo ) < 0 ) {
707 fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
708 free( dvd_file );
709 return 0;
710 }
711 dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
712 dvd_file->title_devs[ 0 ] = dev;
713 dvdinput_title( dvd_file->title_devs[0], 0);
714 dvd_file->filesize = dvd_file->title_sizes[ 0 ];
715
716 } else {
717 for( i = 0; i < 9; ++i ) {
718
719 sprintf( filename, "VTS_%02i_%i.VOB", title, i + 1 );
720 if( !findDVDFile( dvd, filename, full_path ) ) {
721 break;
722 }
723
724 if( stat( full_path, &fileinfo ) < 0 ) {
725 fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
726 break;
727 }
728
729 dvd_file->title_sizes[ i ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
730 dvd_file->title_devs[ i ] = dvdinput_open( full_path );
731 dvdinput_title( dvd_file->title_devs[ i ], 0 );
732 dvd_file->filesize += dvd_file->title_sizes[ i ];
733 }
734 if( !dvd_file->title_devs[ 0 ] ) {
735 free( dvd_file );
736 return 0;
737 }
738 }
739
740 return dvd_file;
741 }
742
743 dvd_file_t *DVDOpenFile( dvd_reader_t *dvd, int titlenum,
744 dvd_read_domain_t domain )
745 {
746 char filename[ MAX_UDF_FILE_NAME_LEN ];
747
748 /* Check arguments. */
749 if( dvd == NULL || titlenum < 0 )
750 return NULL;
751
752 switch( domain ) {
753 case DVD_READ_INFO_FILE:
754 if( titlenum == 0 ) {
755 sprintf( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
756 } else {
757 sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );
758 }
759 break;
760 case DVD_READ_INFO_BACKUP_FILE:
761 if( titlenum == 0 ) {
762 sprintf( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
763 } else {
764 sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );
765 }
766 break;
767 case DVD_READ_MENU_VOBS:
768 if( dvd->isImageFile ) {
769 return DVDOpenVOBUDF( dvd, titlenum, 1 );
770 } else {
771 return DVDOpenVOBPath( dvd, titlenum, 1 );
772 }
773 break;
774 case DVD_READ_TITLE_VOBS:
775 if( titlenum == 0 ) return 0;
776 if( dvd->isImageFile ) {
777 return DVDOpenVOBUDF( dvd, titlenum, 0 );
778 } else {
779 return DVDOpenVOBPath( dvd, titlenum, 0 );
780 }
781 break;
782 default:
783 fprintf( stderr, "libdvdread: Invalid domain for file open.\n" );
784 return NULL;
785 }
786
787 if( dvd->isImageFile ) {
788 return DVDOpenFileUDF( dvd, filename );
789 } else {
790 return DVDOpenFilePath( dvd, filename );
791 }
792 }
793
794 void DVDCloseFile( dvd_file_t *dvd_file )
795 {
796 int i;
797
798 if( dvd_file ) {
799 if( dvd_file->dvd->isImageFile ) {
800 ;
801 } else {
802 for( i = 0; i < 9; ++i ) {
803 if( dvd_file->title_devs[ i ] ) {
804 dvdinput_close( dvd_file->title_devs[i] );
805 }
806 }
807 }
808
809 free( dvd_file );
810 dvd_file = 0;
811 }
812 }
813
814 /* Internal, but used from dvd_udf.c */
815 int UDFReadBlocksRaw( dvd_reader_t *device, uint32_t lb_number,
816 size_t block_count, unsigned char *data,
817 int encrypted )
818 {
819 int ret;
820
821 if( !device->dev ) {
822 fprintf( stderr, "libdvdread: Fatal error in block read.\n" );
823 return 0;
824 }
825
826 ret = dvdinput_seek( device->dev, (int) lb_number );
827 if( ret != (int) lb_number ) {
828 fprintf( stderr, "libdvdread: Can't seek to block %u\n", lb_number );
829 return 0;
830 }
831
832 return dvdinput_read( device->dev, (char *) data,
833 (int) block_count, encrypted );
834 }
835
836 /* This is using a single input and starting from 'dvd_file->lb_start' offset.
837 *
838 * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
839 * into the buffer located at 'data' and if 'encrypted' is set
840 * descramble the data if it's encrypted. Returning either an
841 * negative error or the number of blocks read. */
842 static int DVDReadBlocksUDF( dvd_file_t *dvd_file, uint32_t offset,
843 size_t block_count, unsigned char *data,
844 int encrypted )
845 {
846 return UDFReadBlocksRaw( dvd_file->dvd, dvd_file->lb_start + offset,
847 block_count, data, encrypted );
848 }
849
850 /* This is using possibly several inputs and starting from an offset of '0'.
851 *
852 * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
853 * into the buffer located at 'data' and if 'encrypted' is set
854 * descramble the data if it's encrypted. Returning either an
855 * negative error or the number of blocks read. */
856 static int DVDReadBlocksPath( dvd_file_t *dvd_file, unsigned int offset,
857 size_t block_count, unsigned char *data,
858 int encrypted )
859 {
860 int i;
861 int ret, ret2, off;
862
863 ret = 0;
864 ret2 = 0;
865 for( i = 0; i < 9; ++i ) {
866 if( !dvd_file->title_sizes[ i ] ) return 0; /* Past end of file */
867
868 if( offset < dvd_file->title_sizes[ i ] ) {
869 if( ( offset + block_count ) <= dvd_file->title_sizes[ i ] ) {
870 off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );
871 if( off < 0 || off != (int)offset ) {
872 fprintf( stderr, "libdvdread: Can't seek to block %d\n",
873 offset );
874 return off < 0 ? off : 0;
875 }
876 ret = dvdinput_read( dvd_file->title_devs[ i ], data,
877 (int)block_count, encrypted );
878 break;
879 } else {
880 size_t part1_size = dvd_file->title_sizes[ i ] - offset;
881 /* FIXME: Really needs to be a while loop.
882 * (This is only true if you try and read >1GB at a time) */
883
884 /* Read part 1 */
885 off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );
886 if( off < 0 || off != (int)offset ) {
887 fprintf( stderr, "libdvdread: Can't seek to block %d\n",
888 offset );
889 return off < 0 ? off : 0;
890 }
891 ret = dvdinput_read( dvd_file->title_devs[ i ], data,
892 (int)part1_size, encrypted );
893 if( ret < 0 ) return ret;
894 /* FIXME: This is wrong if i is the last file in the set.
895 * also error from this read will not show in ret. */
896
897 /* Does the next part exist? If not then return now. */
898 if( !dvd_file->title_devs[ i + 1 ] ) return ret;
899
900 /* Read part 2 */
901 off = dvdinput_seek( dvd_file->title_devs[ i + 1 ], 0 );
902 if( off < 0 || off != 0 ) {
903 fprintf( stderr, "libdvdread: Can't seek to block %d\n",
904 0 );
905 return off < 0 ? off : 0;
906 }
907 ret2 = dvdinput_read( dvd_file->title_devs[ i + 1 ],
908 data + ( part1_size
909 * (int64_t)DVD_VIDEO_LB_LEN ),
910 (int)(block_count - part1_size),
911 encrypted );
912 if( ret2 < 0 ) return ret2;
913 break;
914 }
915 } else {
916 offset -= dvd_file->title_sizes[ i ];
917 }
918 }
919
920 return ret + ret2;
921 }
922
923 /* This is broken reading more than 2Gb at a time is ssize_t is 32-bit. */
924 ssize_t DVDReadBlocks( dvd_file_t *dvd_file, int offset,
925 size_t block_count, unsigned char *data )
926 {
927 int ret;
928
929 /* Check arguments. */
930 if( dvd_file == NULL || offset < 0 || data == NULL )
931 return -1;
932
933 /* Hack, and it will still fail for multiple opens in a threaded app ! */
934 if( dvd_file->dvd->css_title != dvd_file->css_title ) {
935 dvd_file->dvd->css_title = dvd_file->css_title;
936 if( dvd_file->dvd->isImageFile ) {
937 dvdinput_title( dvd_file->dvd->dev, (int)dvd_file->lb_start );
938 }
939 /* Here each vobu has it's own dvdcss handle, so no need to update
940 else {
941 dvdinput_title( dvd_file->title_devs[ 0 ], (int)dvd_file->lb_start );
942 }*/
943 }
944
945 if( dvd_file->dvd->isImageFile ) {
946 ret = DVDReadBlocksUDF( dvd_file, (uint32_t)offset,
947 block_count, data, DVDINPUT_READ_DECRYPT );
948 } else {
949 ret = DVDReadBlocksPath( dvd_file, (unsigned int)offset,
950 block_count, data, DVDINPUT_READ_DECRYPT );
951 }
952
953 return (ssize_t)ret;
954 }
955
956 int DVDFileSeek( dvd_file_t *dvd_file, int offset )
957 {
958 /* Check arguments. */
959 if( dvd_file == NULL || offset < 0 )
960 return -1;
961
962 if( offset > dvd_file->filesize * DVD_VIDEO_LB_LEN ) {
963 return -1;
964 }
965 dvd_file->seek_pos = (uint32_t) offset;
966 return offset;
967 }
968
969 ssize_t DVDReadBytes( dvd_file_t *dvd_file, void *data, size_t byte_size )
970 {
971 unsigned char *secbuf;
972 unsigned int numsec, seek_sector, seek_byte;
973 int ret;
974
975 /* Check arguments. */
976 if( dvd_file == NULL || data == NULL )
977 return -1;
978
979 seek_sector = dvd_file->seek_pos / DVD_VIDEO_LB_LEN;
980 seek_byte = dvd_file->seek_pos % DVD_VIDEO_LB_LEN;
981
982 numsec = ( ( seek_byte + byte_size ) / DVD_VIDEO_LB_LEN ) +
983 ( ( ( seek_byte + byte_size ) % DVD_VIDEO_LB_LEN ) ? 1 : 0 );
984
985 secbuf = (unsigned char *) malloc( numsec * DVD_VIDEO_LB_LEN );
986 if( !secbuf ) {
987 fprintf( stderr, "libdvdread: Can't allocate memory "
988 "for file read!\n" );
989 return 0;
990 }
991
992 if( dvd_file->dvd->isImageFile ) {
993 ret = DVDReadBlocksUDF( dvd_file, (uint32_t) seek_sector,
994 (size_t) numsec, secbuf, DVDINPUT_NOFLAGS );
995 } else {
996 ret = DVDReadBlocksPath( dvd_file, seek_sector,
997 (size_t) numsec, secbuf, DVDINPUT_NOFLAGS );
998 }
999
1000 if( ret != (int) numsec ) {
1001 free( secbuf );
1002 return ret < 0 ? ret : 0;
1003 }
1004
1005 memcpy( data, &(secbuf[ seek_byte ]), byte_size );
1006 free( secbuf );
1007
1008 dvd_file->seek_pos += byte_size;
1009 return byte_size;
1010 }
1011
1012 ssize_t DVDFileSize( dvd_file_t *dvd_file )
1013 {
1014 /* Check arguments. */
1015 if( dvd_file == NULL )
1016 return -1;
1017
1018 return dvd_file->filesize;
1019 }
1020
1021 int DVDDiscID( dvd_reader_t *dvd, unsigned char *discid )
1022 {
1023 struct md5_ctx ctx;
1024 int title;
1025
1026 /* Check arguments. */
1027 if( dvd == NULL || discid == NULL )
1028 return 0;
1029
1030 /* Go through the first 10 IFO:s, in order,
1031 * and md5sum them, i.e VIDEO_TS.IFO and VTS_0?_0.IFO */
1032 md5_init_ctx( &ctx );
1033 for( title = 0; title < 10; title++ ) {
1034 dvd_file_t *dvd_file = DVDOpenFile( dvd, title, DVD_READ_INFO_FILE );
1035 if( dvd_file != NULL ) {
1036 ssize_t bytes_read;
1037 size_t file_size = dvd_file->filesize * DVD_VIDEO_LB_LEN;
1038 char *buffer = malloc( file_size );
1039
1040 if( buffer == NULL ) {
1041 fprintf( stderr, "libdvdread: DVDDiscId, failed to "
1042 "allocate memory for file read!\n" );
1043 return -1;
1044 }
1045 bytes_read = DVDReadBytes( dvd_file, buffer, file_size );
1046 if( bytes_read != file_size ) {
1047 fprintf( stderr, "libdvdread: DVDDiscId read returned %d bytes"
1048 ", wanted %d\n", bytes_read, file_size );
1049 DVDCloseFile( dvd_file );
1050 return -1;
1051 }
1052
1053 md5_process_bytes( buffer, file_size, &ctx );
1054
1055 DVDCloseFile( dvd_file );
1056 free( buffer );
1057 }
1058 }
1059 md5_finish_ctx( &ctx, discid );
1060
1061 return 0;
1062 }
1063
1064
1065 int DVDISOVolumeInfo( dvd_reader_t *dvd,
1066 char *volid, unsigned int volid_size,
1067 unsigned char *volsetid, unsigned int volsetid_size )
1068 {
1069 unsigned char *buffer;
1070 int ret;
1071
1072 /* Check arguments. */
1073 if( dvd == NULL )
1074 return 0;
1075
1076 if( dvd->dev == NULL ) {
1077 /* No block access, so no ISO... */
1078 return -1;
1079 }
1080
1081 buffer = malloc( DVD_VIDEO_LB_LEN );
1082 if( buffer == NULL ) {
1083 fprintf( stderr, "libdvdread: DVDISOVolumeInfo, failed to "
1084 "allocate memory for file read!\n" );
1085 return -1;
1086 }
1087
1088 ret = UDFReadBlocksRaw( dvd, 16, 1, buffer, 0 );
1089 if( ret != 1 ) {
1090 fprintf( stderr, "libdvdread: DVDISOVolumeInfo, failed to "
1091 "read ISO9660 Primary Volume Descriptor!\n" );
1092 return -1;
1093 }
1094
1095 if( (volid != NULL) && (volid_size > 0) ) {
1096 unsigned int n;
1097 for(n = 0; n < 32; n++) {
1098 if(buffer[40+n] == 0x20) {
1099 break;
1100 }
1101 }
1102
1103 if(volid_size > n+1) {
1104 volid_size = n+1;
1105 }
1106
1107 memcpy(volid, &buffer[40], volid_size-1);
1108 volid[volid_size-1] = '\0';
1109 }
1110
1111 if( (volsetid != NULL) && (volsetid_size > 0) ) {
1112 if(volsetid_size > 128) {
1113 volsetid_size = 128;
1114 }
1115 memcpy(volsetid, &buffer[190], volsetid_size);
1116 }
1117 return 0;
1118 }
1119
1120
1121 int DVDUDFVolumeInfo( dvd_reader_t *dvd,
1122 char *volid, unsigned int volid_size,
1123 unsigned char *volsetid, unsigned int volsetid_size )
1124 {
1125 int ret;
1126 /* Check arguments. */
1127 if( dvd == NULL )
1128 return -1;
1129
1130 if( dvd->dev == NULL ) {
1131 /* No block access, so no UDF VolumeSet Identifier */
1132 return -1;
1133 }
1134
1135 if( (volid != NULL) && (volid_size > 0) ) {
1136 ret = UDFGetVolumeIdentifier(dvd, volid, volid_size);
1137 if(!ret) {
1138 return -1;
1139 }
1140 }
1141 if( (volsetid != NULL) && (volsetid_size > 0) ) {
1142 ret = UDFGetVolumeSetIdentifier(dvd, volsetid, volsetid_size);
1143 if(!ret) {
1144 return -1;
1145 }
1146 }
1147
1148 return 0;
1149 }