1 /*
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>
5  *
6  * This file is part of libdvdread.
7  *
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.
12  *
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.
17  *
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.
21  */
22 
23 #include "config.h"
24 #include <sys/types.h>      /* off_t */
25 #include <sys/stat.h>       /* stat */
26 #include <sys/time.h>       /* For the timing of dvdcss_title crack. */
27 #include <fcntl.h>          /* open */
28 #include <stdlib.h>         /* free */
29 #include <stdio.h>          /* fprintf */
30 #include <errno.h>          /* errno, EIN* */
31 #include <string.h>         /* memcpy, strlen */
32 #include <unistd.h>         /* chdir, getcwd */
33 #include <limits.h>         /* PATH_MAX */
34 #include <dirent.h>         /* opendir, readdir */
35 #include <ctype.h>          /* isalpha */
36 
37 /* misc win32 helpers */
38 #ifdef _WIN32
39 # ifndef HAVE_GETTIMEOFDAY
40    /* replacement gettimeofday implementation */
41 #  include <sys/timeb.h>
_private_gettimeofday(struct timeval * tv,void * tz)42 static inline int _private_gettimeofday( struct timeval *tv, void *tz )
43 {
44   struct timeb t;
45   ftime( &t );
46   tv->tv_sec = t.time;
47   tv->tv_usec = t.millitm * 1000;
48   return 0;
49 }
50 #  define gettimeofday(TV, TZ) _private_gettimeofday((TV), (TZ))
51 # endif
52 # include <io.h> /* read() */
53 # define lseek64 _lseeki64
54 #endif
55 
56 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__) || defined(__APPLE__)
57 # define SYS_BSD 1
58 #endif
59 
60 #if defined(__sun)
61 # include <sys/mnttab.h>
62 #elif defined(__APPLE__)
63 # include <sys/param.h>
64 # include <sys/ucred.h>
65 # include <sys/mount.h>
66 #elif defined(SYS_BSD)
67 # include <fstab.h>
68 #elif defined(__linux__)
69 # include <mntent.h>
70 # include <paths.h>
71 #endif
72 
73 #include "dvdread/dvd_udf.h"
74 #include "dvdread/dvd_reader.h"
75 #include "dvd_input.h"
76 #include "dvdread_internal.h"
77 #include "md5.h"
78 #include "dvdread/ifo_read.h"
79 
80 #define DEFAULT_UDF_CACHE_LEVEL 1
81 
82 struct dvd_reader_device_s {
83   /* Basic information. */
84   int isImageFile;
85 
86   /* Hack for keeping track of the css status.
87    * 0: no css, 1: perhaps (need init of keys), 2: have done init */
88   int css_state;
89   int css_title; /* Last title that we have called dvdinpute_title for. */
90 
91   /* Information required for an image file. */
92   dvd_input_t dev;
93 
94   /* Information required for a directory path drive. */
95   char *path_root;
96 
97   /* Filesystem cache */
98   int udfcache_level; /* 0 - turned off, 1 - on */
99   void *udfcache;
100 };
101 
102 #define TITLES_MAX 9
103 
104 struct dvd_file_s {
105   /* Basic information. */
106   dvd_reader_t *ctx;
107 
108   /* Hack for selecting the right css title. */
109   int css_title;
110 
111   /* Information required for an image file. */
112   uint32_t lb_start;
113   uint32_t seek_pos;
114 
115   /* Information required for a directory path drive. */
116   size_t title_sizes[ TITLES_MAX ];
117   dvd_input_t title_devs[ TITLES_MAX ];
118 
119   /* Calculated at open-time, size in blocks. */
120   ssize_t filesize;
121 
122   /* Cache of the dvd_file. If not NULL, the cache corresponds to the whole
123    * dvd_file. Used only for IFO and BUP. */
124   unsigned char *cache;
125 };
126 
127 /**
128  * Set the level of caching on udf
129  * level = 0 (no caching)
130  * level = 1 (caching filesystem info)
131  */
DVDUDFCacheLevel(dvd_reader_t * reader,int level)132 int DVDUDFCacheLevel(dvd_reader_t *reader, int level)
133 {
134   dvd_reader_device_t *dev = reader->rd;
135 
136   if(level > 0) {
137     level = 1;
138   } else if(level < 0) {
139     return dev->udfcache_level;
140   }
141 
142   dev->udfcache_level = level;
143 
144   return level;
145 }
146 
GetUDFCacheHandle(dvd_reader_t * reader)147 void *GetUDFCacheHandle(dvd_reader_t *reader)
148 {
149   dvd_reader_device_t *dev = reader->rd;
150 
151   return dev->udfcache;
152 }
153 
SetUDFCacheHandle(dvd_reader_t * reader,void * cache)154 void SetUDFCacheHandle(dvd_reader_t *reader, void *cache)
155 {
156   dvd_reader_device_t *dev = reader->rd;
157 
158   dev->udfcache = cache;
159 }
160 
161 
162 
163 /* Loop over all titles and call dvdcss_title to crack the keys. */
initAllCSSKeys(dvd_reader_t * ctx)164 static int initAllCSSKeys( dvd_reader_t *ctx )
165 {
166   dvd_reader_device_t *dvd = ctx->rd;
167   struct timeval all_s, all_e;
168   struct timeval t_s, t_e;
169   char filename[ MAX_UDF_FILE_NAME_LEN ];
170   uint32_t start, len;
171   int title;
172 
173   const char *nokeys_str = getenv("DVDREAD_NOKEYS");
174   if(nokeys_str != NULL)
175     return 0;
176 
177   Log2(ctx,"Attempting to retrieve all CSS keys" );
178   Log2(ctx,"This can take a _long_ time, please be patient" );
179   gettimeofday(&all_s, NULL);
180 
181   for( title = 0; title < 100; title++ ) {
182     gettimeofday( &t_s, NULL );
183     if( title == 0 ) {
184       sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
185     } else {
186       sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 0 );
187     }
188     start = UDFFindFile( ctx, filename, &len );
189     if( start != 0 && len != 0 ) {
190       /* Perform CSS key cracking for this title. */
191       Log3(ctx,"Get key for %s at 0x%08x",filename, start );
192       if( dvdinput_title( dvd->dev, (int)start ) < 0 ) {
193         Log1(ctx,"Error cracking CSS key for %s (0x%08x)", filename, start);
194       }
195       gettimeofday( &t_e, NULL );
196       Log3(ctx,"Elapsed time %ld", (long int) t_e.tv_sec - t_s.tv_sec );
197     }
198 
199     if( title == 0 ) continue;
200 
201     gettimeofday( &t_s, NULL );
202     sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 1 );
203     start = UDFFindFile( ctx, filename, &len );
204     if( start == 0 || len == 0 ) break;
205 
206     /* Perform CSS key cracking for this title. */
207     Log3(ctx,"Get key for %s at 0x%08x",filename, start );
208     if( dvdinput_title( dvd->dev, (int)start ) < 0 ) {
209       Log1(ctx,"Error cracking CSS key for %s (0x%08x)", filename, start);
210     }
211     gettimeofday( &t_e, NULL );
212     Log3(ctx,"Elapsed time %ld", (long int) t_e.tv_sec - t_s.tv_sec );
213   }
214   title--;
215 
216   Log3(ctx,"Found %d VTS's", title );
217   gettimeofday(&all_e, NULL);
218   Log3(ctx,"Elapsed time %ld", (long int) all_e.tv_sec - all_s.tv_sec );
219 
220   return 0;
221 }
222 
223 
224 
225 /**
226  * Open a DVD image or block device file or use stream_cb functions.
227  */
DVDOpenImageFile(dvd_reader_t * ctx,const char * location,dvd_reader_stream_cb * stream_cb,int have_css)228 static dvd_reader_device_t *DVDOpenImageFile( dvd_reader_t *ctx,
229                                               const char *location,
230                                         dvd_reader_stream_cb *stream_cb,
231                                        int have_css )
232 {
233   dvd_reader_device_t *dvd;
234   dvd_input_t dev;
235 
236   dev = dvdinput_open( ctx->priv, &ctx->logcb, location, stream_cb );
237   if( !dev ) {
238     Log0(ctx,"Can't open %s for reading", location );
239     return NULL;
240   }
241 
242   dvd = calloc( 1, sizeof( dvd_reader_device_t ) );
243   if( !dvd ) {
244     dvdinput_close(dev);
245     return NULL;
246   }
247   dvd->isImageFile = 1;
248   dvd->dev = dev;
249 
250   dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
251 
252   if( have_css ) {
253     /* Only if DVDCSS_METHOD = title, a bit if it's disc or if
254      * DVDCSS_METHOD = key but region mismatch. Unfortunately we
255      * don't have that information. */
256 
257     dvd->css_state = 1; /* Need key init. */
258   }
259 
260   return dvd;
261 }
262 
DVDOpenPath(const char * path_root)263 static dvd_reader_device_t *DVDOpenPath( const char *path_root )
264 {
265   dvd_reader_device_t *dvd;
266 
267   dvd = calloc( 1, sizeof( dvd_reader_device_t ) );
268   if( !dvd ) return NULL;
269   dvd->path_root = strdup( path_root );
270   if(!dvd->path_root) {
271     free(dvd);
272     return NULL;
273   }
274   dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
275 
276   return dvd;
277 }
278 
279 #if defined(__sun)
280 /* /dev/rdsk/c0t6d0s0 (link to /devices/...)
281    /vol/dev/rdsk/c0t6d0/??
282    /vol/rdsk/<name> */
sun_block2char(const char * path)283 static char *sun_block2char( const char *path )
284 {
285   char *new_path;
286 
287   /* Must contain "/dsk/" */
288   if( !strstr( path, "/dsk/" ) ) return (char *) strdup( path );
289 
290   /* Replace "/dsk/" with "/rdsk/" */
291   new_path = malloc( strlen(path) + 2 );
292   if(!new_path) return NULL;
293   strcpy( new_path, path );
294   strcpy( strstr( new_path, "/dsk/" ), "" );
295   strcat( new_path, "/rdsk/" );
296   strcat( new_path, strstr( path, "/dsk/" ) + strlen( "/dsk/" ) );
297 
298   return new_path;
299 }
300 #endif
301 
302 #if defined(SYS_BSD)
303 /* FreeBSD /dev/(r)(a)cd0c (a is for atapi), recommended to _not_ use r
304    update: FreeBSD and DragonFly no longer uses the prefix so don't add it.
305    OpenBSD /dev/rcd0c, it needs to be the raw device
306    NetBSD  /dev/rcd0[d|c|..] d for x86, c (for non x86), perhaps others
307    Darwin  /dev/rdisk0,  it needs to be the raw device
308    BSD/OS  /dev/sr0c (if not mounted) or /dev/rsr0c ('c' any letter will do)
309    returns a string allocated with strdup. It should be freed when no longer
310    used. */
bsd_block2char(const char * path)311 static char *bsd_block2char( const char *path )
312 {
313 #if defined(__FreeBSD__) || defined(__DragonFly__)
314   return (char *) strdup( path );
315 #else
316   char *new_path;
317 
318   /* If it doesn't start with "/dev/" or does start with "/dev/r" exit */
319   if( strncmp( path, "/dev/",  5 ) || !strncmp( path, "/dev/r", 6 ) )
320     return (char *) strdup( path );
321 
322   /* Replace "/dev/" with "/dev/r" */
323   new_path = malloc( strlen(path) + 2 );
324   if(!new_path) return NULL;
325   strcpy( new_path, "/dev/r" );
326   strcat( new_path, path + strlen( "/dev/" ) );
327 
328   return new_path;
329 #endif /* __FreeBSD__ || __DragonFly__ */
330 }
331 #endif
332 
DVDOpenCommon(void * priv,const dvd_logger_cb * logcb,const char * ppath,dvd_reader_stream_cb * stream_cb)333 static dvd_reader_t *DVDOpenCommon( void *priv,
334                                     const dvd_logger_cb *logcb,
335                                     const char *ppath,
336                                     dvd_reader_stream_cb *stream_cb )
337 {
338   struct stat fileinfo;
339   int ret, have_css, retval, cdir = -1;
340   char *dev_name = NULL;
341   char *path = NULL, *new_path = NULL, *path_copy = NULL;
342   dvd_reader_t *ctx = calloc(1, sizeof(*ctx));
343   if(!ctx)
344       return NULL;
345 
346   ctx->priv = priv;
347   if(logcb)
348     ctx->logcb = *logcb;
349 
350 #if defined(_WIN32) || defined(__OS2__)
351       int len;
352 #endif
353 
354   /* Try to open DVD using stream_cb functions */
355   if( priv != NULL && stream_cb != NULL )
356   {
357     have_css = dvdinput_setup( ctx->priv, &ctx->logcb );
358     ctx->rd = DVDOpenImageFile( ctx, NULL, stream_cb, have_css );
359     if(!ctx->rd)
360     {
361         free(ctx);
362         return NULL;
363     }
364     return ctx;
365   }
366 
367   if( ppath == NULL )
368     goto DVDOpen_error;
369 
370   path = strdup(ppath);
371   if( path == NULL )
372     goto DVDOpen_error;
373 
374   /* Try to open libdvdcss or fall back to standard functions */
375   have_css = dvdinput_setup( ctx->priv, &ctx->logcb );
376 
377 #if defined(_WIN32) || defined(__OS2__)
378   /* Strip off the trailing \ if it is not a drive */
379   len = strlen(path);
380   if ((len > 1) &&
381       (path[len - 1] == '\\')  &&
382       (path[len - 2] != ':'))
383   {
384     path[len-1] = '\0';
385   }
386 #endif
387 
388   ret = stat( path, &fileinfo );
389 
390   if( ret < 0 ) {
391 
392     /* maybe "host:port" url? try opening it with acCeSS library */
393     if( strchr(path,':') ) {
394       ctx->rd = DVDOpenImageFile( ctx, path, NULL, have_css );
395       free(path);
396       if(!ctx->rd)
397       {
398           free(ctx);
399           return NULL;
400       }
401       return ctx;
402     }
403 
404     /* If we can't stat the file, give up */
405     Log0(ctx, "Can't stat %s", path );
406     perror("");
407     goto DVDOpen_error;
408   }
409 
410   /* First check if this is a block/char device or a file*/
411   if( S_ISBLK( fileinfo.st_mode ) ||
412       S_ISCHR( fileinfo.st_mode ) ||
413       S_ISREG( fileinfo.st_mode ) ) {
414 
415     /**
416      * Block devices and regular files are assumed to be DVD-Video images.
417      */
418 #if defined(__sun)
419     dev_name = sun_block2char( path );
420 #elif defined(SYS_BSD)
421     dev_name = bsd_block2char( path );
422 #else
423     dev_name = strdup( path );
424 #endif
425     if(!dev_name)
426         goto DVDOpen_error;
427     ctx->rd = DVDOpenImageFile( ctx, dev_name, NULL, have_css );
428     free( dev_name );
429     free(path);
430     if(!ctx->rd)
431     {
432         free(ctx);
433         return NULL;
434     }
435     return ctx;
436   } else if( S_ISDIR( fileinfo.st_mode ) ) {
437 #if defined(SYS_BSD)
438     struct fstab* fe;
439 #elif defined(__sun) || defined(__linux__)
440     FILE *mntfile;
441 #endif
442 
443     /* XXX: We should scream real loud here. */
444     if( !(path_copy = strdup( path ) ) )
445       goto DVDOpen_error;
446 
447 #ifndef _WIN32 /* don't have fchdir, and getcwd( NULL, ... ) is strange */
448               /* Also WIN32 does not have symlinks, so we don't need this bit of code. */
449 
450     /* Resolve any symlinks and get the absolute dir name. */
451     {
452       if( ( cdir  = open( ".", O_RDONLY ) ) >= 0 ) {
453         if( chdir( path_copy ) == -1 ) {
454           goto DVDOpen_error;
455         }
456         new_path = malloc(PATH_MAX+1);
457         if(!new_path) {
458           goto DVDOpen_error;
459         }
460         if( getcwd( new_path, PATH_MAX ) == NULL ) {
461           goto DVDOpen_error;
462         }
463         retval = fchdir( cdir );
464         close( cdir );
465         cdir = -1;
466         if( retval == -1 ) {
467           goto DVDOpen_error;
468         }
469         free(path_copy);
470         path_copy = new_path;
471         new_path = NULL;
472       }
473     }
474 #endif
475 
476     /**
477      * If we're being asked to open a directory, check if that directory
478      * is the mount point for a DVD-ROM which we can use instead.
479      */
480 
481     if( strlen( path_copy ) > 1 ) {
482       if( path_copy[ strlen( path_copy ) - 1 ] == '/' ) {
483         path_copy[ strlen( path_copy ) - 1 ] = '\0';
484       }
485     }
486 
487 #if defined(_WIN32) || defined(__OS2__)
488     if( strlen( path_copy ) > 9 ) {
489       if( !strcasecmp( &(path_copy[ strlen( path_copy ) - 9 ]),
490                        "\\video_ts"))
491         path_copy[ strlen( path_copy ) - (9-1) ] = '\0';
492     }
493 #endif
494     if( strlen( path_copy ) > 9 ) {
495       if( !strcasecmp( &(path_copy[ strlen( path_copy ) - 9 ]),
496                        "/video_ts" ) ) {
497         path_copy[ strlen( path_copy ) - 9 ] = '\0';
498       }
499     }
500 
501     if(path_copy[0] == '\0') {
502       free( path_copy );
503       if( !(path_copy = strdup( "/" ) ) )
504         goto DVDOpen_error;
505     }
506 
507 #if defined(__APPLE__)
508     struct statfs s[128];
509     int r = getfsstat(NULL, 0, MNT_NOWAIT);
510     if (r > 0) {
511         if (r > 128)
512             r = 128;
513         r = getfsstat(s, r * sizeof(s[0]), MNT_NOWAIT);
514         int i;
515         for (i=0; i<r; i++) {
516             if (!strcmp(path_copy, s[i].f_mntonname)) {
517                 dev_name = bsd_block2char(s[i].f_mntfromname);
518                 Log3(ctx, "Attempting to use device %s"
519                           " mounted on %s for CSS authentication",
520                         dev_name,
521                         s[i].f_mntonname);
522                 ctx->rd = DVDOpenImageFile( ctx, dev_name, NULL, have_css );
523                 break;
524             }
525         }
526     }
527 #elif defined(SYS_BSD)
528     if( ( fe = getfsfile( path_copy ) ) ) {
529       dev_name = bsd_block2char( fe->fs_spec );
530       Log3(ctx, "Attempting to use device %s"
531                " mounted on %s for CSS authentication",
532                dev_name,
533                fe->fs_file );
534       ctx->rd = DVDOpenImageFile( ctx, dev_name, NULL, have_css );
535     }
536 #elif defined(__sun)
537     mntfile = fopen( MNTTAB, "r" );
538     if( mntfile ) {
539       struct mnttab mp;
540       int res;
541 
542       while( ( res = getmntent( mntfile, &mp ) ) != -1 ) {
543         if( res == 0 && !strcmp( mp.mnt_mountp, path_copy ) ) {
544           dev_name = sun_block2char( mp.mnt_special );
545           Log3(ctx, "Attempting to use device %s"
546                    " mounted on %s for CSS authentication",
547                    dev_name,
548                    mp.mnt_mountp );
549           ctx->rd = DVDOpenImageFile( ctx, dev_name, NULL, have_css );
550           break;
551         }
552       }
553       fclose( mntfile );
554     }
555 #elif defined(__linux__)
556     mntfile = fopen( _PATH_MOUNTED, "r" );
557     if( mntfile ) {
558 
559 #ifdef HAVE_GETMNTENT_R
560       struct mntent *me, mbuf;
561       char buf [8192];
562       while( ( me = getmntent_r( mntfile, &mbuf, buf, sizeof(buf) ) ) ) {
563 #else
564       struct mntent *me;
565       while( ( me = getmntent( mntfile ) ) ) {
566 #endif
567         if( !strcmp( me->mnt_dir, path_copy ) ) {
568           Log3(ctx, "Attempting to use device %s"
569                    " mounted on %s for CSS authentication",
570                    me->mnt_fsname,
571                    me->mnt_dir );
572           ctx->rd = DVDOpenImageFile( ctx, me->mnt_fsname, NULL, have_css );
573           dev_name = strdup(me->mnt_fsname);
574           break;
575         }
576       }
577       fclose( mntfile );
578     }
579 #elif defined(_WIN32) || defined(__OS2__)
580 #ifdef __OS2__
581     /* Use DVDOpenImageFile() only if it is a drive */
582     if(isalpha(path[0]) && path[1] == ':' &&
583         ( !path[2] ||
584           ((path[2] == '\\' || path[2] == '/') && !path[3])))
585 #endif
586     ctx->rd = DVDOpenImageFile( ctx, path, NULL, have_css );
587 #endif
588 
589 #if !defined(_WIN32) && !defined(__OS2__)
590     if( !dev_name ) {
591       Log0(ctx, "Couldn't find device name." );
592     } else if( !ctx->rd ) {
593       Log0(ctx, "Device %s inaccessible, "
594                 "CSS authentication not available.", dev_name );
595     }
596 #else
597     if( !ctx->rd ) {
598         Log0(ctx, "Device %s inaccessible, "
599                  "CSS authentication not available.", path );
600     }
601 #endif
602 
603     free( dev_name );
604     dev_name = NULL;
605     free( path_copy );
606     path_copy = NULL;
607 
608     /**
609      * If we've opened a drive, just use that.
610      */
611     if(ctx->rd)
612     {
613         free(path);
614         return ctx;
615     }
616     /**
617      * Otherwise, we now try to open the directory tree instead.
618      */
619     ctx->rd = DVDOpenPath( path );
620     free( path );
621     if(!ctx->rd)
622     {
623         free(ctx);
624         return NULL;
625     }
626     return ctx;
627   }
628 
629 DVDOpen_error:
630   /* If it's none of the above, screw it. */
631   Log0(ctx, "Could not open %s", path );
632   free( path );
633   free( path_copy );
634   if ( cdir >= 0 )
635     close( cdir );
636   free( new_path );
637   return NULL;
638 }
639 
640 dvd_reader_t *DVDOpen( const char *ppath )
641 {
642     return DVDOpenCommon( NULL, NULL, ppath, NULL );
643 }
644 
645 dvd_reader_t *DVDOpenStream( void *stream,
646                              dvd_reader_stream_cb *stream_cb )
647 {
648     return DVDOpenCommon( stream, NULL, NULL, stream_cb );
649 }
650 
651 dvd_reader_t *DVDOpen2( void *priv, const dvd_logger_cb *logcb,
652                         const char *ppath )
653 {
654     return DVDOpenCommon( priv, logcb, ppath, NULL );
655 }
656 
657 dvd_reader_t *DVDOpenStream2( void *priv, const dvd_logger_cb *logcb,
658                               dvd_reader_stream_cb *stream_cb )
659 {
660     return DVDOpenCommon( priv, logcb, NULL, stream_cb );
661 }
662 
663 void DVDClose( dvd_reader_t *dvd )
664 {
665   if( dvd ) {
666     if( dvd->rd->dev ) dvdinput_close( dvd->rd->dev );
667     if( dvd->rd->path_root ) free( dvd->rd->path_root );
668     if( dvd->rd->udfcache ) FreeUDFCache( dvd->rd->udfcache );
669     free( dvd->rd );
670     free( dvd );
671   }
672 }
673 
674 /**
675  * Open an unencrypted file on a DVD image file.
676  */
677 static dvd_file_t *DVDOpenFileUDF( dvd_reader_t *ctx, const char *filename,
678                                    int do_cache )
679 {
680   uint32_t start, len;
681   dvd_file_t *dvd_file;
682 
683   start = UDFFindFile( ctx, filename, &len );
684   if( !start ) {
685     Log0(ctx, "DVDOpenFileUDF:UDFFindFile %s failed", filename );
686     return NULL;
687   }
688 
689   dvd_file = calloc( 1, sizeof( dvd_file_t ) );
690   if( !dvd_file ) {
691     Log0(ctx, "DVDOpenFileUDF:malloc failed" );
692     return NULL;
693   }
694   dvd_file->ctx = ctx;
695   dvd_file->lb_start = start;
696   dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
697 
698   /* Read the whole file in cache (unencrypted) if asked and if it doesn't
699    * exceed 128KB */
700   if( do_cache && len < 64 * DVD_VIDEO_LB_LEN ) {
701     int ret;
702 
703     dvd_file->cache = malloc( len );
704     if( !dvd_file->cache )
705         return dvd_file;
706 
707     ret = InternalUDFReadBlocksRaw( ctx, dvd_file->lb_start,
708                                     dvd_file->filesize, dvd_file->cache,
709                                     DVDINPUT_NOFLAGS );
710     if( ret != dvd_file->filesize ) {
711         free( dvd_file->cache );
712         dvd_file->cache = NULL;
713     }
714   }
715 
716   return dvd_file;
717 }
718 
719 /**
720  * Searches for <file> in directory <path>, ignoring case.
721  * Returns 0 and full filename in <filename>.
722  *     or -1 on file not found.
723  *     or -2 on path not found.
724  */
725 static int findDirFile( const char *path, const char *file, char *filename )
726 {
727   DIR *dir;
728   struct dirent *ent;
729 
730   dir = opendir( path );
731   if( !dir ) return -2;
732 
733   while( ( ent = readdir( dir ) ) != NULL ) {
734     if( !strcasecmp( ent->d_name, file ) ) {
735       sprintf( filename, "%s%s%s", path,
736                ( ( path[ strlen( path ) - 1 ] == '/' ) ? "" : "/" ),
737                ent->d_name );
738       closedir(dir);
739       return 0;
740     }
741   }
742   closedir(dir);
743   return -1;
744 }
745 
746 static int findDVDFile( dvd_reader_t *dvd, const char *file, char *filename )
747 {
748   const char *nodirfile;
749   int ret;
750 
751   /* Strip off the directory for our search */
752   if( !strncasecmp( "/VIDEO_TS/", file, 10 ) ) {
753     nodirfile = &(file[ 10 ]);
754   } else {
755     nodirfile = file;
756   }
757 
758   ret = findDirFile( dvd->rd->path_root, nodirfile, filename );
759   if( ret < 0 ) {
760     char video_path[ PATH_MAX + 1 ];
761 
762     /* Try also with adding the path, just in case. */
763     sprintf( video_path, "%s/VIDEO_TS/", dvd->rd->path_root );
764     ret = findDirFile( video_path, nodirfile, filename );
765     if( ret < 0 ) {
766       /* Try with the path, but in lower case. */
767       sprintf( video_path, "%s/video_ts/", dvd->rd->path_root );
768       ret = findDirFile( video_path, nodirfile, filename );
769       if( ret < 0 ) {
770         return 0;
771       }
772     }
773   }
774 
775   return 1;
776 }
777 
778 /**
779  * Open an unencrypted file from a DVD directory tree.
780  */
781 static dvd_file_t *DVDOpenFilePath( dvd_reader_t *ctx, const char *filename )
782 {
783   char full_path[ PATH_MAX + 1 ];
784   dvd_file_t *dvd_file;
785   struct stat fileinfo;
786   dvd_input_t dev;
787 
788   /* Get the full path of the file. */
789   if( !findDVDFile( ctx, filename, full_path ) ) {
790     Log0(ctx, "DVDOpenFilePath:findDVDFile %s failed", filename );
791     return NULL;
792   }
793 
794   dev = dvdinput_open( ctx->priv, &ctx->logcb, full_path, NULL );
795   if( !dev ) {
796     Log0(ctx, "DVDOpenFilePath:dvdinput_open %s failed", full_path );
797     return NULL;
798   }
799 
800   dvd_file = calloc( 1, sizeof( dvd_file_t ) );
801   if( !dvd_file ) {
802     Log0(ctx, "DVDOpenFilePath:dvd_file malloc failed" );
803     dvdinput_close(dev);
804     return NULL;
805   }
806   dvd_file->ctx = ctx;
807 
808   if( stat( full_path, &fileinfo ) < 0 ) {
809     Log0(ctx, "Can't stat() %s.", filename );
810     free( dvd_file );
811     dvdinput_close( dev );
812     return NULL;
813   }
814   dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
815   dvd_file->title_devs[ 0 ] = dev;
816   dvd_file->filesize = dvd_file->title_sizes[ 0 ];
817 
818   return dvd_file;
819 }
820 
821 static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *ctx, int title, int menu )
822 {
823   char filename[ MAX_UDF_FILE_NAME_LEN ];
824   uint32_t start, len;
825   dvd_file_t *dvd_file;
826 
827   if( title == 0 ) {
828     strcpy( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
829   } else {
830     sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
831   }
832   start = UDFFindFile( ctx, filename, &len );
833   if( start == 0 ) return NULL;
834 
835   dvd_file = calloc( 1, sizeof( dvd_file_t ) );
836   if( !dvd_file ) return NULL;
837   dvd_file->ctx = ctx;
838   /*Hack*/ dvd_file->css_title = title << 1 | menu;
839   dvd_file->lb_start = start;
840   dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
841 
842   /* Calculate the complete file size for every file in the VOBS */
843   if( !menu ) {
844     int cur;
845 
846     for( cur = 2; cur < 10; cur++ ) {
847       sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur );
848       if( !UDFFindFile( ctx, filename, &len ) ) break;
849       dvd_file->filesize += len / DVD_VIDEO_LB_LEN;
850     }
851   }
852 
853   if( ctx->rd->css_state == 1 /* Need key init */ ) {
854     initAllCSSKeys( ctx );
855     ctx->rd->css_state = 2;
856   }
857   /*
858   if( dvdinput_title( dvd_file->dvd->dev, (int)start ) < 0 ) {
859       Log0(ctx, "Error cracking CSS key for %s", filename );
860   }
861   */
862 
863   return dvd_file;
864 }
865 
866 static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *ctx, int title, int menu )
867 {
868   char filename[ MAX_UDF_FILE_NAME_LEN ];
869   char full_path[ PATH_MAX + 1 ];
870   struct stat fileinfo;
871   dvd_file_t *dvd_file;
872 
873   dvd_file = calloc( 1, sizeof( dvd_file_t ) );
874   if( !dvd_file ) return NULL;
875   dvd_file->ctx = ctx;
876   /*Hack*/ dvd_file->css_title = title << 1 | menu;
877 
878   if( menu ) {
879     dvd_input_t dev;
880 
881     if( title == 0 ) {
882       strcpy( filename, "VIDEO_TS.VOB" );
883     } else {
884       sprintf( filename, "VTS_%02i_0.VOB", title );
885     }
886     if( !findDVDFile( ctx, filename, full_path ) ) {
887       free( dvd_file );
888       return NULL;
889     }
890 
891     dev = dvdinput_open( ctx->priv, &ctx->logcb, full_path, NULL );
892     if( dev == NULL ) {
893       free( dvd_file );
894       return NULL;
895     }
896 
897     if( stat( full_path, &fileinfo ) < 0 ) {
898       Log0(ctx, "Can't stat() %s.", filename );
899       dvdinput_close(dev);
900       free( dvd_file );
901       return NULL;
902     }
903     dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
904     dvd_file->title_devs[ 0 ] = dev;
905     dvdinput_title( dvd_file->title_devs[0], 0);
906     dvd_file->filesize = dvd_file->title_sizes[ 0 ];
907 
908   } else {
909     int i;
910 
911     for( i = 0; i < TITLES_MAX; ++i ) {
912 
913       sprintf( filename, "VTS_%02i_%i.VOB", title, i + 1 );
914       if( !findDVDFile( ctx, filename, full_path ) ) {
915         break;
916       }
917 
918       if( stat( full_path, &fileinfo ) < 0 ) {
919         Log0(ctx, "Can't stat() %s.", filename );
920         break;
921       }
922 
923       dvd_file->title_sizes[ i ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
924       dvd_file->title_devs[ i ] = dvdinput_open( ctx->priv, &ctx->logcb, full_path, NULL );
925       dvdinput_title( dvd_file->title_devs[ i ], 0 );
926       dvd_file->filesize += dvd_file->title_sizes[ i ];
927     }
928     if( !dvd_file->title_devs[ 0 ] ) {
929       free( dvd_file );
930       return NULL;
931     }
932   }
933 
934   return dvd_file;
935 }
936 
937 dvd_file_t *DVDOpenFile( dvd_reader_t *ctx, int titlenum,
938                          dvd_read_domain_t domain )
939 {
940   dvd_reader_device_t *dvd = ctx->rd;
941   char filename[ MAX_UDF_FILE_NAME_LEN ];
942   int do_cache = 0;
943 
944   /* Check arguments. */
945   if( dvd == NULL || titlenum < 0 )
946     return NULL;
947 
948   switch( domain ) {
949   case DVD_READ_INFO_FILE:
950     if( titlenum == 0 ) {
951       strcpy( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
952     } else {
953       sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );
954     }
955     do_cache = 1;
956     break;
957   case DVD_READ_INFO_BACKUP_FILE:
958     if( titlenum == 0 ) {
959       strcpy( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
960     } else {
961       sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );
962     }
963     do_cache = 1;
964     break;
965   case DVD_READ_MENU_VOBS:
966     if( dvd->isImageFile ) {
967       return DVDOpenVOBUDF( ctx, titlenum, 1 );
968     } else {
969       return DVDOpenVOBPath( ctx, titlenum, 1 );
970     }
971     break;
972   case DVD_READ_TITLE_VOBS:
973     if( titlenum == 0 ) return NULL;
974     if( dvd->isImageFile ) {
975       return DVDOpenVOBUDF( ctx, titlenum, 0 );
976     } else {
977       return DVDOpenVOBPath( ctx, titlenum, 0 );
978     }
979     break;
980   default:
981     Log1(ctx, "Invalid domain for file open." );
982     return NULL;
983   }
984 
985   if( dvd->isImageFile ) {
986     return DVDOpenFileUDF( ctx, filename, do_cache );
987   } else {
988     return DVDOpenFilePath( ctx, filename );
989   }
990 }
991 
992 void DVDCloseFile( dvd_file_t *dvd_file )
993 {
994   dvd_reader_device_t *dvd = dvd_file->ctx->rd;
995   if( dvd_file && dvd ) {
996     if( !dvd->isImageFile ) {
997       int i;
998 
999       for( i = 0; i < TITLES_MAX; ++i ) {
1000         if( dvd_file->title_devs[ i ] ) {
1001           dvdinput_close( dvd_file->title_devs[i] );
1002         }
1003       }
1004     }
1005 
1006     free( dvd_file->cache );
1007     free( dvd_file );
1008     dvd_file = NULL;
1009   }
1010 }
1011 
1012 static int DVDFileStatVOBUDF( dvd_reader_t *dvd, int title,
1013                               int menu, dvd_stat_t *statbuf )
1014 {
1015   char filename[ MAX_UDF_FILE_NAME_LEN ];
1016   uint32_t size;
1017   off_t tot_size;
1018   off_t parts_size[ 9 ];
1019   int nr_parts = 0;
1020   int n;
1021 
1022   if( title == 0 )
1023     strcpy( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
1024   else
1025     sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
1026 
1027   if( !UDFFindFile( dvd, filename, &size ) )
1028     return -1;
1029 
1030   tot_size = size;
1031   nr_parts = 1;
1032   parts_size[ 0 ] = size;
1033 
1034   if( !menu ) {
1035     int cur;
1036 
1037     for( cur = 2; cur < 10; cur++ ) {
1038       sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur );
1039       if( !UDFFindFile( dvd, filename, &size ) )
1040         break;
1041 
1042       parts_size[ nr_parts ] = size;
1043       tot_size += size;
1044       nr_parts++;
1045     }
1046   }
1047 
1048   statbuf->size = tot_size;
1049   statbuf->nr_parts = nr_parts;
1050   for( n = 0; n < nr_parts; n++ )
1051     statbuf->parts_size[ n ] = parts_size[ n ];
1052 
1053   return 0;
1054 }
1055 
1056 
1057 static int DVDFileStatVOBPath( dvd_reader_t *dvd, int title,
1058                                int menu, dvd_stat_t *statbuf )
1059 {
1060   char filename[ MAX_UDF_FILE_NAME_LEN ];
1061   char full_path[ PATH_MAX + 1 ];
1062   struct stat fileinfo;
1063   off_t tot_size;
1064   off_t parts_size[ 9 ];
1065   int nr_parts = 0;
1066   int n;
1067 
1068   if( title == 0 )
1069     strcpy( filename, "VIDEO_TS.VOB" );
1070   else
1071     sprintf( filename, "VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
1072 
1073   if( !findDVDFile( dvd, filename, full_path ) )
1074     return -1;
1075 
1076   if( stat( full_path, &fileinfo ) < 0 ) {
1077     Log1(dvd, "Can't stat() %s.", filename );
1078     return -1;
1079   }
1080 
1081   tot_size = fileinfo.st_size;
1082   nr_parts = 1;
1083   parts_size[ 0 ] = fileinfo.st_size;
1084 
1085   if( !menu ) {
1086     int cur;
1087     for( cur = 2; cur < 10; cur++ ) {
1088       sprintf( filename, "VTS_%02d_%d.VOB", title, cur );
1089       if( !findDVDFile( dvd, filename, full_path ) )
1090         break;
1091 
1092       if( stat( full_path, &fileinfo ) < 0 ) {
1093         Log1(dvd, "Can't stat() %s.", filename );
1094         break;
1095       }
1096 
1097       parts_size[ nr_parts ] = fileinfo.st_size;
1098       tot_size += parts_size[ nr_parts ];
1099       nr_parts++;
1100     }
1101   }
1102 
1103   statbuf->size = tot_size;
1104   statbuf->nr_parts = nr_parts;
1105   for( n = 0; n < nr_parts; n++ )
1106     statbuf->parts_size[ n ] = parts_size[ n ];
1107 
1108   return 0;
1109 }
1110 
1111 
1112 int DVDFileStat( dvd_reader_t *reader, int titlenum,
1113                  dvd_read_domain_t domain, dvd_stat_t *statbuf )
1114 {
1115   dvd_reader_device_t *dvd = reader->rd;
1116   char filename[ MAX_UDF_FILE_NAME_LEN ];
1117   struct stat fileinfo;
1118   uint32_t size;
1119 
1120   /* Check arguments. */
1121   if( dvd == NULL || titlenum < 0 ) {
1122     errno = EINVAL;
1123     return -1;
1124   }
1125 
1126   switch( domain ) {
1127   case DVD_READ_INFO_FILE:
1128     if( titlenum == 0 )
1129       strcpy( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
1130     else
1131       sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );
1132 
1133     break;
1134   case DVD_READ_INFO_BACKUP_FILE:
1135     if( titlenum == 0 )
1136       strcpy( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
1137     else
1138       sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );
1139 
1140     break;
1141   case DVD_READ_MENU_VOBS:
1142     if( dvd->isImageFile )
1143       return DVDFileStatVOBUDF( reader, titlenum, 1, statbuf );
1144     else
1145       return DVDFileStatVOBPath( reader, titlenum, 1, statbuf );
1146 
1147     break;
1148   case DVD_READ_TITLE_VOBS:
1149     if( titlenum == 0 )
1150       return -1;
1151 
1152     if( dvd->isImageFile )
1153       return DVDFileStatVOBUDF( reader, titlenum, 0, statbuf );
1154     else
1155       return DVDFileStatVOBPath( reader, titlenum, 0, statbuf );
1156 
1157     break;
1158   default:
1159     Log1(reader, "Invalid domain for file stat." );
1160     errno = EINVAL;
1161     return -1;
1162   }
1163 
1164   if( dvd->isImageFile ) {
1165     if( UDFFindFile( reader, filename, &size ) ) {
1166       statbuf->size = size;
1167       statbuf->nr_parts = 1;
1168       statbuf->parts_size[ 0 ] = size;
1169       return 0;
1170     }
1171   } else {
1172     char full_path[ PATH_MAX + 1 ];
1173 
1174     if( findDVDFile( reader, filename, full_path ) ) {
1175       if( stat( full_path, &fileinfo ) < 0 )
1176         Log1(reader, "Can't stat() %s.", filename );
1177       else {
1178         statbuf->size = fileinfo.st_size;
1179         statbuf->nr_parts = 1;
1180         statbuf->parts_size[ 0 ] = statbuf->size;
1181         return 0;
1182       }
1183     }
1184   }
1185   return -1;
1186 }
1187 
1188 /* Internal, but used from dvd_udf.c */
1189 int InternalUDFReadBlocksRaw( const dvd_reader_t *ctx, uint32_t lb_number,
1190                       size_t block_count, unsigned char *data,
1191                       int encrypted )
1192 {
1193   int ret;
1194 
1195   if( !ctx->rd->dev ) {
1196     Log0(ctx, "Fatal error in block read." );
1197     return -1;
1198   }
1199 
1200   ret = dvdinput_seek( ctx->rd->dev, (int) lb_number );
1201   if( ret != (int) lb_number ) {
1202     Log1(ctx, "Can't seek to block %u", lb_number );
1203     return ret;
1204   }
1205 
1206   ret = dvdinput_read( ctx->rd->dev, (char *) data,
1207                        (int) block_count, encrypted );
1208   return ret;
1209 }
1210 
1211 /* This is using a single input and starting from 'dvd_file->lb_start' offset.
1212  *
1213  * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
1214  * into the buffer located at 'data' and if 'encrypted' is set
1215  * descramble the data if it's encrypted.  Returning either an
1216  * negative error or the number of blocks read. */
1217 static int DVDReadBlocksUDF( const dvd_file_t *dvd_file, uint32_t offset,
1218                              size_t block_count, unsigned char *data,
1219                              int encrypted )
1220 {
1221   /* If the cache is present and we don't need to decrypt, use the cache to
1222    * feed the data */
1223   if( dvd_file->cache && (encrypted & DVDINPUT_READ_DECRYPT) == 0 ) {
1224     /* Check if we don't exceed the cache (or file) size */
1225     if( block_count + offset > (size_t) dvd_file->filesize )
1226       return 0;
1227 
1228     /* Copy the cache at a specified offset into data. offset and block_count
1229      * must be converted into bytes */
1230     memcpy( data, dvd_file->cache + (off_t)offset * (off_t)DVD_VIDEO_LB_LEN,
1231             (off_t)block_count * (off_t)DVD_VIDEO_LB_LEN );
1232 
1233     /* return the amount of blocks copied */
1234     return block_count;
1235   } else {
1236     /* use dvdinput access */
1237     return InternalUDFReadBlocksRaw( dvd_file->ctx, dvd_file->lb_start + offset,
1238                              block_count, data, encrypted );
1239   }
1240 }
1241 
1242 /* This is using possibly several inputs and starting from an offset of '0'.
1243  *
1244  * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
1245  * into the buffer located at 'data' and if 'encrypted' is set
1246  * descramble the data if it's encrypted.  Returning either an
1247  * negative error or the number of blocks read. */
1248 static int DVDReadBlocksPath( const dvd_file_t *dvd_file, unsigned int offset,
1249                               size_t block_count, unsigned char *data,
1250                               int encrypted )
1251 {
1252   const dvd_reader_t *ctx = dvd_file->ctx;
1253   int i;
1254   int ret, ret2, off;
1255 
1256   ret = 0;
1257   ret2 = 0;
1258   for( i = 0; i < TITLES_MAX; ++i ) {
1259     if( !dvd_file->title_sizes[ i ] ) return 0; /* Past end of file */
1260 
1261     if( offset < dvd_file->title_sizes[ i ] ) {
1262       if( ( offset + block_count ) <= dvd_file->title_sizes[ i ] ) {
1263         off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );
1264         if( off < 0 || off != (int)offset ) {
1265           Log1(ctx, "Can't seek to block %u", offset );
1266           return off < 0 ? off : 0;
1267         }
1268         ret = dvdinput_read( dvd_file->title_devs[ i ], data,
1269                              (int)block_count, encrypted );
1270         break;
1271       } else {
1272         size_t part1_size = dvd_file->title_sizes[ i ] - offset;
1273         /* FIXME: Really needs to be a while loop.
1274          * (This is only true if you try and read >1GB at a time) */
1275 
1276         /* Read part 1 */
1277         off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );
1278         if( off < 0 || off != (int)offset ) {
1279           Log1(ctx, "Can't seek to block %u", offset );
1280           return off < 0 ? off : 0;
1281         }
1282         ret = dvdinput_read( dvd_file->title_devs[ i ], data,
1283                              (int)part1_size, encrypted );
1284         if( ret < 0 ) return ret;
1285         /* FIXME: This is wrong if i is the last file in the set.
1286          * also error from this read will not show in ret. */
1287 
1288         /* Does the next part exist? If not then return now. */
1289         if( i + 1 >= TITLES_MAX || !dvd_file->title_devs[ i + 1 ] )
1290           return ret;
1291 
1292         /* Read part 2 */
1293         off = dvdinput_seek( dvd_file->title_devs[ i + 1 ], 0 );
1294         if( off < 0 || off != 0 ) {
1295           Log1(ctx, "Can't seek to block %d", 0 );
1296           return off < 0 ? off : 0;
1297         }
1298         ret2 = dvdinput_read( dvd_file->title_devs[ i + 1 ],
1299                               data + ( part1_size
1300                                        * (int64_t)DVD_VIDEO_LB_LEN ),
1301                               (int)(block_count - part1_size),
1302                               encrypted );
1303         if( ret2 < 0 ) return ret2;
1304         break;
1305       }
1306     } else {
1307       offset -= dvd_file->title_sizes[ i ];
1308     }
1309   }
1310 
1311   return ret + ret2;
1312 }
1313 
1314 /* This is broken reading more than 2Gb at a time is ssize_t is 32-bit. */
1315 ssize_t DVDReadBlocks( dvd_file_t *dvd_file, int offset,
1316                        size_t block_count, unsigned char *data )
1317 {
1318   dvd_reader_t *ctx = dvd_file->ctx;
1319   dvd_reader_device_t *dvd = ctx->rd;
1320   int ret;
1321 
1322   /* Check arguments. */
1323   if( dvd_file == NULL || offset < 0 || data == NULL )
1324     return -1;
1325 
1326   /* Hack, and it will still fail for multiple opens in a threaded app ! */
1327   if( dvd->css_title != dvd_file->css_title ) {
1328       dvd->css_title = dvd_file->css_title;
1329     if( dvd->isImageFile ) {
1330       dvdinput_title( dvd->dev, (int)dvd_file->lb_start );
1331     }
1332     /* Here each vobu has it's own dvdcss handle, so no need to update
1333     else {
1334       dvdinput_title( dvd_file->title_devs[ 0 ], (int)dvd_file->lb_start );
1335     }*/
1336   }
1337 
1338   if( dvd->isImageFile ) {
1339     ret = DVDReadBlocksUDF( dvd_file, (uint32_t)offset,
1340                             block_count, data, DVDINPUT_READ_DECRYPT );
1341   } else {
1342     ret = DVDReadBlocksPath( dvd_file, (unsigned int)offset,
1343                              block_count, data, DVDINPUT_READ_DECRYPT );
1344   }
1345 
1346   return (ssize_t)ret;
1347 }
1348 
1349 int32_t DVDFileSeek( dvd_file_t *dvd_file, int32_t offset )
1350 {
1351   /* Check arguments. */
1352   if( dvd_file == NULL || offset < 0 )
1353     return -1;
1354 
1355   if( offset > dvd_file->filesize * DVD_VIDEO_LB_LEN ) {
1356     return -1;
1357   }
1358   dvd_file->seek_pos = (uint32_t) offset;
1359   return offset;
1360 }
1361 
1362 int DVDFileSeekForce(dvd_file_t *dvd_file, int offset, int force_size)
1363 {
1364   dvd_reader_t *ctx = dvd_file->ctx;
1365   dvd_reader_device_t *dvd = ctx->rd;
1366   /* Check arguments. */
1367   if( dvd_file == NULL || offset <= 0 )
1368       return -1;
1369 
1370   if( dvd->isImageFile ) {
1371     if( force_size < 0 )
1372       force_size = (offset - 1) / DVD_VIDEO_LB_LEN + 1;
1373     if( dvd_file->filesize < force_size ) {
1374       dvd_file->filesize = force_size;
1375       free(dvd_file->cache);
1376       dvd_file->cache = NULL;
1377       Log2(ctx, "Ignored size of file indicated in UDF.");
1378     }
1379   }
1380 
1381   if( offset > dvd_file->filesize * DVD_VIDEO_LB_LEN )
1382     return -1;
1383 
1384   dvd_file->seek_pos = (uint32_t) offset;
1385   return offset;
1386 }
1387 
1388 ssize_t DVDReadBytes( dvd_file_t *dvd_file, void *data, size_t byte_size )
1389 {
1390   dvd_reader_t *ctx = dvd_file->ctx;
1391   dvd_reader_device_t *dvd = ctx->rd;
1392   unsigned char *secbuf_base, *secbuf;
1393   unsigned int numsec, seek_sector, seek_byte;
1394   int ret;
1395 
1396   /* Check arguments. */
1397   if( dvd_file == NULL || data == NULL || (ssize_t)byte_size < 0 )
1398     return -1;
1399 
1400   seek_sector = dvd_file->seek_pos / DVD_VIDEO_LB_LEN;
1401   seek_byte   = dvd_file->seek_pos % DVD_VIDEO_LB_LEN;
1402 
1403   numsec = ( ( seek_byte + byte_size ) / DVD_VIDEO_LB_LEN ) +
1404     ( ( ( seek_byte + byte_size ) % DVD_VIDEO_LB_LEN ) ? 1 : 0 );
1405 
1406   secbuf_base = malloc( numsec * DVD_VIDEO_LB_LEN + 2048 );
1407   if( !secbuf_base ) {
1408     Log0(ctx, "Can't allocate memory for file read" );
1409     return 0;
1410   }
1411   secbuf = (unsigned char *)(((uintptr_t)secbuf_base & ~((uintptr_t)2047)) + 2048);
1412 
1413   if( dvd->isImageFile ) {
1414     ret = DVDReadBlocksUDF( dvd_file, (uint32_t) seek_sector,
1415                             (size_t) numsec, secbuf, DVDINPUT_NOFLAGS );
1416   } else {
1417     ret = DVDReadBlocksPath( dvd_file, seek_sector,
1418                              (size_t) numsec, secbuf, DVDINPUT_NOFLAGS );
1419   }
1420 
1421   if( ret != (int) numsec ) {
1422     free( secbuf_base );
1423     return ret < 0 ? ret : 0;
1424   }
1425 
1426   memcpy( data, &(secbuf[ seek_byte ]), byte_size );
1427   free( secbuf_base );
1428 
1429   DVDFileSeekForce(dvd_file, dvd_file->seek_pos + byte_size, -1);
1430   return byte_size;
1431 }
1432 
1433 ssize_t DVDFileSize( dvd_file_t *dvd_file )
1434 {
1435   /* Check arguments. */
1436   if( dvd_file == NULL )
1437     return -1;
1438 
1439   return dvd_file->filesize;
1440 }
1441 
1442 int DVDDiscID( dvd_reader_t *dvd, unsigned char *discid )
1443 {
1444   struct md5_s ctx;
1445   int title;
1446   int title_sets;
1447   int nr_of_files = 0;
1448   ifo_handle_t *vmg_ifo;
1449 
1450   /* Check arguments. */
1451   if( dvd == NULL || discid == NULL )
1452     return 0;
1453 
1454   vmg_ifo = ifoOpen( dvd, 0 );
1455   if( !vmg_ifo ) {
1456     Log0(dvd, "DVDDiscId, failed to open VMG IFO" );
1457     return -1;
1458   }
1459 
1460   title_sets = vmg_ifo->vmgi_mat->vmg_nr_of_title_sets + 1;
1461   ifoClose( vmg_ifo );
1462 
1463   if( title_sets > 10 )
1464   	title_sets = 10;
1465 
1466   /* Go through the first IFO:s, in order, up until the tenth,
1467    * and md5sum them, i.e  VIDEO_TS.IFO and VTS_0?_0.IFO */
1468   InitMD5( &ctx );
1469   for( title = 0; title < title_sets; title++ ) {
1470     dvd_file_t *dvd_file = DVDOpenFile( dvd, title, DVD_READ_INFO_FILE );
1471     if( dvd_file != NULL ) {
1472       ssize_t bytes_read;
1473       ssize_t file_size = dvd_file->filesize * DVD_VIDEO_LB_LEN;
1474       char *buffer_base = malloc( file_size + 2048 );
1475 
1476       if( buffer_base == NULL ) {
1477           DVDCloseFile( dvd_file );
1478           Log0(dvd, "DVDDiscId, failed to allocate memory for file read" );
1479           return -1;
1480       }
1481 
1482       char *buffer = (char *)(((uintptr_t)buffer_base & ~((uintptr_t)2047)) + 2048);
1483 
1484       bytes_read = DVDReadBytes( dvd_file, buffer, file_size );
1485       if( bytes_read != file_size ) {
1486           Log1(dvd, "DVDDiscId read returned %zd bytes"
1487                    ", wanted %zd", bytes_read, file_size );
1488           DVDCloseFile( dvd_file );
1489           free( buffer_base );
1490           return -1;
1491       }
1492 
1493       AddMD5( &ctx, buffer, file_size );
1494 
1495       DVDCloseFile( dvd_file );
1496       free( buffer_base );
1497       nr_of_files++;
1498     }
1499   }
1500   EndMD5( &ctx );
1501   memcpy( discid, ctx.buf, 16 );
1502   if(!nr_of_files)
1503     return -1;
1504 
1505   return 0;
1506 }
1507 
1508 
1509 int DVDISOVolumeInfo( dvd_reader_t *ctx,
1510                       char *volid, unsigned int volid_size,
1511                       unsigned char *volsetid, unsigned int volsetid_size )
1512 {
1513   dvd_reader_device_t *dvd = ctx->rd;
1514   unsigned char *buffer, *buffer_base;
1515   int ret;
1516 
1517   /* Check arguments. */
1518   if( dvd == NULL )
1519     return 0;
1520 
1521   if( dvd->dev == NULL ) {
1522     /* No block access, so no ISO... */
1523     return -1;
1524   }
1525 
1526   buffer_base = malloc( DVD_VIDEO_LB_LEN + 2048 );
1527 
1528   if( buffer_base == NULL ) {
1529     Log0(ctx, "DVDISOVolumeInfo, failed to "
1530              "allocate memory for file read" );
1531     return -1;
1532   }
1533 
1534   buffer = (unsigned char *)(((uintptr_t)buffer_base & ~((uintptr_t)2047)) + 2048);
1535 
1536   ret = InternalUDFReadBlocksRaw( ctx, 16, 1, buffer, 0 );
1537   if( ret != 1 ) {
1538     Log0(ctx, "DVDISOVolumeInfo, failed to "
1539              "read ISO9660 Primary Volume Descriptor" );
1540     free( buffer_base );
1541     return -1;
1542   }
1543 
1544   if( (volid != NULL) && (volid_size > 0) ) {
1545     unsigned int n;
1546     for(n = 0; n < 32; n++) {
1547       if(buffer[40+n] == 0x20) {
1548         break;
1549       }
1550     }
1551 
1552     if(volid_size > n+1) {
1553       volid_size = n+1;
1554     }
1555 
1556     memcpy(volid, &buffer[40], volid_size-1);
1557     volid[volid_size-1] = '\0';
1558   }
1559 
1560   if( (volsetid != NULL) && (volsetid_size > 0) ) {
1561     if(volsetid_size > 128) {
1562       volsetid_size = 128;
1563     }
1564     memcpy(volsetid, &buffer[190], volsetid_size);
1565   }
1566   free( buffer_base );
1567   return 0;
1568 }
1569 
1570 
1571 int DVDUDFVolumeInfo( dvd_reader_t *ctx,
1572                       char *volid, unsigned int volid_size,
1573                       unsigned char *volsetid, unsigned int volsetid_size )
1574 {
1575   int ret;
1576   /* Check arguments. */
1577   if( ctx == NULL || ctx->rd == NULL )
1578     return -1;
1579 
1580   if( ctx->rd->dev == NULL ) {
1581     /* No block access, so no UDF VolumeSet Identifier */
1582     return -1;
1583   }
1584 
1585   if( (volid != NULL) && (volid_size > 0) ) {
1586     ret = UDFGetVolumeIdentifier(ctx, volid, volid_size);
1587     if(!ret) {
1588       return -1;
1589     }
1590   }
1591   if( (volsetid != NULL) && (volsetid_size > 0) ) {
1592     ret =  UDFGetVolumeSetIdentifier(ctx, volsetid, volsetid_size);
1593     if(!ret) {
1594       return -1;
1595     }
1596   }
1597 
1598   return 0;
1599 }
1600