dvd_reader.c 43.6 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
 */

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
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;
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;
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;
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 124

  /* Cache of the dvd_file. If not NULL, the cache corresponds to the whole
   * dvd_file. Used only for IFO and BUP. */
  unsigned char *cache;
125 126
};

127
int InternalUDFReadBlocksRaw( const dvd_reader_t *device, uint32_t lb_number,
128 129
                      size_t block_count, unsigned char *data,
                      int encrypted );
130 131 132 133 134 135 136 137 138

/**
 * 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;
139

140 141 142 143 144 145 146
  if(level > 0) {
    level = 1;
  } else if(level < 0) {
    return dev->udfcache_level;
  }

  dev->udfcache_level = level;
147

148 149 150 151 152 153
  return level;
}

void *GetUDFCacheHandle(dvd_reader_t *device)
{
  struct dvd_reader_s *dev = (struct dvd_reader_s *)device;
154

155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
  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 )
{
170 171 172 173 174 175 176 177 178
  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;
179

180 181 182 183 184
  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);
185

186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
  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 );
    }
205

206
    if( title == 0 ) continue;
207

208 209 210 211
    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;
212

213 214 215 216 217
    /* 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);
218
    }
219
    gettimeofday( &t_e, NULL );
220
    fprintf( stderr, "libdvdread: Elapsed time %ld\n",
221 222 223
             (long int) t_e.tv_sec - t_s.tv_sec );
  }
  title--;
224

225 226 227 228 229 230
  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;
231 232 233 234 235
}



/**
Thomas Guillem's avatar
Thomas Guillem committed
236
 * Open a DVD image or block device file or use stream_cb functions.
237
 */
Thomas Guillem's avatar
Thomas Guillem committed
238 239 240 241
static dvd_reader_t *DVDOpenImageFile( const char *location,
                                       void *stream,
                                       dvd_reader_stream_cb *stream_cb,
                                       int have_css )
242
{
243 244
  dvd_reader_t *dvd;
  dvd_input_t dev;
245

Thomas Guillem's avatar
Thomas Guillem committed
246
  dev = dvdinput_open( location, stream, stream_cb );
247 248 249 250
  if( !dev ) {
    fprintf( stderr, "libdvdread: Can't open %s for reading\n", location );
    return NULL;
  }
251

252
  dvd = malloc( sizeof( dvd_reader_t ) );
253 254 255 256
  if( !dvd ) {
    dvdinput_close(dev);
    return NULL;
  }
257
  memset( dvd, 0, sizeof( dvd_reader_t ) );
258 259 260
  dvd->isImageFile = 1;
  dvd->dev = dev;
  dvd->path_root = NULL;
261

262 263
  dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
  dvd->udfcache = NULL;
264

265 266
  if( have_css ) {
    /* Only if DVDCSS_METHOD = title, a bit if it's disc or if
Erik Hovland's avatar
Erik Hovland committed
267
     * DVDCSS_METHOD = key but region mismatch. Unfortunately we
268
     * don't have that information. */
269

270 271 272
    dvd->css_state = 1; /* Need key init. */
  }
  dvd->css_title = 0;
273

274
  return dvd;
275 276 277 278
}

static dvd_reader_t *DVDOpenPath( const char *path_root )
{
279 280
  dvd_reader_t *dvd;

281
  dvd = malloc( sizeof( dvd_reader_t ) );
282 283 284 285 286 287
  if( !dvd ) return NULL;
  dvd->isImageFile = 0;
  dvd->dev = 0;
  dvd->path_root = strdup( path_root );
  if(!dvd->path_root) {
    free(dvd);
288
    return NULL;
289 290 291
  }
  dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
  dvd->udfcache = NULL;
292

293 294
  dvd->css_state = 0; /* Only used in the UDF path */
  dvd->css_title = 0; /* Only matters in the UDF path */
295

296
  return dvd;
297 298 299 300 301 302 303 304
}

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

