dvd_reader.c 41.1 KB
Newer Older
1
2
/*
 * Copyright (C) 2001-2004 Billy Biggs <vektor@dumbterm.net>,
3
4
 *                         Håkan Hjort <d95hjort@dtek.chalmers.se>,
 *                         Björn Englund <d4bjorn@dtek.chalmers.se>
5
 *
6
7
8
 * This file is part of libdvdread.
 *
 * 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
11
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
12
 *
13
14
15
16
 * libdvdread is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
17
 *
18
19
20
 * You should have received a copy of the GNU General Public License along
 * with libdvdread; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
22
 */

Marcel Mol's avatar
Marcel Mol committed
23
#include "config.h"
24
25
26
27
28
29
30
31
32
33
34
#include <sys/types.h>      /* off_t */
#include <sys/stat.h>       /* stat */
#include <sys/time.h>       /* For the timing of dvdcss_title crack. */
#include <fcntl.h>          /* open */
#include <stdlib.h>         /* free */
#include <stdio.h>          /* fprintf */
#include <errno.h>          /* errno, EIN* */
#include <string.h>         /* memcpy, strlen */
#include <unistd.h>         /* chdir, getcwd */
#include <limits.h>         /* PATH_MAX */
#include <dirent.h>         /* opendir, readdir */
35
#include <ctype.h>          /* isalpha */
36
37
38

/* misc win32 helpers */
#ifdef WIN32
39
40
41
# ifndef HAVE_GETTIMEOFDAY
   /* replacement gettimeofday implementation */
#  include <sys/timeb.h>
42
43
44
45
46
47
48
49
static inline int _private_gettimeofday( struct timeval *tv, void *tz )
{
  struct timeb t;
  ftime( &t );
  tv->tv_sec = t.time;
  tv->tv_usec = t.millitm * 1000;
  return 0;
}
50
51
52
53
#  define gettimeofday(TV, TZ) _private_gettimeofday((TV), (TZ))
# endif
# include <io.h> /* read() */
# define lseek64 _lseeki64
54
#endif
Erik Hovland's avatar
Erik Hovland committed
55

56
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__) || defined(__APPLE__)
57
# define SYS_BSD 1
58
59
60
#endif

#if defined(__sun)
61
# include <sys/mnttab.h>
62
#elif defined(__APPLE__)
63
64
65
# include <sys/param.h>
# include <sys/ucred.h>
# include <sys/mount.h>
66
#elif defined(SYS_BSD)
67
# include <fstab.h>
68
#elif defined(__linux__)
69
70
# include <mntent.h>
# include <paths.h>
71
72
#endif

73
74
#include "dvdread/dvd_udf.h"
#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
81
82

#define DEFAULT_UDF_CACHE_LEVEL 1

struct dvd_reader_s {
83
84
  /* Basic information. */
  int isImageFile;
Erik Hovland's avatar
Erik Hovland committed
85

86
87
88
89
  /* Hack for keeping track of the css status.
   * 0: no css, 1: perhaps (need init of keys), 2: have done init */
  int css_state;
  int css_title; /* Last title that we have called dvdinpute_title for. */
90

91
92
  /* Information required for an image file. */
  dvd_input_t dev;
93

94
95
  /* Information required for a directory path drive. */
  char *path_root;
Erik Hovland's avatar
Erik Hovland committed
96

97
98
99
  /* Filesystem cache */
  int udfcache_level; /* 0 - turned off, 1 - on */
  void *udfcache;
100
101
102
103
104
};

#define TITLES_MAX 9

struct dvd_file_s {
105
106
  /* Basic information. */
  dvd_reader_t *dvd;
Erik Hovland's avatar
Erik Hovland committed
107

108
109
  /* Hack for selecting the right css title. */
  int css_title;
110

111
112
113
  /* Information required for an image file. */
  uint32_t lb_start;
  uint32_t seek_pos;
114

115
116
117
  /* Information required for a directory path drive. */
  size_t title_sizes[ TITLES_MAX ];
  dvd_input_t title_devs[ TITLES_MAX ];
118

119
120
  /* Calculated at open-time, size in blocks. */
  ssize_t filesize;
121
122
};

123
int InternalUDFReadBlocksRaw( const dvd_reader_t *device, uint32_t lb_number,
124
125
                      size_t block_count, unsigned char *data,
                      int encrypted );
126
127
128
129
130
131
132
133
134

/**
 * Set the level of caching on udf
 * level = 0 (no caching)
 * level = 1 (caching filesystem info)
 */
int DVDUDFCacheLevel(dvd_reader_t *device, int level)
{
  struct dvd_reader_s *dev = (struct dvd_reader_s *)device;
Erik Hovland's avatar
Erik Hovland committed
135

136
137
138
139
140
141
142
  if(level > 0) {
    level = 1;
  } else if(level < 0) {
    return dev->udfcache_level;
  }

  dev->udfcache_level = level;
Erik Hovland's avatar
Erik Hovland committed
143

144
145
146
147
148
149
  return level;
}

void *GetUDFCacheHandle(dvd_reader_t *device)
{
  struct dvd_reader_s *dev = (struct dvd_reader_s *)device;
Erik Hovland's avatar
Erik Hovland committed
150

151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
  return dev->udfcache;
}

void SetUDFCacheHandle(dvd_reader_t *device, void *cache)
{
  struct dvd_reader_s *dev = (struct dvd_reader_s *)device;

  dev->udfcache = cache;
}



