Add UDF2.50 logic, which includes BD5ISO, and HD-DVD support.
Support non-DVD style access. (File iteration, and reading).

Diffed against svn 1153.


This patch includes:

1. Add ExtFinfo (262) support

2. Add FileType (250) re-direct support

3. DVDOpendir/DVDReaddir/DVDClosedir/DVDOpenFile for UDF access.

4. dvdread-1.9.7 stat methods for obtaining VOB data and sizes

5. AD chaining, instead of assuming files will always fit in 1 AD group.

6. dvd_udf API uses "UDF_FILE" type instead of "starting block number".

7. Separate struct AD and struct FileAD instead of ugly 64->32 bit work.

8. Minor memory clearing problems, and memory leaks.

9. ISO inside RAR support, dvd_input method for spawning unrar.

If the last item is not desired, simply ignore the changes to dvd_input.[ch].

Lund

--
Jorgen Lundman       | <[email protected]>
Unix Administrator   | +81 (0)3 -5456-2687 ext 1017 (work)
Shibuya-ku, Tokyo    | +81 (0)90-5578-8500          (cell)
Japan                | +81 (0)3 -3375-1767          (home)
Index: dvd_reader.c
===================================================================
--- dvd_reader.c        (revision 1153)
+++ dvd_reader.c        (working copy)
@@ -102,7 +102,7 @@
   int css_title;
 
   /* Information required for an image file. */
-  uint32_t lb_start;
+  UDF_FILE udf_file;
   uint32_t seek_pos;
 
   /* Information required for a directory path drive. */
@@ -111,6 +111,10 @@
 
   /* Calculated at open-time, size in blocks. */
   ssize_t filesize;
+
+  /* Calculated size of file in bytes. */
+  uint64_t filebytes;
+
 };
 
 int UDFReadBlocksRaw( dvd_reader_t *device, uint32_t lb_number,
@@ -159,7 +163,8 @@
   struct timeval all_s, all_e;
   struct timeval t_s, t_e;
   char filename[ MAX_UDF_FILE_NAME_LEN ];
-  uint32_t start, len;
+  UDF_FILE udf_file;
+  uint64_t len;
   int title;
 
   char *nokeys_str = getenv("DVDREAD_NOKEYS");
@@ -179,31 +184,33 @@
     } else {
       sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 0 );
     }
-    start = UDFFindFile( dvd, filename, &len );
-    if( start != 0 && len != 0 ) {
+    udf_file = UDFFindFile( dvd, filename, &len );
+    if( udf_file != NULL && 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);
+               filename, UDFFileBlockPos(udf_file, 0) );
+      if( dvdinput_title( dvd->dev, (int)UDFFileBlockPos(udf_file, 0) ) < 0 ) {
+        fprintf( stderr, "libdvdread: Error cracking CSS key for %s 
(0x%08x)\n", filename, UDFFileBlockPos(udf_file, 0));
       }
       gettimeofday( &t_e, NULL );
       fprintf( stderr, "libdvdread: Elapsed time %ld\n",
                (long int) t_e.tv_sec - t_s.tv_sec );
     }
 
+    UDFFreeFile(dvd, udf_file); udf_file = NULL;
+
     if( title == 0 ) continue;
 
     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;
+    udf_file = UDFFindFile( dvd, filename, &len );
+    if( udf_file == NULL || len == 0 ) break;
 
     /* 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);
+             filename, UDFFileBlockPos(udf_file, 0) );
+    if( dvdinput_title( dvd->dev, (int)UDFFileBlockPos(udf_file, 0) ) < 0 ) {
+      fprintf( stderr, "libdvdread: Error cracking CSS key for %s 
(0x%08x)!!\n", filename, UDFFileBlockPos(udf_file, 0));
     }
     gettimeofday( &t_e, NULL );
     fprintf( stderr, "libdvdread: Elapsed time %ld\n",
@@ -216,6 +223,9 @@
   fprintf( stderr, "libdvdread: Elapsed time %ld\n",
            (long int) all_e.tv_sec - all_s.tv_sec );
 
+  UDFFreeFile(dvd, udf_file); udf_file = NULL;
+
+
   return 0;
 }
 
@@ -259,6 +269,47 @@
   return dvd;
 }
 
+/*
+ * Open a image inside a rar. Takes extra filepath argument to the rar archive
+ */
+dvd_reader_t *DVDOpenRARImageFile( const char *unrar_cmd,
+                                   const char *rarfile,
+                                   const char *location )
+{
+  dvd_reader_t *dvd;
+  dvd_input_t dev;
+  
+  dvdinput_setupRAR(unrar_cmd, rarfile);
+  
+  dev = dvdinput_open( location );
+  if( !dev ) {
+      fprintf( stderr, "libdvdread: Can't openRAR %s:%s for reading\n", 
+               rarfile, location);
+      return NULL;
+  }
+  
+  dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
+  if( !dvd ) {
+      dvdinput_close(dev);
+        return NULL;
+  }
+  dvd->isImageFile = 1;
+  dvd->dev = dev;
+  dvd->path_root = NULL;
+  
+  dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
+  dvd->udfcache = NULL;
+  
+  dvd->css_title = 0;
+  
+  return dvd;
+}
+
+
+
+
+
+
 static dvd_reader_t *DVDOpenPath( const char *path_root )
 {
   dvd_reader_t *dvd;
@@ -565,11 +616,12 @@
  */
 static dvd_file_t *DVDOpenFileUDF( dvd_reader_t *dvd, char *filename )
 {
-  uint32_t start, len;
+  UDF_FILE udf_file;
+  uint64_t len;
   dvd_file_t *dvd_file;
 
-  start = UDFFindFile( dvd, filename, &len );
-  if( !start ) {
+  udf_file = UDFFindFile( dvd, filename, &len );
+  if( !udf_file ) {
     fprintf( stderr, "libdvdnav:DVDOpenFileUDF:UDFFindFile %s failed\n", 
filename );
     return NULL;
   }
@@ -577,14 +629,16 @@
   dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
   if( !dvd_file ) {
     fprintf( stderr, "libdvdnav:DVDOpenFileUDF:malloc failed\n" );
+    UDFFreeFile(dvd, udf_file);
     return NULL;
   }
   dvd_file->dvd = dvd;
-  dvd_file->lb_start = start;
+  dvd_file->udf_file = udf_file;
   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;
+  dvd_file->filebytes = len;
 
   return dvd_file;
 }
@@ -676,7 +730,7 @@
     return NULL;
   }
   dvd_file->dvd = dvd;
-  dvd_file->lb_start = 0;
+  dvd_file->udf_file = NULL;
   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 ) );
@@ -690,6 +744,7 @@
   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 ];
+  dvd_file->filebytes = fileinfo.st_size;
 
   return dvd_file;
 }