307 308
  /* Must contain "/dsk/" */
  if( !strstr( path, "/dsk/" ) ) return (char *) strdup( path );
309

310 311
  /* Replace "/dsk/" with "/rdsk/" */
  new_path = malloc( strlen(path) + 2 );
312
  if(!new_path) return NULL;
313 314 315 316
  strcpy( new_path, path );
  strcpy( strstr( new_path, "/dsk/" ), "" );
  strcat( new_path, "/rdsk/" );
  strcat( new_path, strstr( path, "/dsk/" ) + strlen( "/dsk/" ) );
317

318
  return new_path;
319 320 321 322
}
#endif

#if defined(SYS_BSD)
Erik Hovland's avatar
Erik Hovland committed
323
/* FreeBSD /dev/(r)(a)cd0c (a is for atapi), recommended to _not_ use r
324
   update: FreeBSD and DragonFly no longer uses the prefix so don't add it.
325 326 327
   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
328 329 330
   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. */
331 332
static char *bsd_block2char( const char *path )
{
333 334 335
#if defined(__FreeBSD__) || defined(__DragonFly__)
  return (char *) strdup( path );
#else
336
  char *new_path;
337

338
  /* If it doesn't start with "/dev/" or does start with "/dev/r" exit */
339
  if( strncmp( path, "/dev/",  5 ) || !strncmp( path, "/dev/r", 6 ) )
340
    return (char *) strdup( path );
341

342 343
  /* Replace "/dev/" with "/dev/r" */
  new_path = malloc( strlen(path) + 2 );
344
  if(!new_path) return NULL;
345 346
  strcpy( new_path, "/dev/r" );
  strcat( new_path, path + strlen( "/dev/" ) );
347

348
  return new_path;
349
#endif /* __FreeBSD__ || __DragonFly__ */
350 351 352
}
#endif

Thomas Guillem's avatar
Thomas Guillem committed
353 354 355
static dvd_reader_t *DVDOpenCommon( const char *ppath,
                                    void *stream,
                                    dvd_reader_stream_cb *stream_cb )
356
{
357
  struct stat fileinfo;
358
  int ret, have_css, retval, cdir = -1;
359 360
  dvd_reader_t *ret_val = NULL;
  char *dev_name = NULL;
361
  char *path = NULL, *new_path = NULL, *path_copy = NULL;
362

363
#if defined(_WIN32) || defined(__OS2__)
364
      int len;
365 366
#endif

Thomas Guillem's avatar
Thomas Guillem committed
367 368 369 370 371 372 373
  /* Try to open DVD using stream_cb functions */
  if( stream != NULL && stream_cb != NULL )
  {
    have_css = dvdinput_setup();
    return DVDOpenImageFile( NULL, stream, stream_cb, have_css );
  }

374
  if( ppath == NULL )
375
    goto DVDOpen_error;
376

377 378
      path = strdup(ppath);
  if( path == NULL )
379
    goto DVDOpen_error;
380

381 382
  /* Try to open libdvdcss or fall back to standard functions */
  have_css = dvdinput_setup();
383

384
#if defined(_WIN32) || defined(__OS2__)
385 386 387 388 389 390 391 392
  /* 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';
  }
393
#endif
394

395
  ret = stat( path, &fileinfo );
396

397
  if( ret < 0 ) {
398

399 400
    /* maybe "host:port" url? try opening it with acCeSS library */
    if( strchr(path,':') ) {
Thomas Guillem's avatar
Thomas Guillem committed
401
                    ret_val = DVDOpenImageFile( path, NULL, NULL, have_css );
402 403
                    free(path);
            return ret_val;
404 405
    }

406 407 408
    /* If we can't stat the file, give up */
    fprintf( stderr, "libdvdread: Can't stat %s\n", path );
    perror("");
409
    goto DVDOpen_error;
410 411 412 413 414 415
  }

  /* 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 ) ) {
416

417 418 419
    /**
     * Block devices and regular files are assumed to be DVD-Video images.
     */