/* Loop over all titles and call dvdcss_title to crack the keys. */
static int initAllCSSKeys( dvd_reader_t *dvd )
{
166
167
168
169
170
171
172
173
174
  struct timeval all_s, all_e;
  struct timeval t_s, t_e;
  char filename[ MAX_UDF_FILE_NAME_LEN ];
  uint32_t start, len;
  int title;

  char *nokeys_str = getenv("DVDREAD_NOKEYS");
  if(nokeys_str != NULL)
    return 0;
Erik Hovland's avatar
Erik Hovland committed
175

176
177
178
179
180
  fprintf( stderr, "\n" );
  fprintf( stderr, "libdvdread: Attempting to retrieve all CSS keys\n" );
  fprintf( stderr, "libdvdread: This can take a _long_ time, "
           "please be patient\n\n" );
  gettimeofday(&all_s, NULL);
Erik Hovland's avatar
Erik Hovland committed
181

182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
  for( title = 0; title < 100; title++ ) {
    gettimeofday( &t_s, NULL );
    if( title == 0 ) {
      sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
    } else {
      sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 0 );
    }
    start = UDFFindFile( dvd, filename, &len );
    if( start != 0 && len != 0 ) {
      /* Perform CSS key cracking for this title. */
      fprintf( stderr, "libdvdread: Get key for %s at 0x%08x\n",
               filename, start );
      if( dvdinput_title( dvd->dev, (int)start ) < 0 ) {
        fprintf( stderr, "libdvdread: Error cracking CSS key for %s (0x%08x)\n", filename, start);
      }
      gettimeofday( &t_e, NULL );
      fprintf( stderr, "libdvdread: Elapsed time %ld\n",
               (long int) t_e.tv_sec - t_s.tv_sec );
    }
201

202
    if( title == 0 ) continue;
203

204
205
206
207
    gettimeofday( &t_s, NULL );
    sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 1 );
    start = UDFFindFile( dvd, filename, &len );
    if( start == 0 || len == 0 ) break;
208

209
210
211
212
213
    /* Perform CSS key cracking for this title. */
    fprintf( stderr, "libdvdread: Get key for %s at 0x%08x\n",
             filename, start );
    if( dvdinput_title( dvd->dev, (int)start ) < 0 ) {
      fprintf( stderr, "libdvdread: Error cracking CSS key for %s (0x%08x)!!\n", filename, start);
214
    }
215
    gettimeofday( &t_e, NULL );
Erik Hovland's avatar
Erik Hovland committed
216
    fprintf( stderr, "libdvdread: Elapsed time %ld\n",
217
218
219
             (long int) t_e.tv_sec - t_s.tv_sec );
  }
  title--;
Erik Hovland's avatar
Erik Hovland committed
220

221
222
223
224
225
226
  fprintf( stderr, "libdvdread: Found %d VTS's\n", title );
  gettimeofday(&all_e, NULL);
  fprintf( stderr, "libdvdread: Elapsed time %ld\n",
           (long int) all_e.tv_sec - all_s.tv_sec );

  return 0;
227
228
229
230
231
232
233
234
235
}



/**
 * Open a DVD image or block device file.
 */
static dvd_reader_t *DVDOpenImageFile( const char *location, int have_css )
{
236
237
  dvd_reader_t *dvd;
  dvd_input_t dev;
Erik Hovland's avatar
Erik Hovland committed
238

239
240
241
242
243
  dev = dvdinput_open( location );
  if( !dev ) {
    fprintf( stderr, "libdvdread: Can't open %s for reading\n", location );
    return NULL;
  }
244

245
  dvd = malloc( sizeof( dvd_reader_t ) );
246
247
248
249
  if( !dvd ) {
    dvdinput_close(dev);
    return NULL;
  }
250
  memset( dvd, 0, sizeof( dvd_reader_t ) );
251
252
253
  dvd->isImageFile = 1;
  dvd->dev = dev;
  dvd->path_root = NULL;
Erik Hovland's avatar
Erik Hovland committed
254

255
256
  dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
  dvd->udfcache = NULL;
257

258
259
  if( have_css ) {
    /* Only if DVDCSS_METHOD = title, a bit if it's disc or if
Erik Hovland's avatar
Erik Hovland committed
260
     * DVDCSS_METHOD = key but region mismatch. Unfortunately we
261
     * don't have that information. */
Erik Hovland's avatar
Erik Hovland committed
262

263
264
265
    dvd->css_state = 1; /* Need key init. */
  }
  dvd->css_title = 0;
Erik Hovland's avatar
Erik Hovland committed
266

267
  return dvd;
268
269
270
271
}

static dvd_reader_t *DVDOpenPath( const char *path_root )
{
272
273
  dvd_reader_t *dvd;

274
  dvd = malloc( sizeof( dvd_reader_t ) );
275
276
277
278
279
280
  if( !dvd ) return NULL;
  dvd->isImageFile = 0;
  dvd->dev = 0;
  dvd->path_root = strdup( path_root );
  if(!dvd->path_root) {
    free(dvd);
281
    return NULL;
282
283
284
  }
  dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
  dvd->udfcache = NULL;
Erik Hovland's avatar
Erik Hovland committed
285

286
287
  dvd->css_state = 0; /* Only used in the UDF path */
  dvd->css_title = 0; /* Only matters in the UDF path */
288

289
  return dvd;
290
291
292
293
294
295
296
297
}

#if defined(__sun)
/* /dev/rdsk/c0t6d0s0 (link to /devices/...)
   /vol/dev/rdsk/c0t6d0/??
   /vol/rdsk/<name> */
static char *sun_block2char( const char *path )
{
298
  char *new_path;
299

300
301
  /* Must contain "/dsk/" */
  if( !strstr( path, "/dsk/" ) ) return (char *) strdup( path );
302

303
304
  /* Replace "/dsk/" with "/rdsk/" */
  new_path = malloc( strlen(path) + 2 );
305
  if(!new_path) return NULL;
306
307
308
309
  strcpy( new_path, path );
  strcpy( strstr( new_path, "/dsk/" ), "" );
  strcat( new_path, "/rdsk/" );
  strcat( new_path, strstr( path, "/dsk/" ) + strlen( "/dsk/" ) );
310

311
  return new_path;
312
313
314
315
}
#endif