@@ -697,7 +752,8 @@
 static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *dvd, int title, int menu )
 {
   char filename[ MAX_UDF_FILE_NAME_LEN ];
-  uint32_t start, len;
+  UDF_FILE udf_file;
+  uint64_t len;
   dvd_file_t *dvd_file;
 
   if( title == 0 ) {
@@ -705,18 +761,19 @@
   } else {
     sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
   }
-  start = UDFFindFile( dvd, filename, &len );
-  if( start == 0 ) return NULL;
+  udf_file = UDFFindFile( dvd, filename, &len );
+  if( udf_file == 0 ) return NULL;
 
   dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
   if( !dvd_file ) return NULL;
   dvd_file->dvd = dvd;
   /*Hack*/ dvd_file->css_title = title << 1 | menu;
-  dvd_file->lb_start = start;
+  dvd_file->udf_file = udf_file;
   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;
+  dvd_file->filebytes = len;
 
   /* Calculate the complete file size for every file in the VOBS */
   if( !menu ) {
@@ -755,11 +812,12 @@
   if( !dvd_file ) return NULL;
   dvd_file->dvd = dvd;
   /*Hack*/ dvd_file->css_title = title << 1 | menu;
-  dvd_file->lb_start = 0;
+  dvd_file->udf_file = NULL;
   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;
+  dvd_file->filebytes = 0;
 
   if( menu ) {
     dvd_input_t dev;
@@ -790,6 +848,7 @@
     dvd_file->title_devs[ 0 ] = dev;
     dvdinput_title( dvd_file->title_devs[0], 0);
     dvd_file->filesize = dvd_file->title_sizes[ 0 ];
+    dvd_file->filebytes = fileinfo.st_size;
 
   } else {
     for( i = 0; i < TITLES_MAX; ++i ) {
@@ -884,11 +943,200 @@
       }
     }
 
+    if (dvd_file->udf_file)
+        UDFFreeFile(NULL, dvd_file->udf_file); 
+
     free( dvd_file );
     dvd_file = 0;
   }
 }
 