420
    dvd_reader_t *dvd = NULL;
421
#if defined(__sun)
422
    dev_name = sun_block2char( path );
423
#elif defined(SYS_BSD)
424
    dev_name = bsd_block2char( path );
425
#else
426
    dev_name = strdup( path );
427
#endif
428 429
    if(!dev_name)
        goto DVDOpen_error;
Thomas Guillem's avatar
Thomas Guillem committed
430
    dvd = DVDOpenImageFile( dev_name, NULL, NULL, have_css );
431
    free( dev_name );
432
    free(path);
433
    return dvd;
434 435
  } else if( S_ISDIR( fileinfo.st_mode ) ) {
    dvd_reader_t *auth_drive = 0;
436
#if defined(SYS_BSD)
437
    struct fstab* fe;
438
#elif defined(__sun) || defined(__linux__)
439
    FILE *mntfile;
440 441
#endif

442
    /* XXX: We should scream real loud here. */
443 444
    if( !(path_copy = strdup( path ) ) )
      goto DVDOpen_error;
445 446 447 448

#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
449
    /* Resolve any symlinks and get the absolute dir name. */
450
    {
451 452 453 454
      if( ( cdir  = open( ".", O_RDONLY ) ) >= 0 ) {
        if( chdir( path_copy ) == -1 ) {
          goto DVDOpen_error;
        }
455 456
        new_path = malloc(PATH_MAX+1);
        if(!new_path) {
457 458 459 460
          goto DVDOpen_error;
        }
        if( getcwd( new_path, PATH_MAX ) == NULL ) {
          goto DVDOpen_error;
461
        }
462
        retval = fchdir( cdir );
463
        close( cdir );
464 465 466 467
        cdir = -1;
        if( retval == -1 ) {
          goto DVDOpen_error;
        }
468
        free(path_copy);
469 470
        path_copy = new_path;
        new_path = NULL;
471 472
      }
    }
473
#endif
474

475 476
    /**
     * If we're being asked to open a directory, check if that directory
Erik Hovland's avatar
Erik Hovland committed
477
     * is the mount point for a DVD-ROM which we can use instead.
478
     */
479

480 481 482 483 484 485
    if( strlen( path_copy ) > 1 ) {
      if( path_copy[ strlen( path_copy ) - 1 ] == '/' ) {
        path_copy[ strlen( path_copy ) - 1 ] = '\0';
      }
    }

486
#if defined(_WIN32) || defined(__OS2__)
487 488 489 490 491 492
    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
493 494 495 496 497 498 499 500 501 502 503
    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';
    }
504

505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520
#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);
Thomas Guillem's avatar
Thomas Guillem committed
521
                auth_drive = DVDOpenImageFile( dev_name, NULL, NULL, have_css );
522 523 524 525 526
                break;
            }
        }
    }
#elif defined(SYS_BSD)
527 528 529 530 531 532 533
    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 );
Thomas Guillem's avatar
Thomas Guillem committed
534
      auth_drive = DVDOpenImageFile( dev_name, NULL, NULL, have_css );
535
    }
536
#elif defined(__sun)
537 538 539 540 541 542 543 544 545 546 547 548 549
    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 );
Thomas Guillem's avatar
Thomas Guillem committed
550
          auth_drive = DVDOpenImageFile( dev_name, NULL, NULL, have_css );
551
          break;
552
        }
553 554 555
      }
      fclose( mntfile );
    }
556
#elif defined(__linux__)
557
    mntfile = fopen( _PATH_MOUNTED, "r" );
558 559 560 561 562 563 564 565 566 567
    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 );
Thomas Guillem's avatar
Thomas Guillem committed
568
          auth_drive = DVDOpenImageFile( me->mnt_fsname, NULL, NULL, have_css );
569 570
          dev_name = strdup(me->mnt_fsname);
          break;
571
        }
572 573 574
      }
      fclose( mntfile );
    }