#if defined(SYS_BSD)
Erik Hovland's avatar
Erik Hovland committed
316
/* FreeBSD /dev/(r)(a)cd0c (a is for atapi), recommended to _not_ use r
317
   update: FreeBSD and DragonFly no longer uses the prefix so don't add it.
318
319
320
   OpenBSD /dev/rcd0c, it needs to be the raw device
   NetBSD  /dev/rcd0[d|c|..] d for x86, c (for non x86), perhaps others
   Darwin  /dev/rdisk0,  it needs to be the raw device
321
322
323
   BSD/OS  /dev/sr0c (if not mounted) or /dev/rsr0c ('c' any letter will do)
   returns a string allocated with strdup. It should be freed when no longer
   used. */
324
325
static char *bsd_block2char( const char *path )
{
326
327
328
#if defined(__FreeBSD__) || defined(__DragonFly__)
  return (char *) strdup( path );
#else
329
  char *new_path;
330

331
  /* If it doesn't start with "/dev/" or does start with "/dev/r" exit */
332
  if( strncmp( path, "/dev/",  5 ) || !strncmp( path, "/dev/r", 6 ) )
333
    return (char *) strdup( path );
334

335
336
  /* Replace "/dev/" with "/dev/r" */
  new_path = malloc( strlen(path) + 2 );
337
  if(!new_path) return NULL;
338
339
  strcpy( new_path, "/dev/r" );
  strcat( new_path, path + strlen( "/dev/" ) );
340

341
  return new_path;
342
#endif /* __FreeBSD__ || __DragonFly__ */
343
344
345
}
#endif

346