+
+
+static int DVDFileStatVOBUDF(dvd_reader_t *dvd, int title, 
+                             int menu, dvd_stat_t *statbuf)
+{
+  char filename[ MAX_UDF_FILE_NAME_LEN ];
+  uint64_t size;
+  off_t tot_size;
+  off_t parts_size[9];
+  int nr_parts = 0;
+  int n;
+  UDF_FILE udf_file;
+ 
+  if( title == 0 ) {
+    sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
+  } else {
+    sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
+  }
+  if(!(udf_file = UDFFindFile( dvd, filename, &size ))) {
+    return -1;
+  }
+  UDFFreeFile(dvd, udf_file);
+
+  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( !(udf_file = UDFFindFile( dvd, filename, &size ) )) {
+        break;
+      }
+          UDFFreeFile(dvd, udf_file);
+
+      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 ) {
+    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 ) {
+        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;
+  uint64_t size;
+  UDF_FILE udf_file;
+
+  /* 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:
+    errno = EINVAL;
+    return -1;
+  }
+  
+  if( dvd->isImageFile ) {
+          if( (udf_file = UDFFindFile( dvd, filename, &size )) ) {
+                  statbuf->size = size;
+                  statbuf->nr_parts = 1;
+                  statbuf->parts_size[0] = size;
+                  UDFFreeFile(dvd, udf_file);
+                  return 0;
+          }
+  } else {
+    if( findDVDFile( dvd, filename, full_path ) )  {
+      if( stat( full_path, &fileinfo ) < 0 ) {
+                  ;
+      } else {
+        statbuf->size = fileinfo.st_size;
+        statbuf->nr_parts = 1;
+        statbuf->parts_size[0] = statbuf->size;
+        return 0;
+      }
+    }
+  }
+  return -1;
+}
+
+
+
+
 /* Internal, but used from dvd_udf.c */
 int UDFReadBlocksRaw( dvd_reader_t *device, uint32_t lb_number,
                       size_t block_count, unsigned char *data,
@@ -922,7 +1170,8 @@
                              size_t block_count, unsigned char *data,
                              int encrypted )
 {
-  return UDFReadBlocksRaw( dvd_file->dvd, dvd_file->lb_start + offset,
+  return UDFReadBlocksRaw( dvd_file->dvd, 
+                           UDFFileBlockPos(dvd_file->udf_file, offset),
                            block_count, data, encrypted );
 }
 
@@ -1014,7 +1263,7 @@
   if( dvd_file->dvd->css_title != dvd_file->css_title ) {
     dvd_file->dvd->css_title = dvd_file->css_title;
     if( dvd_file->dvd->isImageFile ) {
-      dvdinput_title( dvd_file->dvd->dev, (int)dvd_file->lb_start );
+      dvdinput_title( dvd_file->dvd->dev, 
(int)UDFFileBlockPos(dvd_file->udf_file, 0) );
     }
     /* Here each vobu has it's own dvdcss handle, so no need to update
     else {
@@ -1121,6 +1370,16 @@
   return dvd_file->filesize;
 }
 
+uint64_t DVDFileSize64( dvd_file_t *dvd_file )
+{
+    /* Check arguments. */
+    if( dvd_file == NULL )
+      return -1;
+    
+    return dvd_file->filebytes;
+}
+
+
 int DVDDiscID( dvd_reader_t *dvd, unsigned char *discid )
 {
   struct md5_ctx ctx;
@@ -1142,6 +1401,8 @@
       char *buffer_base = malloc( file_size + 2048 );
       char *buffer = (char *)(((uintptr_t)buffer_base & ~((uintptr_t)2047)) + 
2048);
 
+      dvd_file->udf_file = NULL;
+
       if( buffer_base == NULL ) {
           DVDCloseFile( dvd_file );
           fprintf( stderr, "libdvdread: DVDDiscId, failed to "
@@ -1262,3 +1523,104 @@
 
   return 0;
 }
+
+
+/** 
+ * opendir(3)-like function for traversing a UDF image.
+ * 
+ * dvd_dir_t *DVDOpenDir( dvd_reader_t *dvd, char *subdir )
+ *
+ * Opens "subdir" directory for traversal. Returns NULL for failure
+ * or a valid dvd_dir_t * otherwise.
+ *
+ * This function differs from POSIX in that it must also be passed
+ * the dvd_reader_t *.
+ *
+ */
+dvd_dir_t *DVDOpenDir( dvd_reader_t *dvd, char *subdir )
+{
+  UDF_FILE udf_file;
+  uint64_t filesize;
+  dvd_dir_t *result;
+
+  udf_file = UDFFindFile( dvd, subdir, &filesize );
+  
+  if (!udf_file) return NULL;
+  
+#ifdef DEBUG
+  fprintf(stdout, "Found '%s' at %d (size %"PRIu64")\n", subdir, 
+          UDFFileBlockPos(udf_file, 0), filesize);
+#endif
+  
+  result = (dvd_dir_t *)malloc(sizeof(*result));
+  if (!result) {
+      UDFFreeFile(dvd, udf_file);
+      return NULL;
+  }
+  
+  memset(result, 0, sizeof(*result));
+  
+  result->dir_location = UDFFileBlockPos(udf_file, 0);
+  result->dir_current  = UDFFileBlockPos(udf_file, 0);
+  result->dir_length   = filesize; 
+  UDFFreeFile(dvd, udf_file);
+  
+  return result;
+}
+
+
+/**
+ * readdir(3)-like function for traversing a UDF image.
+ *
+ * This function differs from POSIX in that it must also be passed
+ * the dvd_reader_t *.
+ *
+ */
+dvd_dirent_t *DVDReadDir( dvd_reader_t *dvd, dvd_dir_t *dirp )
+{
+
+  if (!UDFScanDirX(dvd, dirp)) {
+    dirp->current_p = 0;
+    dirp->dir_current = dirp->dir_location; // this is a rewind, wanted?
+    return NULL;
+  }
+  
+#ifdef DEBUG
+  fprintf(stderr, "DVDReadDir(%s)\r\n", dirp->entry.d_name);
+#endif
+  
+  return &dirp->entry;
+
+}
+
+/**
+ * closedir(3)-like function for traversing a UDF image.
+ *
+ * This function differs from POSIX in that it must also be passed
+ * the dvd_reader_t *.
+ *
+ */
+
+int DVDCloseDir( dvd_reader_t *dvd, dvd_dir_t *dirp )
+{
+  if (!dirp) return 0;
+
+  free(dirp);
+
+  return 0;
+
+}
+
+/**
+ * Nearly identical function to DVDOpenFile, but instead of mapping
+ * DVD domain and title, this takes an actual filename to open.
+ */
+dvd_file_t *DVDOpenFilename( dvd_reader_t *dvd, char *filename)
+{
+  if( dvd->isImageFile ) {
+      return DVDOpenFileUDF( dvd, filename );
+  } else {
+      return DVDOpenFilePath( dvd, filename );
+  }
+}
+
Index: dvd_input.h
===================================================================
--- dvd_input.h (revision 1153)
+++ dvd_input.h (working copy)
@@ -46,5 +46,7 @@
  * Setup function accessed by dvd_reader.c.  Returns 1 if there is CSS support.
  */
 int dvdinput_setup(void);
+int dvdinput_setupRAR(const char *, const char *);
 
+
 #endif /* LIBDVDREAD_DVD_INPUT_H */
Index: dvd_reader.h
===================================================================
--- dvd_reader.h        (revision 1153)
+++ dvd_reader.h        (working copy)
@@ -104,6 +104,17 @@
  */
 void DVDClose( dvd_reader_t * );
 
+/*
+ * Open an ImageFile contained in a RAR archive volume. This API call should
+ * probably be cleaned up.
+ * @lundman: 20080817
+ */
+dvd_reader_t *DVDOpenRARImageFile( const char *unrar_cmd,
+                                                                  const char 
*rarfile,
+                                                                  const char 
*location );
+
+
+
 /**
  *
  */
@@ -116,7 +127,89 @@
                                   single file. */
 } dvd_read_domain_t;
 
+
+/*
+ * DVD stat structure for getting VOB sizes
+ */
+typedef struct {
+  off_t size;          /**< Total size of file in bytes */
+  int nr_parts;           /**< Number of file parts */
+  off_t parts_size[9]; /**< Size of each part in bytes */
+} dvd_stat_t;
+
+
+/*
+ * DVDReaddir entry types.
+ */
+typedef enum {
+  DVD_DT_UNKNOWN = 0,
+  DVD_DT_FIFO,
+  DVD_DT_CHR,
+  DVD_DT_DIR,
+  DVD_DT_BLK,
+  DVD_DT_REG,
+  DVD_DT_LNK,
+  DVD_DT_SOCK,
+  DVD_DT_WHT
+} dvd_dir_type_t;
+
+/*
+ * DVDReaddir structure.
+ * Extended a little from POSIX to also return filesize.
+ */
+typedef struct {
+  unsigned char  d_name[MAX_UDF_FILE_NAME_LEN]; 
+  // "Shall not exceed 1023; Ecma-167 page 123"
+  dvd_dir_type_t d_type;       // DT_REG, DT_DIR
+  unsigned int   d_namlen;
+  uint64_t       d_filesize;
+} dvd_dirent_t;
+
+
+/*
+ * DVDOpendir DIR* structure
+ */
+typedef struct {
+  uint32_t dir_location;
+  uint32_t dir_length;
+  uint32_t dir_current;  // Separate to _location should we one day want to
+                            // implement dir_rewind()
+  unsigned int current_p; // Internal implementation specific. UDFScanDirX
+  dvd_dirent_t entry;
+} dvd_dir_t;
+
+
 /**
+ * Stats a file on the DVD given the title number and domain.
+ * The information about the file is stored in a dvd_stat_t
+ * which contains information about the size of the file and
+ * the number of parts in case of a multipart file and the respective
+ * sizes of the parts.
+ * A multipart file is for instance VTS_02_1.VOB, VTS_02_2.VOB, VTS_02_3.VOB
+ * The size of VTS_02_1.VOB will be stored in stat->parts_size[0],
+ * VTS_02_2.VOB in stat->parts_size[1], ...
+ * The total size (sum of all parts) is stored in stat->size and
+ * stat->nr_parts will hold the number of parts.
+ * Only DVD_READ_TITLE_VOBS (VTS_??_[1-9].VOB) can be multipart files.
+ * 
+ * This function is only of use if you want to get the size of each file
+ * in the filesystem. These sizes are not needed to use any other
+ * functions in libdvdread. 
+ *
+ * @param dvd  A dvd read handle.
+ * @param titlenum Which Video Title Set should be used, VIDEO_TS is 0.
+ * @param domain Which domain. 
+ * @param stat Pointer to where the result is stored.
+ * @return If successful 0, otherwise -1.
+ *
+ * int DVDFileStat(dvd, titlenum, domain, stat);
+ */
+int DVDFileStat(dvd_reader_t *, int, dvd_read_domain_t, dvd_stat_t *);
+
+
+
+
+/**
  * Opens a file on the DVD given the title number and domain.
  *
  * If the title number is 0, the video manager information is opened
@@ -194,7 +287,12 @@
  * blocks = DVDFileSize(dvd_file);
  */
 ssize_t DVDFileSize( dvd_file_t * );
+/*
+ * Define separate 64-bit version to be back-ward compatible.
+ */
+uint64_t DVDFileSize64( dvd_file_t * );
 
+
 /**
  * Get a unique 128 bit disc ID.
  * This is the MD5 sum of VIDEO_TS.IFO and the VTS_0?_0.IFO files
@@ -271,6 +369,23 @@
  */
 int DVDUDFCacheLevel( dvd_reader_t *, int );
 
+
+/*
+ * Directory iterator functions to mimick POSIX's opendir/readdir/closedir
+ */
+dvd_dir_t    *DVDOpenDir     ( dvd_reader_t *, char *);
+
+dvd_dirent_t *DVDReadDir     ( dvd_reader_t *, dvd_dir_t *);
+
+int           DVDCloseDir    ( dvd_reader_t *, dvd_dir_t *);
+
+/*
+ * Open a file based on filename. Usually used after opendir()/readdir().
+ */
+dvd_file_t   *DVDOpenFilename( dvd_reader_t *, char * );
+
+
+
 #ifdef __cplusplus
 };
 #endif
Index: dvd_udf.c
===================================================================
--- dvd_udf.c   (revision 1153)
+++ dvd_udf.c   (working copy)
@@ -5,6 +5,7 @@
  * Modifications by:
  *   Billy Biggs <[email protected]>.
  *   Björn Englund <[email protected]>.
+ *   Jörgen Lundman <[email protected]> for UDF-2.50 changes.
  *
  * dvdudf: parse and read the UDF volume information of a DVD Video
  * Copyright (C) 1999 Christian Wolff for convergence integrated media
@@ -40,6 +41,7 @@
 #include <inttypes.h>
 
 #include "dvd_reader.h"
+#define DVD_UFD_INTERNAL
 #include "dvd_udf.h"
 
 /* Private but located in/shared with dvd_reader.c */
@@ -88,6 +90,10 @@
   uint32_t Length;
 };
 
+/* Previously, dvdread re-used AD to carry up the filesize, resulting in
+ * a 64bit->32bit problem. This is acceptable on most DVD images (1GB VOB 
files)
+ * but not desirable. Now it has changed to use a FileAD when it is actually
+ * used for files, and AD otherwise.   - Lund */
 struct AD {
   uint32_t Location;
   uint32_t Length;
@@ -95,6 +101,26 @@
   uint16_t Partition;
 };
 
+/* Previously dvdread would assume files only had one AD chain, and since they
+ * are 1GB or less, this is most problably true. However, now we handle chains
+ * for large files. ECMA_167 does not specify the maximum number of chains, is
+ * it as many as can fit in a 2048 block (minus ID 266 size), or some other 
+ * limit. For now, I have assumed that;
+ * a 4.4GB file uses 5 AD chains. A BluRay disk can store 50GB of data, so the
+ * largest file should be 50 GB. So the maximum number of chains should be
+ * around 62.
+ */
+
+#define UDF_MAX_AD_CHAINS 50
+
+struct FileAD {
+    uint64_t Length;
+       uint32_t num_AD;
+       uint32_t Partition_Start; 
+       struct AD AD_chain[UDF_MAX_AD_CHAINS];
+};
+
+
 struct extent_ad {
   uint32_t location;
   uint32_t length;
@@ -119,7 +145,7 @@
 
 struct icbmap {
   uint32_t lbn;
-  struct AD file;
+  struct FileAD file;
   uint8_t filetype;
 };
 
@@ -329,6 +355,15 @@
                   | ((uint32_t)data[(p) + 1] << 8)      \
                   | ((uint32_t)data[(p) + 2] << 16)     \
                   | ((uint32_t)data[(p) + 3] << 24))
+#define GETN8(p) ((uint64_t)data[p]                 \
+                  | ((uint64_t)data[(p) + 1] << 8)  \
+                  | ((uint64_t)data[(p) + 2] << 16) \
+                  | ((uint64_t)data[(p) + 3] << 24) \
+                  | ((uint64_t)data[(p) + 4] << 32) \
+                  | ((uint64_t)data[(p) + 5] << 40) \
+                  | ((uint64_t)data[(p) + 6] << 48) \
+                  | ((uint64_t)data[(p) + 7] << 56))
+
 /* This is wrong with regard to endianess */
 #define GETN(p, n, target) memcpy(target, &data[p], n)
 
