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