347
348
dvd_reader_t *DVDOpen( const char *ppath )
{
349
  struct stat fileinfo;
350
  int ret, have_css, retval, cdir = -1;
351
352
  dvd_reader_t *ret_val = NULL;
  char *dev_name = NULL;
353
  char *path = NULL, *new_path = NULL, *path_copy = NULL;
354

355
#if defined(_WIN32) || defined(__OS2__)
356
      int len;
357
358
#endif

359
  if( ppath == NULL )
360
    goto DVDOpen_error;
361

362
363
      path = strdup(ppath);
  if( path == NULL )
364
    goto DVDOpen_error;
Erik Hovland's avatar
Erik Hovland committed
365

366
367
  /* Try to open libdvdcss or fall back to standard functions */
  have_css = dvdinput_setup();
368

369
#if defined(_WIN32) || defined(__OS2__)
370
371
372
373
374
375
376
377
  /* Strip off the trailing \ if it is not a drive */
  len = strlen(path);
  if ((len > 1) &&
      (path[len - 1] == '\\')  &&
      (path[len - 2] != ':'))
  {
    path[len-1] = '\0';
  }
378
#endif
Erik Hovland's avatar
Erik Hovland committed
379

380
  ret = stat( path, &fileinfo );
381

382
  if( ret < 0 ) {
Erik Hovland's avatar
Erik Hovland committed
383

384
385
386
387
388
    /* maybe "host:port" url? try opening it with acCeSS library */
    if( strchr(path,':') ) {
                    ret_val = DVDOpenImageFile( path, have_css );
                    free(path);
            return ret_val;
389
390
    }

391
392
393
    /* If we can't stat the file, give up */
    fprintf( stderr, "libdvdread: Can't stat %s\n", path );
    perror("");
394
    goto DVDOpen_error;
395
396
397
398
399
400
  }

  /* First check if this is a block/char device or a file*/
  if( S_ISBLK( fileinfo.st_mode ) ||
      S_ISCHR( fileinfo.st_mode ) ||
      S_ISREG( fileinfo.st_mode ) ) {
401

402
403
404
    /**
     * Block devices and regular files are assumed to be DVD-Video images.
     */
405
    dvd_reader_t *dvd = NULL;
406
#if defined(__sun)
407
    dev_name = sun_block2char( path );
408
#elif defined(SYS_BSD)
409
    dev_name = bsd_block2char( path );
410
#else
411
    dev_name = strdup( path );
412
#endif
413
414
    if(!dev_name)
        goto DVDOpen_error;
415
416
    dvd = DVDOpenImageFile( dev_name, have_css );
    free( dev_name );
417
    free(path);
418
    return dvd;
419
420
  } else if( S_ISDIR( fileinfo.st_mode ) ) {
    dvd_reader_t *auth_drive = 0;
421
#if defined(SYS_BSD)
422
    struct fstab* fe;
423
#elif defined(__sun) || defined(__linux__)
424
    FILE *mntfile;
425
426
#endif

427
    /* XXX: We should scream real loud here. */
428
429
    if( !(path_copy = strdup( path ) ) )
      goto DVDOpen_error;
430
431
432
433

#ifndef WIN32 /* don't have fchdir, and getcwd( NULL, ... ) is strange */
              /* Also WIN32 does not have symlinks, so we don't need this bit of code. */

Erik Hovland's avatar
Erik Hovland committed
434
    /* Resolve any symlinks and get the absolute dir name. */
435
    {
436
437
438
439
      if( ( cdir  = open( ".", O_RDONLY ) ) >= 0 ) {
        if( chdir( path_copy ) == -1 ) {
          goto DVDOpen_error;
        }
440
441
        new_path = malloc(PATH_MAX+1);
        if(!new_path) {
442
443
444
445
          goto DVDOpen_error;
        }
        if( getcwd( new_path, PATH_MAX ) == NULL ) {
          goto DVDOpen_error;
446
        }
447
        retval = fchdir( cdir );
448
        close( cdir );
449
450
451
452
453
454
        cdir = -1;
        if( retval == -1 ) {
          goto DVDOpen_error;
        }
        path_copy = new_path;
        new_path = NULL;
455
456
      }
    }
Erik Hovland's avatar
Erik Hovland committed
457
#endif
458

459
460
    /**
     * If we're being asked to open a directory, check if that directory
Erik Hovland's avatar
Erik Hovland committed
461
     * is the mount point for a DVD-ROM which we can use instead.
462
     */
463

464
465
466
467
468
469
    if( strlen( path_copy ) > 1 ) {
      if( path_copy[ strlen( path_copy ) - 1 ] == '/' ) {
        path_copy[ strlen( path_copy ) - 1 ] = '\0';
      }
    }

470
#if defined(_WIN32) || defined(__OS2__)
471
472
473
474
475
476
    if(strlen(path_copy) > TITLES_MAX) {
      if(!strcasecmp(&(path_copy[strlen( path_copy ) - TITLES_MAX]),
                       "\\video_ts"))
        path_copy[strlen(path_copy) - (TITLES_MAX-1)] = '\0';
    }
#endif
477
478
479
480
481
482
483
484
485
486
487
    if( strlen( path_copy ) > TITLES_MAX ) {
      if( !strcasecmp( &(path_copy[ strlen( path_copy ) - TITLES_MAX ]),
                       "/video_ts" ) ) {
        path_copy[ strlen( path_copy ) - TITLES_MAX ] = '\0';
      }
    }

    if(path_copy[0] == '\0') {
      path_copy[0] = '/';
      path_copy[1] = '\0';
    }
488

489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
#if defined(__APPLE__)
    struct statfs s[128];
    int r = getfsstat(NULL, 0, MNT_NOWAIT);
    if (r > 0) {
        if (r > 128)
            r = 128;
        r = getfsstat(s, r * sizeof(s[0]), MNT_NOWAIT);
        int i;
        for (i=0; i<r; i++) {
            if (!strcmp(path_copy, s[i].f_mntonname)) {
                dev_name = bsd_block2char(s[i].f_mntfromname);
                fprintf( stderr,
                        "libdvdread: Attempting to use device %s"
                        " mounted on %s for CSS authentication\n",
                        dev_name,
                        s[i].f_mntonname);
                auth_drive = DVDOpenImageFile( dev_name, have_css );
                break;
            }
        }
    }
#elif defined(SYS_BSD)
511
512
513
514
515
516
517
518
519
    if( ( fe = getfsfile( path_copy ) ) ) {
      dev_name = bsd_block2char( fe->fs_spec );
      fprintf( stderr,
               "libdvdread: Attempting to use device %s"
               " mounted on %s for CSS authentication\n",
               dev_name,
               fe->fs_file );
      auth_drive = DVDOpenImageFile( dev_name, have_css );
    }
520
#elif defined(__sun)
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
    mntfile = fopen( MNTTAB, "r" );
    if( mntfile ) {
      struct mnttab mp;
      int res;

      while( ( res = getmntent( mntfile, &mp ) ) != -1 ) {
        if( res == 0 && !strcmp( mp.mnt_mountp, path_copy ) ) {
          dev_name = sun_block2char( mp.mnt_special );
          fprintf( stderr,
                   "libdvdread: Attempting to use device %s"
                   " mounted on %s for CSS authentication\n",
                   dev_name,
                   mp.mnt_mountp );
          auth_drive = DVDOpenImageFile( dev_name, have_css );
          break;
536
        }
537
538
539
      }
      fclose( mntfile );
    }
540
#elif defined(__linux__)
541
    mntfile = fopen( _PATH_MOUNTED, "r" );
542
543
544
545
546
547
548
549
550
551
552
553
554
    if( mntfile ) {
      struct mntent *me;

      while( ( me = getmntent( mntfile ) ) ) {
        if( !strcmp( me->mnt_dir, path_copy ) ) {
          fprintf( stderr,
                   "libdvdread: Attempting to use device %s"
                   " mounted on %s for CSS authentication\n",
                   me->mnt_fsname,
                   me->mnt_dir );
          auth_drive = DVDOpenImageFile( me->mnt_fsname, have_css );
          dev_name = strdup(me->mnt_fsname);
          break;
555
        }
556
557
558
      }
      fclose( mntfile );
    }
559
#elif defined(_WIN32) || defined(__OS2__)
560
561
#ifdef __OS2__
    /* Use DVDOpenImageFile() only if it is a drive */
562
    if(isalpha(path[0]) && path[1] == ':' &&
563
564
565
        ( !path[2] ||
          ((path[2] == '\\' || path[2] == '/') && !path[3])))
#endif
566
567
568
    auth_drive = DVDOpenImageFile( path, have_css );
#endif

569
#if !defined(_WIN32) && !defined(__OS2__)
570
571
572
573
574
575
    if( !dev_name ) {
      fprintf( stderr, "libdvdread: Couldn't find device name.\n" );
    } else if( !auth_drive ) {
      fprintf( stderr, "libdvdread: Device %s inaccessible, "
               "CSS authentication not available.\n", dev_name );
    }
576
#else
577
578
    if( !auth_drive ) {
        fprintf( stderr, "libdvdread: Device %s inaccessible, "
579
                 "CSS authentication not available.\n", path );
580
    }
581
582
#endif

583
    free( dev_name );
584
    dev_name = NULL;
585
    free( path_copy );
586
    path_copy = NULL;
587

588
589
590
591
592
593
    /**
     * If we've opened a drive, just use that.
     */
    if( auth_drive ) {
      free(path);
      return auth_drive;
594
    }
595
596
597
598
599
600
601
    /**
     * Otherwise, we now try to open the directory tree instead.
     */
    ret_val = DVDOpenPath( path );
      free( path );
      return ret_val;
  }
602

603
DVDOpen_error:
604
605
  /* If it's none of the above, screw it. */
  fprintf( stderr, "libdvdread: Could not open %s\n", path );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
606
607
  free( path );
  free( path_copy );
608
609
  if ( cdir >= 0 )
    close( cdir );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
610
  free( new_path );
611
  return NULL;
612
613
614
615
}