575
#elif defined(_WIN32) || defined(__OS2__)
576 577
#ifdef __OS2__
    /* Use DVDOpenImageFile() only if it is a drive */
578
    if(isalpha(path[0]) && path[1] == ':' &&
579 580 581
        ( !path[2] ||
          ((path[2] == '\\' || path[2] == '/') && !path[3])))
#endif
Thomas Guillem's avatar
Thomas Guillem committed
582
    auth_drive = DVDOpenImageFile( path, NULL, NULL, have_css );
583 584
#endif

585
#if !defined(_WIN32) && !defined(__OS2__)
586 587 588 589 590 591
    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 );
    }
592
#else
593 594
    if( !auth_drive ) {
        fprintf( stderr, "libdvdread: Device %s inaccessible, "
595
                 "CSS authentication not available.\n", path );
596
    }
597 598
#endif

599
    free( dev_name );
600
    dev_name = NULL;
601
    free( path_copy );
602
    path_copy = NULL;
603

604 605 606 607 608 609
    /**
     * If we've opened a drive, just use that.
     */
    if( auth_drive ) {
      free(path);
      return auth_drive;
610
    }
611 612 613 614 615 616 617
    /**
     * Otherwise, we now try to open the directory tree instead.
     */
    ret_val = DVDOpenPath( path );
      free( path );
      return ret_val;
  }
618

619
DVDOpen_error:
620 621
  /* If it's none of the above, screw it. */
  fprintf( stderr, "libdvdread: Could not open %s\n", path );
622 623
  free( path );
  free( path_copy );
624 625
  if ( cdir >= 0 )
    close( cdir );
626
  free( new_path );
627
  return NULL;
628 629
}

Thomas Guillem's avatar
Thomas Guillem committed
630 631 632 633 634 635 636 637 638 639 640
dvd_reader_t *DVDOpen( const char *ppath )
{
    return DVDOpenCommon( ppath, NULL, NULL );
}

dvd_reader_t *DVDOpenStream( void *stream,
                             dvd_reader_stream_cb *stream_cb )
{
    return DVDOpenCommon( NULL, stream, stream_cb );
}

641 642
void DVDClose( dvd_reader_t *dvd )
{
643 644 645 646 647 648
  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 );
  }
649 650 651 652 653
}

/**
 * Open an unencrypted file on a DVD image file.
 */
654 655
static dvd_file_t *DVDOpenFileUDF( dvd_reader_t *dvd, char *filename,
                                   int do_cache )
656
{
657 658
  uint32_t start, len;
  dvd_file_t *dvd_file;
659

660 661
  start = UDFFindFile( dvd, filename, &len );
  if( !start ) {
662
    fprintf( stderr, "libdvdread:DVDOpenFileUDF:UDFFindFile %s failed\n", filename );
663 664
    return NULL;
  }
665

666
  dvd_file = malloc( sizeof( dvd_file_t ) );
667
  if( !dvd_file ) {
668
    fprintf( stderr, "libdvdread:DVDOpenFileUDF:malloc failed\n" );
669 670 671 672 673 674 675 676
    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;
677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
  dvd_file->cache = NULL;

  /* Read the whole file in cache (unencrypted) if asked and if it doesn't
   * exceed 128KB */
  if( do_cache && len < 64 * DVD_VIDEO_LB_LEN ) {
    int ret;

    dvd_file->cache = malloc( len );
    if( !dvd_file->cache )
        return dvd_file;

    ret = InternalUDFReadBlocksRaw( dvd, dvd_file->lb_start,
                                    dvd_file->filesize, dvd_file->cache,
                                    DVDINPUT_NOFLAGS );
    if( ret != dvd_file->filesize ) {
        free( dvd_file->cache );
        dvd_file->cache = NULL;
    }
  }
696 697

  return dvd_file;
698 699 700 701 702 703 704 705
}

/**
 * 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.
 */
706
static int findDirFile( const char *path, const char *file, char *filename )
707
{
708 709 710 711 712 713 714 715 716 717 718 719 720
  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;
721
    }
722 723 724
  }
  closedir(dir);
  return -1;