@@ -429,43 +464,172 @@
 }
 
 static int UDFFileEntry( uint8_t *data, uint8_t *FileType,
-                         struct Partition *partition, struct AD *ad )
+                         struct Partition *partition, struct FileAD *fad )
 {
   uint16_t flags;
   uint32_t L_EA, L_AD;
   unsigned int p;
+  unsigned int curr_ad;
 
   UDFICB( &data[ 16 ], FileType, &flags );
 
   /* Init ad for an empty file (i.e. there isn't a AD, L_AD == 0 ) */
-  ad->Length = GETN4( 60 ); /* Really 8 bytes a 56 */
-  ad->Flags = 0;
-  ad->Location = 0; /* what should we put here?  */
-  ad->Partition = partition->Number; /* use number of current partition */
+  fad->Length = GETN8( 56 ); /* Was 4 bytes at 60, changed for 64bit. */
 
   L_EA = GETN4( 168 );
   L_AD = GETN4( 172 );
   p = 176 + L_EA;
+  curr_ad = 0;
+
+  /* Function changed to record all AD chains, not just the last one! */
   while( p < 176 + L_EA + L_AD ) {
-    switch( flags & 0x0007 ) {
-    case 0: UDFShortAD( &data[ p ], ad, partition ); p += 8;  break;
-    case 1: UDFLongAD( &data[ p ], ad );  p += 16; break;
-    case 2: UDFExtAD( &data[ p ], ad );   p += 20; break;
-    case 3:
-      switch( L_AD ) {
-      case 8:  UDFShortAD( &data[ p ], ad, partition ); break;
-      case 16: UDFLongAD( &data[ p ], ad );  break;
-      case 20: UDFExtAD( &data[ p ], ad );   break;
-      }
-      p += L_AD;
-      break;
-    default:
-      p += L_AD; break;
-    }
+         struct AD *ad;
+         
+         if (curr_ad >= UDF_MAX_AD_CHAINS) return 0;
+      
+         ad =  &fad->AD_chain[curr_ad];
+         ad->Partition = partition->Number;
+         ad->Flags = 0;
+         // Increase AD chain ptr
+         curr_ad++;
+         
+         switch( flags & 0x0007 ) {
+         case 0: 
+                 UDFShortAD( &data[ p ], ad, partition );
+                 p += 8;
+                 break;
+         case 1: 
+                 UDFLongAD( &data[ p ], ad );
+                 p += 16; 
+                 break;
+         case 2: 
+                 UDFExtAD( &data[ p ], ad );
+                 p += 20; 
+                 break;
+         case 3:
+                 switch( L_AD ) {
+                 case 8:  
+                         UDFShortAD( &data[ p ], ad, partition );
+                         break;
+                 case 16:
+                         UDFLongAD( &data[ p ],  ad );
+                         break;
+                 case 20: 
+                         UDFExtAD( &data[ p ], ad );  
+                         break;
+                 }
+                 p += L_AD;
+                 break;
+         default:
+                 p += L_AD;
+                 break;
+         }
   }
   return 0;
+  
 }
 