void DVDClose( dvd_reader_t *dvd )
{
616
617
618
619
620
621
  if( dvd ) {
    if( dvd->dev ) dvdinput_close( dvd->dev );
    if( dvd->path_root ) free( dvd->path_root );
    if( dvd->udfcache ) FreeUDFCache( dvd->udfcache );
    free( dvd );
  }
622
623
624
625
626
627
628
}

/**
 * Open an unencrypted file on a DVD image file.
 */
static dvd_file_t *DVDOpenFileUDF( dvd_reader_t *dvd, char *filename )
{
629
630
  uint32_t start, len;
  dvd_file_t *dvd_file;
631

632
633
  start = UDFFindFile( dvd, filename, &len );
  if( !start ) {
634
    fprintf( stderr, "libdvdread:DVDOpenFileUDF:UDFFindFile %s failed\n", filename );
635
636
    return NULL;
  }
637

638
  dvd_file = malloc( sizeof( dvd_file_t ) );
639
  if( !dvd_file ) {
640
    fprintf( stderr, "libdvdread:DVDOpenFileUDF:malloc failed\n" );
641
642
643
644
645
646
647
648
649
650
    return NULL;
  }
  dvd_file->dvd = dvd;
  dvd_file->lb_start = start;
  dvd_file->seek_pos = 0;
  memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
  memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
  dvd_file->filesize = len / DVD_VIDEO_LB_LEN;

  return dvd_file;
651
652
653
654
655
656
657
658
}

/**
 * Searches for <file> in directory <path>, ignoring case.
 * Returns 0 and full filename in <filename>.
 *     or -1 on file not found.
 *     or -2 on path not found.
 */
Erik Hovland's avatar
Erik Hovland committed
659
static int findDirFile( const char *path, const char *file, char *filename )
660
{
661
662
663
664
665
666
667
668
669
670
671
672
673
  DIR *dir;
  struct dirent *ent;

  dir = opendir( path );
  if( !dir ) return -2;

  while( ( ent = readdir( dir ) ) != NULL ) {
    if( !strcasecmp( ent->d_name, file ) ) {
      sprintf( filename, "%s%s%s", path,
               ( ( path[ strlen( path ) - 1 ] == '/' ) ? "" : "/" ),
               ent->d_name );
      closedir(dir);
      return 0;
674
    }
675
676
677
  }
  closedir(dir);
  return -1;
678
679
680
681
}

static int findDVDFile( dvd_reader_t *dvd, const char *file, char *filename )
{
682
683
684
685
686
687
688
689
690
691
  char video_path[ PATH_MAX + 1 ];
  const char *nodirfile;
  int ret;

  /* Strip off the directory for our search */
  if( !strncasecmp( "/VIDEO_TS/", file, 10 ) ) {
    nodirfile = &(file[ 10 ]);
  } else {
    nodirfile = file;
  }
692

693
694
695
696
697
  ret = findDirFile( dvd->path_root, nodirfile, filename );
  if( ret < 0 ) {
    /* Try also with adding the path, just in case. */
    sprintf( video_path, "%s/VIDEO_TS/", dvd->path_root );
    ret = findDirFile( video_path, nodirfile, filename );
698
    if( ret < 0 ) {
699
700
701
702
703
704
      /* Try with the path, but in lower case. */
      sprintf( video_path, "%s/video_ts/", dvd->path_root );
      ret = findDirFile( video_path, nodirfile, filename );
      if( ret < 0 ) {
        return 0;
      }
705
    }
706
  }
707

708
  return 1;
709
710
711
712
713
714
715
}

/**
 * Open an unencrypted file from a DVD directory tree.
 */
static dvd_file_t *DVDOpenFilePath( dvd_reader_t *dvd, char *filename )
{
716
717
718
719
720
721
722
  char full_path[ PATH_MAX + 1 ];
  dvd_file_t *dvd_file;
  struct stat fileinfo;
  dvd_input_t dev;

  /* Get the full path of the file. */
  if( !findDVDFile( dvd, filename, full_path ) ) {
723
    fprintf( stderr, "libdvdread:DVDOpenFilePath:findDVDFile %s failed\n", filename );
724
725
    return NULL;
  }
726

727
728
  dev = dvdinput_open( full_path );
  if( !dev ) {
729
    fprintf( stderr, "libdvdread:DVDOpenFilePath:dvdinput_open %s failed\n", full_path );
730
731
    return NULL;
  }
732

733
  dvd_file = malloc( sizeof( dvd_file_t ) );
734
  if( !dvd_file ) {
735
    fprintf( stderr, "libdvdread:DVDOpenFilePath:dvd_file malloc failed\n" );
736
737
738
739
740
741
742
743
744
745
746
747
748
    dvdinput_close(dev);
    return NULL;
  }
  dvd_file->dvd = dvd;
  dvd_file->lb_start = 0;
  dvd_file->seek_pos = 0;
  memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
  memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
  dvd_file->filesize = 0;

  if( stat( full_path, &fileinfo ) < 0 ) {
    fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
    free( dvd_file );
749
    dvdinput_close( dev );
750
751
752
753
754
    return NULL;
  }
  dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
  dvd_file->title_devs[ 0 ] = dev;
  dvd_file->filesize = dvd_file->title_sizes[ 0 ];
755

756
757
  return dvd_file;
}
758

