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 . * Björn Englund . + * Jörgen Lundman 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 #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,