725 726 727 728
}

static int findDVDFile( dvd_reader_t *dvd, const char *file, char *filename )
{
729 730 731 732 733 734 735 736 737
  const char *nodirfile;
  int ret;

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

739 740
  ret = findDirFile( dvd->path_root, nodirfile, filename );
  if( ret < 0 ) {
741 742
    char video_path[ PATH_MAX + 1 ];

743 744 745
    /* Try also with adding the path, just in case. */
    sprintf( video_path, "%s/VIDEO_TS/", dvd->path_root );
    ret = findDirFile( video_path, nodirfile, filename );
746
    if( ret < 0 ) {
747 748 749 750 751 752
      /* 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;
      }
753
    }
754
  }
755

756
  return 1;
757 758 759 760 761 762 763
}

/**
 * Open an unencrypted file from a DVD directory tree.
 */
static dvd_file_t *DVDOpenFilePath( dvd_reader_t *dvd, char *filename )
{
764 765 766 767 768 769 770
  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 ) ) {
771
    fprintf( stderr, "libdvdread:DVDOpenFilePath:findDVDFile %s failed\n", filename );
772 773
    return NULL;
  }
774

Thomas Guillem's avatar
Thomas Guillem committed
775
  dev = dvdinput_open( full_path, NULL, NULL );
776
  if( !dev ) {
777
    fprintf( stderr, "libdvdread:DVDOpenFilePath:dvdinput_open %s failed\n", full_path );
778 779
    return NULL;
  }
780

781
  dvd_file = malloc( sizeof( dvd_file_t ) );
782
  if( !dvd_file ) {
783
    fprintf( stderr, "libdvdread:DVDOpenFilePath:dvd_file malloc failed\n" );
784 785 786 787 788 789 790 791 792
    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;
793
  dvd_file->cache = NULL;
794 795 796 797

  if( stat( full_path, &fileinfo ) < 0 ) {
    fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
    free( dvd_file );
798
    dvdinput_close( dev );
799 800 801 802 803
    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 ];
804

805 806
  return dvd_file;
}
807

808 809 810 811 812 813 814 815 816 817 818 819 820 821
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;

822
  dvd_file = malloc( sizeof( dvd_file_t ) );
823 824 825 826 827 828 829 830
  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;
831
  dvd_file->cache = NULL;
832 833 834 835 836 837 838 839 840

  /* 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;
841
    }
842 843 844 845 846 847 848 849 850 851 852 853
  }

  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 );
  }
  */
854

855
  return dvd_file;
856 857
}

858
static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *dvd, int title, int menu )
859
{
860 861 862 863 864
  char filename[ MAX_UDF_FILE_NAME_LEN ];
  char full_path[ PATH_MAX + 1 ];
  struct stat fileinfo;
  dvd_file_t *dvd_file;

865
  dvd_file = malloc( sizeof( dvd_file_t ) );
866 867 868 869 870 871 872 873
  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;
874
  dvd_file->cache = NULL;
875 876 877

  if( menu ) {
    dvd_input_t dev;
878 879

    if( title == 0 ) {
880
      sprintf( filename, "VIDEO_TS.VOB" );
881
    } else {
882
      sprintf( filename, "VTS_%02i_0.VOB", title );
883
    }
884 885 886
    if( !findDVDFile( dvd, filename, full_path ) ) {
      free( dvd_file );
      return NULL;
887
    }
888

Thomas Guillem's avatar
Thomas Guillem committed
889
    dev = dvdinput_open( full_path, NULL, NULL );
890 891 892
    if( dev == NULL ) {
      free( dvd_file );
      return NULL;
893
    }
894

895 896 897 898 899 900 901 902 903 904
    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 ];
905

906
  } else {
907 908
    int i;

909
    for( i = 0; i < TITLES_MAX; ++i ) {
910

911 912 913 914
      sprintf( filename, "VTS_%02i_%i.VOB", title, i + 1 );
      if( !findDVDFile( dvd, filename, full_path ) ) {
        break;
      }
915

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

921
      dvd_file->title_sizes[ i ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
Thomas Guillem's avatar
Thomas Guillem committed
922
      dvd_file->title_devs[ i ] = dvdinput_open( full_path, NULL, NULL );
923 924 925 926 927 928
      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;
929
    }
930
  }
931

932
  return dvd_file;
933 934
}