759
760
761
762
763
764
765
766
767
768
769
770
771
772
static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *dvd, int title, int menu )
{
  char filename[ MAX_UDF_FILE_NAME_LEN ];
  uint32_t start, len;
  dvd_file_t *dvd_file;

  if( title == 0 ) {
    sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
  } else {
    sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
  }
  start = UDFFindFile( dvd, filename, &len );
  if( start == 0 ) return NULL;

773
  dvd_file = malloc( sizeof( dvd_file_t ) );
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
  if( !dvd_file ) return NULL;
  dvd_file->dvd = dvd;
  /*Hack*/ dvd_file->css_title = title << 1 | menu;
  dvd_file->lb_start = start;
  dvd_file->seek_pos = 0;
  memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
  memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
  dvd_file->filesize = len / DVD_VIDEO_LB_LEN;

  /* Calculate the complete file size for every file in the VOBS */
  if( !menu ) {
    int cur;

    for( cur = 2; cur < 10; cur++ ) {
      sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur );
      if( !UDFFindFile( dvd, filename, &len ) ) break;
      dvd_file->filesize += len / DVD_VIDEO_LB_LEN;
791
    }
792
793
794
795
796
797
798
799
800
801
802
803
  }

  if( dvd->css_state == 1 /* Need key init */ ) {
    initAllCSSKeys( dvd );
    dvd->css_state = 2;
  }
  /*
  if( dvdinput_title( dvd_file->dvd->dev, (int)start ) < 0 ) {
      fprintf( stderr, "libdvdread: Error cracking CSS key for %s\n",
               filename );
  }
  */
804

805
  return dvd_file;
806
807
}

808
static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *dvd, int title, int menu )
809
{
810
811
812
813
814
815
  char filename[ MAX_UDF_FILE_NAME_LEN ];
  char full_path[ PATH_MAX + 1 ];
  struct stat fileinfo;
  dvd_file_t *dvd_file;
  int i;

816
  dvd_file = malloc( sizeof( dvd_file_t ) );
817
818
819
820
821
822
823
824
825
826
827
  if( !dvd_file ) return NULL;
  dvd_file->dvd = dvd;
  /*Hack*/ dvd_file->css_title = title << 1 | menu;
  dvd_file->lb_start = 0;
  dvd_file->seek_pos = 0;
  memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
  memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
  dvd_file->filesize = 0;

  if( menu ) {
    dvd_input_t dev;
828
829

    if( title == 0 ) {
830
      sprintf( filename, "VIDEO_TS.VOB" );
831
    } else {
832
      sprintf( filename, "VTS_%02i_0.VOB", title );
833
    }
834
835
836
    if( !findDVDFile( dvd, filename, full_path ) ) {
      free( dvd_file );
      return NULL;
837
    }
Erik Hovland's avatar
Erik Hovland committed
838

839
840
841
842
    dev = dvdinput_open( full_path );
    if( dev == NULL ) {
      free( dvd_file );
      return NULL;
843
    }
Erik Hovland's avatar
Erik Hovland committed
844

845
846
847
848
849
850
851
852
853
854
    if( stat( full_path, &fileinfo ) < 0 ) {
      fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
      dvdinput_close(dev);
      free( dvd_file );
      return NULL;
    }
    dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
    dvd_file->title_devs[ 0 ] = dev;
    dvdinput_title( dvd_file->title_devs[0], 0);
    dvd_file->filesize = dvd_file->title_sizes[ 0 ];
855

856
857
  } else {
    for( i = 0; i < TITLES_MAX; ++i ) {
858

859
860
861
862
      sprintf( filename, "VTS_%02i_%i.VOB", title, i + 1 );
      if( !findDVDFile( dvd, filename, full_path ) ) {
        break;
      }
863

864
865
866
867
      if( stat( full_path, &fileinfo ) < 0 ) {
        fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
        break;
      }
868

869
870
871
872
873
874
875
876
      dvd_file->title_sizes[ i ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
      dvd_file->title_devs[ i ] = dvdinput_open( full_path );
      dvdinput_title( dvd_file->title_devs[ i ], 0 );
      dvd_file->filesize += dvd_file->title_sizes[ i ];
    }
    if( !dvd_file->title_devs[ 0 ] ) {
      free( dvd_file );
      return NULL;
877
    }
878
  }
879

880
  return dvd_file;
881
882
}

Erik Hovland's avatar
Erik Hovland committed
883
dvd_file_t *DVDOpenFile( dvd_reader_t *dvd, int titlenum,
884
                         dvd_read_domain_t domain )
885
{
886
  char filename[ MAX_UDF_FILE_NAME_LEN ];
Erik Hovland's avatar
Erik Hovland committed
887

888
889
890
  /* Check arguments. */
  if( dvd == NULL || titlenum < 0 )
    return NULL;
891

892
893
894
895
896
897
  switch( domain ) {
  case DVD_READ_INFO_FILE:
    if( titlenum == 0 ) {
      sprintf( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
    } else {
      sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );
898
    }
899
900
901
902
903
904
905
906
907
    break;
  case DVD_READ_INFO_BACKUP_FILE:
    if( titlenum == 0 ) {
      sprintf( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
    } else {
      sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );
    }
    break;
  case DVD_READ_MENU_VOBS:
908
    if( dvd->isImageFile ) {
909
      return DVDOpenVOBUDF( dvd, titlenum, 1 );
910
    } else {
911
      return DVDOpenVOBPath( dvd, titlenum, 1 );
912
    }
913
914
    break;
  case DVD_READ_TITLE_VOBS:
915
    if( titlenum == 0 ) return NULL;
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
    if( dvd->isImageFile ) {
      return DVDOpenVOBUDF( dvd, titlenum, 0 );
    } else {
      return DVDOpenVOBPath( dvd, titlenum, 0 );
    }
    break;
  default:
    fprintf( stderr, "libdvdread: Invalid domain for file open.\n" );
    return NULL;
  }

  if( dvd->isImageFile ) {
    return DVDOpenFileUDF( dvd, filename );
  } else {
    return DVDOpenFilePath( dvd, filename );
  }
932
933
934
935
}

void DVDCloseFile( dvd_file_t *dvd_file )
{
936
  int i;
937

938
  if( dvd_file && dvd_file->dvd ) {
Erik Hovland's avatar
Erik Hovland committed
939
    if( !dvd_file->dvd->isImageFile ) {
940
941
942
943
944
      for( i = 0; i < TITLES_MAX; ++i ) {
        if( dvd_file->title_devs[ i ] ) {
          dvdinput_close( dvd_file->title_devs[i] );
        }
      }
945
    }
946
947

    free( dvd_file );
948
    dvd_file = NULL;
949
  }
950
951
}

952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
static int DVDFileStatVOBUDF( dvd_reader_t *dvd, int title,
                              int menu, dvd_stat_t *statbuf )
{
  char filename[ MAX_UDF_FILE_NAME_LEN ];
  uint32_t size;
  off_t tot_size;
  off_t parts_size[ 9 ];
  int nr_parts = 0;
  int n;

  if( title == 0 )
    sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
  else
    sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );

  if( !UDFFindFile( dvd, filename, &size ) )
    return -1;

  tot_size = size;
  nr_parts = 1;
  parts_size[ 0 ] = size;

  if( !menu ) {
    int cur;

    for( cur = 2; cur < 10; cur++ ) {
      sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur );
      if( !UDFFindFile( dvd, filename, &size ) )
        break;

      parts_size[ nr_parts ] = size;
      tot_size += size;
      nr_parts++;
    }
  }

  statbuf->size = tot_size;
  statbuf->nr_parts = nr_parts;
  for( n = 0; n < nr_parts; n++ )
    statbuf->parts_size[ n ] = parts_size[ n ];

  return 0;
}