+/* TagID 266
+ * The differences in ExtFile are indicated below:
+ * struct extfile_entry {
+ *         struct desc_tag         tag;
+ *         struct icb_tag          icbtag;
+ *         uint32_t                uid;
+ *         uint32_t                gid;
+ *         uint32_t                perm;
+ *         uint16_t                link_cnt;
+ *         uint8_t                 rec_format;
+ *         uint8_t                 rec_disp_attr;
+ *         uint32_t                rec_len;
+ *         uint64_t                inf_len;
+ *         uint64_t                obj_size;        // NEW
+ *         uint64_t                logblks_rec;
+ *         struct timestamp        atime;
+ *         struct timestamp        mtime;
+ *         struct timestamp        ctime;           // NEW
+ *         struct timestamp        attrtime;
+ *         uint32_t                ckpoint;
+ *         uint32_t                reserved1;       // NEW
+ *         struct long_ad          ex_attr_icb;
+ *         struct long_ad          streamdir_icb;   // NEW
+ *         struct regid            imp_id;
+ *         uint64_t                unique_id;
+ *         uint32_t                l_ea;   
+ *         uint32_t                l_ad; 
+ *         uint8_t                 data[1];
+ * } __packed;
+ *
+ * So old "l_ea" is now +216
+ *
+ * Lund
+ */
+
+static int UDFExtFileEntry( uint8_t *data, uint8_t *FileType, 
+                                                       struct Partition 
*partition, struct FileAD *fad )
+{
+    uint16_t flags;
+    uint32_t L_EA, L_AD;
+    unsigned int p;
+       unsigned int curr_ad;
+       
+    UDFICB( &data[ 16 ], FileType, &flags );
+       
+    /* Init ad for an empty file (i.e. there isn't a AD, L_AD == 0 ) */
+       fad->Length = GETN8(56); // 64-bit.
+       
+    L_EA = GETN4( 208);
+    L_AD = GETN4( 212);
+    p = 216 + L_EA;
+       curr_ad = 0;
+    while( p < 216 + L_EA + L_AD ) {
+               struct AD *ad;
+               
+               if (curr_ad >= UDF_MAX_AD_CHAINS) return 0;
+#ifdef DEBUG
+               fprintf(stderr, "libdvdread: reading AD chain %d\r\n", curr_ad);
+#endif
+               ad =  &fad->AD_chain[curr_ad];
+               ad->Partition = partition->Number;
+               ad->Flags = 0;
+               curr_ad++;
+               
+        switch( flags & 0x0007 ) {
+               case 0: 
+                       UDFShortAD( &data[ p ], ad, partition );
+                       p += 8;
+                       break;
+               case 1: 
+                       UDFLongAD( &data[ p ], ad );
+                       p += 16; 
+                       break;
+               case 2: 
+                       UDFExtAD( &data[ p ], ad );
+                       p += 20; 
+                       break;
+               case 3:
+                       switch( L_AD ) {
+                       case 8:  
+                               UDFShortAD( &data[ p ], ad, partition );
+                               break;
+                       case 16:
+                               UDFLongAD( &data[ p ],  ad );
+                               break;
+                       case 20: 
+                               UDFExtAD( &data[ p ], ad );  
+                               break;
+                       }
+                       p += L_AD;
+                       break;
+               default:
+                       p += L_AD;
+                       break;
+        }
+    }
+    return 0;
+}
+
+
+
 static int UDFFileIdentifier( uint8_t *data, uint8_t *FileCharacteristics,
                               char *FileName, struct AD *FileICB )
 {
@@ -489,7 +653,7 @@
  * return 1 on success, 0 on error;
  */
 static int UDFMapICB( dvd_reader_t *device, struct AD ICB, uint8_t *FileType,
-                      struct Partition *partition, struct AD *File )
+                      struct Partition *partition, struct FileAD *File )
 {
   uint8_t LogBlock_base[DVD_VIDEO_LB_LEN + 2048];
   uint8_t *LogBlock = (uint8_t *)(((uintptr_t)LogBlock_base & 
~((uintptr_t)2047)) + 2048);
@@ -511,6 +675,12 @@
     else
       UDFDescriptor( LogBlock, &TagID );
 
+#ifdef DEBUG
+       fprintf(stderr, 
+                       "libdvdread: reading block %d TagID %d\r\n",
+                       lbnum, TagID);
+#endif
+
     if( TagID == 261 ) {
       UDFFileEntry( LogBlock, FileType, partition, File );
       memcpy(&tmpmap.file, File, sizeof(tmpmap.file));
@@ -518,8 +688,19 @@
       SetUDFCache(device, MapCache, tmpmap.lbn, &tmpmap);
       return 1;
     };
+
+       /* ExtendedFileInfo */
+       if( TagID == 266 ) {
+               UDFExtFileEntry( LogBlock, FileType, partition, File );
+               memcpy(&tmpmap.file, File, sizeof(tmpmap.file));
+               tmpmap.filetype = *FileType;
+               SetUDFCache(device, MapCache, tmpmap.lbn, &tmpmap);
+               return 1;
+       }
+
+
   } while( ( lbnum <= partition->Start + ICB.Location + ( ICB.Length - 1 )
-             / DVD_VIDEO_LB_LEN ) && ( TagID != 261 ) );
+             / DVD_VIDEO_LB_LEN ) && ( TagID != 261 ) && (TagID != 266) );
 
   return 0;
 }
@@ -530,7 +711,7 @@
  * FileICB: Location of ICB of the found file
  * return 1 on success, 0 on error;
  */