935
dvd_file_t *DVDOpenFile( dvd_reader_t *dvd, int titlenum,
936
                         dvd_read_domain_t domain )
937
{
938
  char filename[ MAX_UDF_FILE_NAME_LEN ];
939
  int do_cache = 0;
940

941 942 943
  /* Check arguments. */
  if( dvd == NULL || titlenum < 0 )
    return NULL;
944

945 946 947 948 949 950
  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 );
951
    }
952
    do_cache = 1;
953 954 955 956 957 958 959
    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 );
    }
960
    do_cache = 1;
961 962
    break;
  case DVD_READ_MENU_VOBS:
963
    if( dvd->isImageFile ) {
964
      return DVDOpenVOBUDF( dvd, titlenum, 1 );
965
    } else {
966
      return DVDOpenVOBPath( dvd, titlenum, 1 );
967
    }
968 969
    break;
  case DVD_READ_TITLE_VOBS:
970
    if( titlenum == 0 ) return NULL;
971 972 973 974 975 976 977 978 979 980 981 982
    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 ) {
983
    return DVDOpenFileUDF( dvd, filename, do_cache );
984 985 986
  } else {
    return DVDOpenFilePath( dvd, filename );
  }
987 988 989 990
}

void DVDCloseFile( dvd_file_t *dvd_file )
{
991
  if( dvd_file && dvd_file->dvd ) {
Erik Hovland's avatar
Erik Hovland committed
992
    if( !dvd_file->dvd->isImageFile ) {
993 994
      int i;

995 996 997 998 999
      for( i = 0; i < TITLES_MAX; ++i ) {
        if( dvd_file->title_devs[ i ] ) {
          dvdinput_close( dvd_file->title_devs[i] );
        }
      }
1000
    }
1001

1002
    free( dvd_file->cache );
1003
    free( dvd_file );
1004
    dvd_file = NULL;
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 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166
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 ];
  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 {
1167 1168
    char full_path[ PATH_MAX + 1 ];

1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182
    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;
}

1183
/* Internal, but used from dvd_udf.c */
1184
int InternalUDFReadBlocksRaw( const dvd_reader_t *device, uint32_t lb_number,
1185 1186
                      size_t block_count, unsigned char *data,
                      int encrypted )
1187
{
1188
  int ret;
1189

1190 1191 1192 1193 1194 1195 1196 1197 1198 1199
  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;
  }
1200

1201 1202 1203
  ret = dvdinput_read( device->dev, (char *) data,
                       (int) block_count, encrypted );
  return ret;
1204 1205 1206 1207 1208 1209 1210 1211
}

/* 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. */
1212
static int DVDReadBlocksUDF( const dvd_file_t *dvd_file, uint32_t offset,
1213 1214
                             size_t block_count, unsigned char *data,
                             int encrypted )
1215
{
1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234
  /* If the cache is present and we don't need to decrypt, use the cache to
   * feed the data */
  if( dvd_file->cache && (encrypted & DVDINPUT_READ_DECRYPT) == 0 ) {
    /* Check if we don't exceed the cache (or file) size */
    if( block_count + offset > (size_t) dvd_file->filesize )
      return 0;

    /* Copy the cache at a specified offset into data. offset and block_count
     * must be converted into bytes */
    memcpy( data, dvd_file->cache + (off_t)offset * (off_t)DVD_VIDEO_LB_LEN,
            (off_t)block_count * (off_t)DVD_VIDEO_LB_LEN );

    /* return the amount of blocks copied */
    return block_count;
  } else {
    /* use dvdinput access */
    return InternalUDFReadBlocksRaw( dvd_file->dvd, dvd_file->lb_start + offset,
                             block_count, data, encrypted );
  }
1235 1236 1237 1238 1239 1240 1241 1242
}