static int DVDFileStatVOBPath( dvd_reader_t *dvd, int title,
                               int menu, dvd_stat_t *statbuf )
{
  char filename[ MAX_UDF_FILE_NAME_LEN ];
  char full_path[ PATH_MAX + 1 ];
  struct stat fileinfo;
  off_t tot_size;
  off_t parts_size[ 9 ];
  int nr_parts = 0;
  int n;

  if( title == 0 )
    sprintf( filename, "VIDEO_TS.VOB" );
  else
    sprintf( filename, "VTS_%02d_%d.VOB", title, menu ? 0 : 1 );

  if( !findDVDFile( dvd, filename, full_path ) )
    return -1;

  if( stat( full_path, &fileinfo ) < 0 ) {
    fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
    return -1;
  }

  tot_size = fileinfo.st_size;
  nr_parts = 1;
  parts_size[ 0 ] = fileinfo.st_size;

  if( !menu ) {
    int cur;
    for( cur = 2; cur < 10; cur++ ) {
      sprintf( filename, "VTS_%02d_%d.VOB", title, cur );
      if( !findDVDFile( dvd, filename, full_path ) )
        break;

      if( stat( full_path, &fileinfo ) < 0 ) {
        fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
        break;
      }

      parts_size[ nr_parts ] = fileinfo.st_size;
      tot_size += parts_size[ nr_parts ];
      nr_parts++;
    }
  }

  statbuf->size = tot_size;
  statbuf->nr_parts = nr_parts;
  for( n = 0; n < nr_parts; n++ )
    statbuf->parts_size[ n ] = parts_size[ n ];

  return 0;
}


int DVDFileStat( dvd_reader_t *dvd, int titlenum,
                 dvd_read_domain_t domain, dvd_stat_t *statbuf )
{
  char filename[ MAX_UDF_FILE_NAME_LEN ];
  char full_path[ PATH_MAX + 1 ];
  struct stat fileinfo;
  uint32_t size;

  /* Check arguments. */
  if( dvd == NULL || titlenum < 0 ) {
    errno = EINVAL;
    return -1;
  }

  switch( domain ) {
  case DVD_READ_INFO_FILE:
    if( titlenum == 0 )
      sprintf( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
    else
      sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );

    break;
  case DVD_READ_INFO_BACKUP_FILE:
    if( titlenum == 0 )
      sprintf( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
    else
      sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );

    break;
  case DVD_READ_MENU_VOBS:
    if( dvd->isImageFile )
      return DVDFileStatVOBUDF( dvd, titlenum, 1, statbuf );
    else
      return DVDFileStatVOBPath( dvd, titlenum, 1, statbuf );

    break;
  case DVD_READ_TITLE_VOBS:
    if( titlenum == 0 )
      return -1;

    if( dvd->isImageFile )
      return DVDFileStatVOBUDF( dvd, titlenum, 0, statbuf );
    else
      return DVDFileStatVOBPath( dvd, titlenum, 0, statbuf );

    break;
  default:
    fprintf( stderr, "libdvdread: Invalid domain for file stat.\n" );
    errno = EINVAL;
    return -1;
  }

  if( dvd->isImageFile ) {
    if( UDFFindFile( dvd, filename, &size ) ) {
      statbuf->size = size;
      statbuf->nr_parts = 1;
      statbuf->parts_size[ 0 ] = size;
      return 0;
    }
  } else {
    if( findDVDFile( dvd, filename, full_path ) ) {
      if( stat( full_path, &fileinfo ) < 0 )
        fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
      else {
        statbuf->size = fileinfo.st_size;
        statbuf->nr_parts = 1;
        statbuf->parts_size[ 0 ] = statbuf->size;
        return 0;
      }
    }
  }
  return -1;
}

