Separated RAR support, as it is the most questionble change in the old
patch. Told emacs to never use tabs too, so it should be cleaner.
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.
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)
--
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,8 @@
return dvd;
}
+
+
static dvd_reader_t *DVDOpenPath( const char *path_root )
{
dvd_reader_t *dvd;
@@ -565,11 +577,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 +590,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 +691,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 +705,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 +713,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 +722,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 +773,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 +809,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 +904,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 +1131,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 +1224,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 +1331,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 +1362,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 +1484,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_reader.h
===================================================================
--- dvd_reader.h (revision 1153)
+++ dvd_reader.h (working copy)
@@ -104,6 +104,8 @@
*/
void DVDClose( dvd_reader_t * );
+
+
/**
*
*/
@@ -116,7 +118,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 +278,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 +360,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 ) {
+ 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 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;
- }
+ 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;
- 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,8 @@
extern "C" {
#endif
+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 +48,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,
_______________________________________________
DVDnav-discuss mailing list
[email protected]
https://lists.mplayerhq.hu/mailman/listinfo/dvdnav-discuss