-static int UDFScanDir( dvd_reader_t *device, struct AD Dir, char *FileName,
+static int UDFScanDir( dvd_reader_t *device, struct FileAD Dir, char *FileName,
                        struct Partition *partition, struct AD *FileICB,
                        int cache_file_info)
 {
@@ -548,13 +729,13 @@
   int in_cache = 0;
 
   /* Scan dir for ICB of file */
-  lbnum = partition->Start + Dir.Location;
+  lbnum = partition->Start + Dir.AD_chain[0].Location;
 
   if(DVDUDFCacheLevel(device, -1) > 0) {
     /* caching */
 
     if(!GetUDFCache(device, LBUDFCache, lbnum, &cached_dir)) {
-      dir_lba = (Dir.Length + DVD_VIDEO_LB_LEN) / DVD_VIDEO_LB_LEN;
+      dir_lba = (Dir.AD_chain[0].Length + DVD_VIDEO_LB_LEN) / DVD_VIDEO_LB_LEN;
       if((cached_dir_base = malloc(dir_lba * DVD_VIDEO_LB_LEN + 2048)) == NULL)
         return 0;
       cached_dir = (uint8_t *)(((uintptr_t)cached_dir_base & 
~((uintptr_t)2047)) + 2048);
@@ -582,15 +763,17 @@
 
     p = 0;
 
-    while( p < Dir.Length ) {
+    while( p < Dir.AD_chain[0].Length ) {  /* Assuming dirs don't use chains? 
*/
       UDFDescriptor( &cached_dir[ p ], &TagID );
       if( TagID == 257 ) {
         p += UDFFileIdentifier( &cached_dir[ p ], &filechar,
                                 filename, &tmpICB );
         if(cache_file_info && !in_cache) {
           uint8_t tmpFiletype;
-          struct AD tmpFile;
+          struct FileAD tmpFile;
 
+          memset(&tmpFile, 0, sizeof(tmpFile));
+
           if( !strcasecmp( FileName, filename ) ) {
             memcpy(FileICB, &tmpICB, sizeof(tmpICB));
             found = 1;
@@ -617,11 +800,11 @@
     return 0;
 
   p = 0;
-  while( p < Dir.Length ) {
+  while( p < Dir.AD_chain[0].Length ) {
     if( p > DVD_VIDEO_LB_LEN ) {
       ++lbnum;
       p -= DVD_VIDEO_LB_LEN;
-      Dir.Length -= DVD_VIDEO_LB_LEN;
+      Dir.AD_chain[0].Length -= DVD_VIDEO_LB_LEN;
       if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) {
         return 0;
       }
@@ -641,6 +824,106 @@
 }
 
 
+/**
+ * Low-level function used by DVDReadDir to simulate readdir().
+ * Dir: Location of directory to scan
+ * FileName: Name of file to look for
+ * FileICB: Location of ICB of the found file
+ * return 1 on success, 0 on error;
+ *
+ * Perhaps a better name is more appropriate, it also does no longer use
+ * memory cache. FIXME. -Lund
+ */
+int UDFScanDirX( dvd_reader_t *device,
+                                dvd_dir_t *dirp )
+{
+    char filename[ MAX_UDF_FILE_NAME_LEN ];
+    uint8_t directory_base[ 2 * DVD_VIDEO_LB_LEN + 2048];
+    uint8_t *directory = (uint8_t *)(((uintptr_t)directory_base & 
~((uintptr_t)2047)) + 2048);
+    uint32_t lbnum;
+    uint16_t TagID;
+    uint8_t filechar;
+    unsigned int p;
+       struct AD FileICB;
+       struct FileAD File;
+    struct Partition partition;
+    uint8_t filetype;
+       
+    if(!(GetUDFCache(device, PartitionCache, 0, &partition)))
+               return 0;
+       
+    /* Scan dir for ICB of file */
+       lbnum = dirp->dir_current;
+    
+       // I have cut out the caching part of the original UDFScanDir() function
+       // one day we should probably bring it back.
+       memset(&File, 0, sizeof(File));
+
+    if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) {
+        return 0;
+    }
+       
+       
+    p = dirp->current_p;
+    while( p < dirp->dir_length ) {
+        if( p > DVD_VIDEO_LB_LEN ) {
+            ++lbnum;
+            p -= DVD_VIDEO_LB_LEN;
+
+            //Dir.Length -= DVD_VIDEO_LB_LEN;
+                        if (dirp->dir_length >= DVD_VIDEO_LB_LEN)
+                                dirp->dir_length -= DVD_VIDEO_LB_LEN;
+                        else
+                                dirp->dir_length = 0;
+
+            if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) {
+                return 0;
+            }
+        }
+        UDFDescriptor( &directory[ p ], &TagID );
+               
+        if( TagID == 257 ) {
+                       
+            p += UDFFileIdentifier( &directory[ p ], &filechar,
+                                    filename, &FileICB );
+                       
+                       dirp->current_p = p;
+                       dirp->dir_current = lbnum;
+                       
+                       if (!*filename)  // No filename, simulate "." dirname
+                               strcpy((char *)dirp->entry.d_name, ".");
+                       else {
+                               // Bah, MSVC don't have strlcpy()
+                               strncpy((char *)dirp->entry.d_name, filename,
+                                               sizeof(dirp->entry.d_name)-1);
+                               dirp->entry.d_name[ sizeof(dirp->entry.d_name) 
- 1 ] = 0;
+                       }
+
+
+                       // Look up the Filedata
+                       if( !UDFMapICB( device, FileICB, &filetype, &partition, 
&File)) 
+                               return 0;
+                       if (filetype == 4) 
+                               dirp->entry.d_type = DVD_DT_DIR;
+                       else
+                               dirp->entry.d_type = DVD_DT_REG; // Add more 
types?
+                       
+                       dirp->entry.d_filesize = File.Length;
+                       
+                       return 1;
+                       
+        } else {
+                       // Not TagID 257
+            return 0;
+        }
+    }
+       // End of DIR
+    return 0;
+}
+
+
+
+
 static int UDFGetAVDP( dvd_reader_t *device,
                        struct avdp_t *avdp)
 {
@@ -768,23 +1051,103 @@
   return part->valid;
 }
 
-uint32_t UDFFindFile( dvd_reader_t *device, char *filename,
-                      uint32_t *filesize )
+
+/*
+ * Release an allocated FileAD data. Technically you can just call free() 
+ * but it is clean to stay modular. (Should we one day record further data.
+ */
+void UDFFreeFile(dvd_reader_t *device, struct FileAD *File)
 {
+       free(File);
+}
+
+
+/*
+ * Translate between File logic block, and actual disk-block, taking into
+ * account for partition start, file block start, and chain position.
+ *
+ * The API users will refer to block 0 as start of file and going up
+ * we need to convert that to actual disk block;
+ * partition_start + file_start + offset
+ * but keep in mind that file_start is chained, and not contiguous.
+ *
+ * We return "0" as error, since a File can not start at physical block 0
+ *
+ */
+uint32_t UDFFileBlockPos(struct FileAD *File, uint32_t file_block)
+{
+       uint32_t result, i, offset;
+       
+       if (!File) return 0;
+       // Look through the chain to see where this block would belong.
+       for (i = 0, offset = 0;
+                i < File->num_AD; 
+                i++) {
+               
+               // Is "file_block" inside this chain? Then use this chain.
+               if (file_block < (offset + 
+                                                 (File->AD_chain[i].Length /  
DVD_VIDEO_LB_LEN)))
+                       break;
+               
+               offset += (File->AD_chain[i].Length /  DVD_VIDEO_LB_LEN);
+        
+       }
+       
+       // Beyond out chains of AD? We could return error, or just fall back to
+       // old behavior in case someone relies on the broken way.
+       if (i >= File->num_AD)
+               i = 0; 
+       
+       // Compute actual block number
+       result = File->Partition_Start 
+               + File->AD_chain[i].Location
+               + file_block
+               - offset;
+       
+       
+#ifdef DEBUG
+       if (!(file_block % 10000))
+               fprintf(stderr, "libdvdread: File block %d -> %d (chain %d + 
offset %d : %d)\r\n", 
+                               file_block, result, 
+                               i,
+                               file_block - offset,
+                               (File->AD_chain[i].Length / DVD_VIDEO_LB_LEN));
+#endif
+       return result;
+       
+}
+
+
+
+
+/*
+ * Find a File/Directory based on name. 
+ *  in: DVD Device *
+ *  in: filename
+ * out: allocated UDF_FILE*, call UDFFreeFile() to release.
+ * out: Filesize. (Note: 64-bit)
+ * out: NULL to indicate error, File Not Found.
+ */
+UDF_FILE UDFFindFile( dvd_reader_t *device, char *filename,
+                      uint64_t *filesize )
+{
   uint8_t LogBlock_base[ DVD_VIDEO_LB_LEN + 2048 ];
   uint8_t *LogBlock = (uint8_t *)(((uintptr_t)LogBlock_base & 
~((uintptr_t)2047)) + 2048);
   uint32_t lbnum;
   uint16_t TagID;
   struct Partition partition;
-  struct AD RootICB, File, ICB;
+  struct AD RootICB, ICB;
+  struct FileAD File;
   char tokenline[ MAX_UDF_FILE_NAME_LEN ];
   char *token;
   uint8_t filetype;
+  struct FileAD *result;
 
   *filesize = 0;
   tokenline[0] = '\0';
   strncat(tokenline, filename, MAX_UDF_FILE_NAME_LEN - 1);
   memset(&ICB, 0, sizeof(ICB));
+  memset(&File, 0, sizeof(File));
 
   if(!(GetUDFCache(device, PartitionCache, 0, &partition) &&
        GetUDFCache(device, RootICBCache, 0, &RootICB))) {
@@ -800,6 +1163,30 @@
       else
         UDFDescriptor( LogBlock, &TagID );
 
+      /*
+       * It seems that someone added a FileType of 250, which seems to be
+       * a "redirect" style file entry. If we discover such an entry, we
+       * add on the "location" to partition->Start, and try again.
+       * Who added this? Is there any official guide somewhere?
+       * 2008/09/17 lundman
+       * Should we handle 261(250) as well? FileEntry+redirect
+       */
+      if( TagID == 266 ) {
+          UDFExtFileEntry( LogBlock, &filetype, &partition, &File );
+          if (filetype == 250) {
+#ifdef DEBUG
+              fprintf(stderr, 
+                      "libdvdread: redirect block filetype 250 found at block 
%d, for location +%d\r\n", 
+                      lbnum, File.AD_chain[0].Location);
+#endif
+              partition.Start += File.AD_chain[0].Location;
+              lbnum = partition.Start;
+              SetUDFCache(device, PartitionCache, 0, &partition);
+              continue;
+          }
+      }
+
+
       /* File Set Descriptor */
       if( TagID == 256 )  /* File Set Descriptor */
         UDFLongAD( &LogBlock[ 400 ], &RootICB );
@@ -808,17 +1195,19 @@
 
     /* Sanity checks. */
     if( TagID != 256 )
-      return 0;
-    if( RootICB.Partition != 0 )
-      return 0;
+      return NULL;
+    /* This following test will fail under UDF2.50 images, as it is no longer
+     * valid */
+    /*if( RootICB.Partition != 0 )
+      return 0;*/
     SetUDFCache(device, RootICBCache, 0, &RootICB);
   }
 
   /* Find root dir */
   if( !UDFMapICB( device, RootICB, &filetype, &partition, &File ) )
-    return 0;
+    return NULL;
   if( filetype != 4 )
-    return 0;  /* Root dir should be dir */
+    return NULL;  /* Root dir should be dir */
   {
     int cache_file_info = 0;
     /* Tokenize filepath */
@@ -837,14 +1226,23 @@
   }
 
   /* Sanity check. */
-  if( File.Partition != 0 )
-    return 0;
+  if( File.AD_chain[0].Partition != 0 )
+    return NULL;
   *filesize = File.Length;
   /* Hack to not return partition.Start for empty files. */
-  if( !File.Location )
-    return 0;
-  else
-    return partition.Start + File.Location;
+  if( !File.AD_chain[0].Location )
+    return NULL;
+
+  /* Allocate a new UDF_FILE and return it. */
+  result = (struct FileAD *) malloc(sizeof(*result));
+  if (!result) return NULL;
+
+  memcpy(result, &File, sizeof(*result));
+  
+  result->Partition_Start = partition.Start;
+  
+  return result;
+
 }
 
 
Index: dvd_udf.h
===================================================================
--- dvd_udf.h   (revision 1153)
+++ dvd_udf.h   (working copy)
@@ -39,6 +39,12 @@
 extern "C" {
 #endif
 
+       //#ifndef DVD_UDF_INTERNAL
+       //struct FileAD;
+       //#endif
+/* opaque type of a file handle; perhaps this could be made prettier. */
+typedef struct FileAD *UDF_FILE;
+
 /**
  * Looks for a file on the UDF disc/imagefile and returns the block number
  * where it begins, or 0 if it is not found.  The filename should be an
@@ -46,7 +52,10 @@
  * '/VIDEO_TS/VTS_01_1.IFO'.  On success, filesize will be set to the size of
  * the file in bytes.
  */
-uint32_t UDFFindFile( dvd_reader_t *device, char *filename, uint32_t *size );
+UDF_FILE UDFFindFile( dvd_reader_t *device, char *filename, uint64_t *size );
+void     UDFFreeFile    ( dvd_reader_t *device, UDF_FILE );
+uint32_t UDFFileBlockPos( UDF_FILE, uint32_t file_block);
+int      UDFScanDirX    ( dvd_reader_t *device, dvd_dir_t *dirp );
 
 void FreeUDFCache(void *cache);
 int UDFGetVolumeIdentifier(dvd_reader_t *device,
Index: dvd_input.c
===================================================================
--- dvd_input.c (revision 1153)
+++ dvd_input.c (working copy)
@@ -25,6 +25,7 @@
 #include <stdlib.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <string.h>
 
 #include "dvd_reader.h"
 #include "dvd_input.h"
@@ -37,6 +38,8 @@
 int         (*dvdinput_title) (dvd_input_t, int);
 int         (*dvdinput_read)  (dvd_input_t, void *, int, int);
 char *      (*dvdinput_error) (dvd_input_t);
+char *      dvdinput_unrar_cmd  = NULL;
+char *      dvdinput_unrar_file = NULL;
 
 #ifdef HAVE_DVDCSS_DVDCSS_H
 /* linking to libdvdcss */
@@ -66,6 +69,10 @@
 static char *        (*DVDcss_error) (dvdcss_handle);
 #endif
 
+/* When is it faster to respawn unrar, rather than seek by eating bytes? */
+#define MAXIMUM_SEEK_SIZE 1048576
+
+
 /* The DVDinput handle, add stuff here for new input methods. */
 struct dvd_input_s {
   /* libdvdcss handle */
@@ -73,6 +80,14 @@
 
   /* dummy file input */
   int fd;
+
+  /* unrar support */
+  char *unrar_archive;
+  char *unrar_file; 
+  FILE *unrar_stream;
+  off_t seek_pos;
+  off_t current_pos;
+
 };
 
 
@@ -362,3 +377,264 @@
     return 0;
   }
 }
+
+
+/**
+ * initialize and open a DVD device or file.
+ */
+static dvd_input_t rarfile_open(const char *target)
+{
+  dvd_input_t dev;
+  
+  /* Allocate the library structure */
+  dev = (dvd_input_t) malloc(sizeof(*dev));
+  if(dev == NULL) {
+    fprintf(stderr, "libdvdread: Could not allocate memory.\n");
+    return NULL;
+  }
+
+  dev->unrar_stream = NULL;
+  dev->unrar_file   = NULL;
+  dev->seek_pos     = 0;  /* Assume start of file */
+  dev->current_pos  = 0;
+
+  /* This is kind of sad, to use setupRAR to pass the rarfile name, then
+   * hope that dvdinput_open is called after. We could also do some sort
+   * of filename separator parsing perhaps. */
+
+  /* Allocated in setupRAR, move it over. */
+  dev->unrar_archive  = dvdinput_unrar_file;
+  dvdinput_unrar_file = NULL;
+
+  /* The filename inside the RAR archive to operate on */
+  dev->unrar_file = strdup(target);
+
+  /* Lets call unrar, to list the release, as a way to quickly check that
+   * all the volumes are present, and the unrar_cmd works? */
+
+  return dev;
+}
+
+
+/**
+ * return the last error message
+ */
+static char *rarfile_error(dvd_input_t dev)
+{
+  /* use strerror(errno)? */
+  return (char *)"unknown error";
+}
+
+/**
+ * seek into the device.
+ */
+static int rarfile_seek(dvd_input_t dev, int blocks)
+{
+
+  dev->seek_pos = (off_t)blocks * (off_t)DVD_VIDEO_LB_LEN;
+
+  /* assert pos % DVD_VIDEO_LB_LEN == 0 */
+  return (int) (dev->seek_pos / DVD_VIDEO_LB_LEN);
+}
+
+/**
+ * set the block for the begining of a new title (key).
+ */
+static int rarfile_title(dvd_input_t dev, int block)
+{
+  return -1;
+}
+
+/**
+ * read data from the device.
+ */
+static int rarfile_read(dvd_input_t dev, void *buffer, int blocks, int flags)
+{
+  size_t len, read_size;
+  char ibuffer[DVD_VIDEO_LB_LEN]; 
+  int ret;
+  
+  /* First, position ourselves where the API wants us. This may mean
+   * spawning new unrar, or possibly eating data until the correct 
+   * position. */
+
+  /* We already have a stream, here, we can be exactly at the right place,
+   * or, need to eat data until the correct position, or
+   * if the seek is too far, close this unrar, and spawn a new.
+   */
+  if (dev->unrar_stream) {
+          
+    /* eat data? */
+    if (dev->seek_pos > dev->current_pos) {
+        
+      /* Seek too far? Better to spawn new unrar? */
+      if ((dev->seek_pos - dev->current_pos) >= MAXIMUM_SEEK_SIZE) {
+#ifdef DEBUG
+        fprintf(stderr, "libdvdread: seek too large, re-spawning unrar\r\n");
+#endif
+        pclose(dev->unrar_stream);
+        dev->unrar_stream = NULL;
+        
+      } else {
+        /* Not too far, read and eat bytes. */
+        while (dev->seek_pos > dev->current_pos) {
+
+          /* Work out how much we need to read, but no larger than
+           * the size of our buffer.*/
+
+          read_size = dev->seek_pos - dev->current_pos;
+          if (read_size > sizeof(ibuffer))
+            read_size = sizeof(ibuffer);
+
+          if ((fread(ibuffer, read_size, 1, dev->unrar_stream)) != 1) {
+            /* Something failed, lets close, and let's try respawn */
+            pclose(dev->unrar_stream);
+            dev->unrar_stream = NULL;
+            break;
+          }
+          dev->current_pos += read_size;
+        } /* while seek > current */
+      } /* NOT >= max seek */
+    } /* NOT seek > current */
+
+    /* Also check if seek < current, then we must restart unrar */
+    if (dev->seek_pos < dev->current_pos) {
+      pclose(dev->unrar_stream);
+      dev->unrar_stream = NULL;
+    }
+    
+  } /* we have active stream */
+  
+  /* Spawn new unrar? */
+  if (!dev->unrar_stream) {
+    snprintf(ibuffer, sizeof(ibuffer), 
+             "%s p -inul -c- -p- -y -cfg- -sk%"PRIu64" -- \"%s\" \"%s\"",
+             dvdinput_unrar_cmd,
+             dev->seek_pos,
+             dev->unrar_archive,
+             dev->unrar_file );
+          
+#ifdef DEBUG
+    fprintf(stderr, "libdvdvread: spawning '%s'\r\n", ibuffer);
+#endif
+
+    dev->unrar_stream = popen(ibuffer, 
+#ifdef WIN32
+                              "rb"
+#else
+                              "r"   /* It is an error to send "b" in Unix :( */
+#endif
+                              );
+          
+    if (!dev->unrar_stream) {
+      return -1;
+    }
+    
+    /* Update ptr */
+    dev->current_pos = dev->seek_pos;
+    
+  }
+
+  /* Assert current == seek ? */ 
+#ifdef DEBUG
+  if (dev->current_pos != dev->seek_pos)
+    fprintf(stderr, "libdvdread: somehow, current_pos != seek_pos!?\r\n");
+#endif  
+  
+  len = (size_t)blocks * DVD_VIDEO_LB_LEN;
+  
+  while(len > 0) {
+      
+    /* ret = read(dev->fd, buffer, len); */
+    ret = fread(buffer, 1, len, dev->unrar_stream);
+
+    if((ret != len) && ferror(dev->unrar_stream)) {
+      /* One of the reads failed, too bad.  We won't even bother
+       * returning the reads that went ok, and as in the posix spec
+       * the file postition is left unspecified after a failure. */
+        return -1;
+    }
+    
+    if (ret < 0)
+      return -1;
+          
+    if ((ret != len) || feof(dev->unrar_stream)) {
+      /* Nothing more to read.  Return the whole blocks, if any, 
+       * that we got. and adjust the file position back to the 
+       * previous block boundary. */
+      size_t bytes = (size_t)blocks * DVD_VIDEO_LB_LEN - len; /* 'ret'? */
+      off_t over_read = -(bytes % DVD_VIDEO_LB_LEN);
+      /*off_t pos =*/ /*lseek(dev->fd, over_read, SEEK_CUR);*/
+      dev->current_pos += (off_t)len;
+      dev->seek_pos = dev->current_pos + (off_t)len + over_read; 
+      /* minus over_read, I did not touch the code above, but I wonder
+       * if it is correct. It does not even use "ret" in the math. */
+      
+      /* should have pos % 2048 == 0 */
+      return (int) (bytes / DVD_VIDEO_LB_LEN);
+    }
+    
+    dev->current_pos += (off_t) ret;
+    dev->seek_pos += (off_t) ret;
+    len -= ret;
+  }
+  
+  return blocks;
+}
+
+
+/**
+ * close the DVD device and clean up.
+ */
+static int rarfile_close(dvd_input_t dev)
+{
+
+  if (dev->unrar_stream) 
+    pclose(dev->unrar_stream);
+  dev->unrar_stream = NULL;
+
+  if (dev->unrar_archive)
+    free(dev->unrar_archive);
+  dev->unrar_archive = NULL;
+
+  if (dev->unrar_file)
+    free(dev->unrar_file);
+  dev->unrar_file = NULL;
+
+  free(dev);
+
+  return 0;
+}
+
+
+
+int dvdinput_setupRAR(const char *unrar_path, const char *rarfile)
+{
+
+#ifdef DEBUG
+  fprintf(stderr, "libdvdread: Configuring for unrar.\n");
+#endif
+  /* Search for unrar in $PATH, common places etc, as well as "unrar_path"
+   *
+   * But we really need unrar with special SEEK -sk option for this to work 
+   *
+   * If you want this to work on Windows, you have to use internal popen()
+   * functions, as Windows popen() does not work correctly.
+   * Please see: http://www.lundman.net/win32_popen.c
+   */
+  
+  dvdinput_unrar_cmd  = strdup(unrar_path);
+  dvdinput_unrar_file = strdup(rarfile);
+  
+  /* Check it is present, and executable? */
+  
+  /* libdvdcss replacement functions */
+  dvdinput_open  = rarfile_open;
+  dvdinput_close = rarfile_close;
+  dvdinput_seek  = rarfile_seek;
+  dvdinput_title = rarfile_title;
+  dvdinput_read  = rarfile_read;
+  dvdinput_error = rarfile_error;
+  
+  return 0;
+}
_______________________________________________
DVDnav-discuss mailing list
[email protected]
https://lists.mplayerhq.hu/mailman/listinfo/dvdnav-discuss

Reply via email to