/* 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. */
1243
static int DVDReadBlocksPath( const dvd_file_t *dvd_file, unsigned int offset,
1244 1245
                              size_t block_count, unsigned char *data,
                              int encrypted )
1246
{
1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258
  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 ) {
1259
          fprintf( stderr, "libdvdread: Can't seek to block %u\n",
1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273
                   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 ) {
1274
          fprintf( stderr, "libdvdread: Can't seek to block %u\n",
1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293
                   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;
1294
        }
1295 1296 1297 1298 1299 1300 1301 1302 1303 1304
        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 ];
1305
    }
1306
  }
1307

1308
  return ret + ret2;
1309 1310 1311
}

/* This is broken reading more than 2Gb at a time is ssize_t is 32-bit. */
1312
ssize_t DVDReadBlocks( dvd_file_t *dvd_file, int offset,
1313
                       size_t block_count, unsigned char *data )
1314
{
1315
  int ret;
1316

1317 1318 1319
  /* Check arguments. */
  if( dvd_file == NULL || offset < 0 || data == NULL )
    return -1;
1320

1321 1322 1323
  /* 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;
1324
    if( dvd_file->dvd->isImageFile ) {
1325
      dvdinput_title( dvd_file->dvd->dev, (int)dvd_file->lb_start );
1326
    }
1327 1328 1329 1330 1331
    /* 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 );
    }*/
  }
1332

1333 1334 1335 1336 1337 1338 1339 1340 1341
  if( dvd_file->dvd->isImageFile ) {
    ret = DVDReadBlocksUDF( dvd_file, (uint32_t)offset,
                            block_count, data, DVDINPUT_READ_DECRYPT );
  } else {
    ret = DVDReadBlocksPath( dvd_file, (unsigned int)offset,
                             block_count, data, DVDINPUT_READ_DECRYPT );
  }

  return (ssize_t)ret;
1342 1343 1344 1345
}

int32_t DVDFileSeek( dvd_file_t *dvd_file, int32_t offset )
{
1346 1347 1348
  /* Check arguments. */
  if( dvd_file == NULL || offset < 0 )
    return -1;
1349

1350 1351 1352 1353 1354
  if( offset > dvd_file->filesize * DVD_VIDEO_LB_LEN ) {
    return -1;
  }
  dvd_file->seek_pos = (uint32_t) offset;
  return offset;
1355 1356 1357 1358
}

int DVDFileSeekForce(dvd_file_t *dvd_file, int offset, int force_size)
{
1359 1360 1361
  /* Check arguments. */
  if( dvd_file == NULL || offset <= 0 )
      return -1;
1362

1363 1364 1365 1366 1367 1368
  if( dvd_file->dvd->isImageFile ) {
    if( force_size < 0 )
      force_size = (offset - 1) / DVD_VIDEO_LB_LEN + 1;
    if( dvd_file->filesize < force_size ) {
      dvd_file->filesize = force_size;
      fprintf(stderr, "libdvdread: Ignored size of file indicated in UDF.\n");
1369
    }
1370
  }
1371

1372 1373
  if( offset > dvd_file->filesize * DVD_VIDEO_LB_LEN )
    return -1;
1374

1375 1376
  dvd_file->seek_pos = (uint32_t) offset;
  return offset;
1377 1378 1379 1380
}

ssize_t DVDReadBytes( dvd_file_t *dvd_file, void *data, size_t byte_size )
{
1381 1382 1383
  unsigned char *secbuf_base, *secbuf;
  unsigned int numsec, seek_sector, seek_byte;
  int ret;
Nico Sabbi's avatar