1126
/* Internal, but used from dvd_udf.c */
1127
int InternalUDFReadBlocksRaw( const dvd_reader_t *device, uint32_t lb_number,
1128
1129
                      size_t block_count, unsigned char *data,
                      int encrypted )
1130
{
1131
  int ret;
1132

1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
  if( !device->dev ) {
    fprintf( stderr, "libdvdread: Fatal error in block read.\n" );
    return 0;
  }

  ret = dvdinput_seek( device->dev, (int) lb_number );
  if( ret != (int) lb_number ) {
    fprintf( stderr, "libdvdread: Can't seek to block %u\n", lb_number );
    return 0;
  }
1143

1144
1145
1146
  ret = dvdinput_read( device->dev, (char *) data,
                       (int) block_count, encrypted );
  return ret;
1147
1148
1149
1150
1151
1152
1153
1154
}

/* This is using a single input and starting from 'dvd_file->lb_start' offset.
 *
 * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
 * into the buffer located at 'data' and if 'encrypted' is set
 * descramble the data if it's encrypted.  Returning either an
 * negative error or the number of blocks read. */
1155
static int DVDReadBlocksUDF( const dvd_file_t *dvd_file, uint32_t offset,
1156
1157
                             size_t block_count, unsigned char *data,
                             int encrypted )
1158
{
1159
  return InternalUDFReadBlocksRaw( dvd_file->dvd, dvd_file->lb_start + offset,
1160
                           block_count, data, encrypted );
1161
1162
1163
1164
1165
1166
1167
1168
}

/* This is using possibly several inputs and starting from an offset of '0'.
 *
 * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
 * into the buffer located at 'data' and if 'encrypted' is set
 * descramble the data if it's encrypted.  Returning either an
 * negative error or the number of blocks read. */
1169
static int DVDReadBlocksPath( const dvd_file_t *dvd_file, unsigned int offset,
1170
1171
                              size_t block_count, unsigned char *data,
                              int encrypted )
1172
{
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
  int i;
  int ret, ret2, off;

  ret = 0;
  ret2 = 0;
  for( i = 0; i < TITLES_MAX; ++i ) {
    if( !dvd_file->title_sizes[ i ] ) return 0; /* Past end of file */

    if( offset < dvd_file->title_sizes[ i ] ) {
      if( ( offset + block_count ) <= dvd_file->title_sizes[ i ] ) {
        off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );
        if( off < 0 || off != (int)offset ) {
          fprintf( stderr, "libdvdread: Can't seek to block %d\n",
                   offset );
          return off < 0 ? off : 0;
        }
        ret = dvdinput_read( dvd_file->title_devs[ i ], data,
                             (int)block_count, encrypted );
        break;
      } else {
        size_t part1_size = dvd_file->title_sizes[ i ] - offset;
        /* FIXME: Really needs to be a while loop.
         * (This is only true if you try and read >1GB at a time) */

        /* Read part 1 */
        off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );
        if( off < 0 || off != (int)offset ) {
          fprintf( stderr, "libdvdread: Can't seek to block %d\n",
                   offset );
          return off < 0 ? off : 0;
        }
        ret = dvdinput_read( dvd_file->title_devs[ i ], data,
                             (int)part1_size, encrypted );
        if( ret < 0 ) return ret;
        /* FIXME: This is wrong if i is the last file in the set.
         * also error from this read will not show in ret. */

        /* Does the next part exist? If not then return now. */
        if( i + 1 >= TITLES_MAX || !dvd_file->title_devs[ i + 1 ] )
          return ret;

        /* Read part 2 */
        off = dvdinput_seek( dvd_file->title_devs[ i + 1 ], 0 );
        if( off < 0 || off != 0 ) {
          fprintf( stderr, "libdvdread: Can't seek to block %d\n",
                   0 );
          return off < 0 ? off : 0;
1220
        }
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
        ret2 = dvdinput_read( dvd_file->title_devs[ i + 1 ],
                              data + ( part1_size
                                       * (int64_t)DVD_VIDEO_LB_LEN ),
                              (int)(block_count - part1_size),
                              encrypted );
        if( ret2 < 0 ) return ret2;
        break;
      }
    } else {
      offset -= dvd_file->title_sizes[ i ];
1231
    }
1232
  }
1233

1234
  return ret + ret2;
1235
1236
1237
}

/* This is broken reading more than 2Gb at a time is ssize_t is 32-bit. */
Erik Hovland's avatar
Erik Hovland committed
1238
ssize_t DVDReadBlocks( dvd_file_t *dvd_file, int offset,
1239
                       size_t block_count, unsigned char *data )
1240
{
1241
  int ret;
Erik Hovland's avatar
Erik Hovland committed
1242

1243
1244
1245
  /* Check arguments. */
  if( dvd_file == NULL || offset < 0 || data == NULL )
    return -1;
Erik Hovland's avatar
Erik Hovland committed
1246

1247
1248
1249
  /* Hack, and it will still fail for multiple opens in a threaded app ! */
  if( dvd_file->dvd->css_title != dvd_file->css_title ) {
    dvd_file->dvd->css_title = dvd_file->css_title;
1250
    if( dvd_file->dvd->isImageFile ) {
1251
      dvdinput_title( dvd_file->dvd->dev, (int)dvd_file->lb_start );
1252
    }
1253
1254
1255
1256
1257
    /* Here each vobu has it's own dvdcss handle, so no need to update
    else {
      dvdinput_title( dvd_file->title_devs[ 0 ], (int)dvd_file->lb_start );
    }*/
  }