Index: src/dvd_reader.c =================================================================== --- src/dvd_reader.c (revision 1181) +++ src/dvd_reader.c (working copy) @@ -100,7 +100,7 @@ int css_title; /* Information required for an image file. */ - uint32_t lb_start; + udf_file_t *udf_file; uint32_t seek_pos; /* Information required for a directory path drive. */ @@ -163,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; + uint32_t len; + udf_file_t *udf_file; int title; char *nokeys_str = getenv("DVDREAD_NOKEYS"); @@ -183,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", @@ -220,6 +223,8 @@ 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; } @@ -587,22 +592,24 @@ */ static dvd_file_t *DVDOpenFileUDF( dvd_reader_t *dvd, char *filename ) { - uint32_t start, len; + uint32_t len; + udf_file_t *udf_file; 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; } dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) ); if( !dvd_file ) { + UDFFreeFile(dvd, udf_file); fprintf( stderr, "libdvdnav:DVDOpenFileUDF:malloc failed\n" ); 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 ) ); @@ -698,7 +705,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 ) ); @@ -719,7 +726,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; + uint32_t len; + udf_file_t *udf_file; dvd_file_t *dvd_file; if( title == 0 ) { @@ -727,18 +735,22 @@ } 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 == NULL ) return NULL; dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) ); - if( !dvd_file ) return NULL; + if( !dvd_file ) { + UDFFreeFile(dvd, udf_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; + udf_file = NULL; /* Calculate the complete file size for every file in the VOBS */ if( !menu ) { @@ -746,7 +758,8 @@ for( cur = 2; cur < 10; cur++ ) { sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur ); - if( !UDFFindFile( dvd, filename, &len ) ) break; + if( !(udf_file = UDFFindFile( dvd, filename, &len )) ) break; + UDFFreeFile(dvd, udf_file); dvd_file->filesize += len / DVD_VIDEO_LB_LEN; } } @@ -777,7 +790,7 @@ 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 ) ); @@ -904,6 +917,8 @@ } } + if( dvd_file->udf_file ) UDFFreeFile( dvd_file->dvd, dvd_file->udf_file ); + free( dvd_file ); dvd_file = 0; } @@ -918,14 +933,16 @@ off_t parts_size[ 9 ]; int nr_parts = 0; int n; + udf_file_t *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( !UDFFindFile( dvd, filename, &size ) ) + if( !(udf_file = UDFFindFile( dvd, filename, &size ) ) ) return -1; + UDFFreeFile(dvd, udf_file); tot_size = size; nr_parts = 1; @@ -936,8 +953,9 @@ for( cur = 2; cur < 10; cur++ ) { sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur ); - if( !UDFFindFile( dvd, filename, &size ) ) + if( !(udf_file = UDFFindFile( dvd, filename, &size ) ) ) break; + UDFFreeFile(dvd, udf_file); parts_size[ nr_parts ] = size; tot_size += size; @@ -1016,6 +1034,7 @@ char full_path[ PATH_MAX + 1 ]; struct stat fileinfo; uint32_t size; + udf_file_t *udf_file; /* Check arguments. */ if( dvd == NULL || titlenum < 0 ) { @@ -1062,10 +1081,11 @@ } if( dvd->isImageFile ) { - if( UDFFindFile( dvd, filename, &size ) ) { + 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 { @@ -1116,7 +1136,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 ); } @@ -1208,11 +1229,11 @@ 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 { - dvdinput_title( dvd_file->title_devs[ 0 ], (int)dvd_file->lb_start ); + dvdinput_title( dvd_file->title_devs[ 0 ], (int)UDFFileBlockPos(dvd_file->udf_file, 0) ); }*/ } Index: src/dvd_udf.c =================================================================== --- src/dvd_udf.c (revision 1181) +++ src/dvd_udf.c (working copy) @@ -93,6 +93,13 @@ uint16_t Partition; }; +struct UDF_FILE { + 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; @@ -117,7 +124,7 @@ struct icbmap { uint32_t lbn; - struct AD file; + udf_file_t file; uint8_t filetype; }; @@ -327,6 +334,14 @@ | ((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) @@ -427,7 +442,7 @@ } static int UDFFileEntry( uint8_t *data, uint8_t *FileType, - struct Partition *partition, struct AD *ad ) + struct Partition *partition, udf_file_t *fad ) { uint16_t flags; uint32_t L_EA, L_AD; @@ -436,15 +451,22 @@ 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 ); /* Really 8 bytes at 56 */ L_EA = GETN4( 168 ); L_AD = GETN4( 172 ); p = 176 + L_EA; + fad->num_AD = 0; while( p < 176 + L_EA + L_AD ) { + struct AD *ad; + + if (fad->num_AD >= UDF_MAX_AD_CHAINS) return 0; + + ad = &fad->AD_chain[fad->num_AD]; + ad->Partition = partition->Number; + ad->Flags = 0; + fad->num_AD++; + switch( flags & 0x0007 ) { case 0: UDFShortAD( &data[ p ], ad, partition ); @@ -503,7 +525,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, udf_file_t *File ) { uint8_t LogBlock_base[DVD_VIDEO_LB_LEN + 2048]; uint8_t *LogBlock = (uint8_t *)(((uintptr_t)LogBlock_base & ~((uintptr_t)2047)) + 2048); @@ -539,12 +561,12 @@ } /** - * Dir: Location of directory to scan + * Dir: Location of directory to scan (This is not a struct-ptr, so not ANSI C) * FileName: Name of file to look for * 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, udf_file_t Dir, char *FileName, struct Partition *partition, struct AD *FileICB, int cache_file_info) { @@ -562,13 +584,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); @@ -596,15 +618,17 @@ p = 0; - while( p < Dir.Length ) { + while( p < Dir.AD_chain[0].Length ) { 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; + udf_file_t tmpFile; + memset(&tmpFile, 0, sizeof(tmpFile)); + if( !strcasecmp( FileName, filename ) ) { memcpy(FileICB, &tmpICB, sizeof(tmpICB)); found = 1; @@ -631,11 +655,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; } @@ -782,7 +806,58 @@ return part->valid; } -uint32_t UDFFindFile( dvd_reader_t *device, char *filename, + +/* + * Release a UDF file descriptor + */ +void UDFFreeFile(dvd_reader_t *device, udf_file_t *File) +{ + free(File); +} + + +/* + * 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 + * + * It is perhaps unfortunate that Location is in blocks, but Length is + * in bytes..? Can bytes be uneven blocksize in the middle of a chain? + * + */ +uint32_t UDFFileBlockPos(udf_file_t *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); + } + + /* If it is not defined in the AD chains, we should return an error, but + * in the interest of being backward (in)compatible with the original + * libdvdread, we revert back to the traditional view that UDF file are + * one contiguous long chain, in case some API software out there relies on + * this incorrect behavior. + */ + if (i >= File->num_AD) i = 0; + + result = File->Partition_Start + File->AD_chain[i].Location + file_block - offset; + return result; +} + + + + +udf_file_t *UDFFindFile( dvd_reader_t *device, char *filename, uint32_t *filesize ) { uint8_t LogBlock_base[ DVD_VIDEO_LB_LEN + 2048 ]; @@ -790,11 +865,12 @@ uint32_t lbnum; uint16_t TagID; struct Partition partition; - struct AD RootICB, File, ICB; + struct AD RootICB, ICB; + udf_file_t File, *result; char tokenline[ MAX_UDF_FILE_NAME_LEN ]; char *token; uint8_t filetype; - + memset(&File, 0, sizeof(File)); *filesize = 0; tokenline[0] = '\0'; strncat(tokenline, filename, MAX_UDF_FILE_NAME_LEN - 1); @@ -822,17 +898,17 @@ /* Sanity checks. */ if( TagID != 256 ) - return 0; + return NULL; if( RootICB.Partition != 0 ) - return 0; + return NULL; 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 */ @@ -841,9 +917,9 @@ while( token != NULL ) { if( !UDFScanDir( device, File, token, &partition, &ICB, cache_file_info)) - return 0; + return NULL; if( !UDFMapICB( device, ICB, &filetype, &partition, &File ) ) - return 0; + return NULL; if(!strcmp(token, "VIDEO_TS")) cache_file_info = 1; token = strtok( NULL, "/" ); @@ -851,14 +927,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 UDF_FILE node for the API user + result = (udf_file_t *) malloc(sizeof(*result)); + if (!result) return NULL; + + // Copy over the information + memcpy(result, &File, sizeof(*result)); + + result->Partition_Start = partition.Start; + + return result; } Index: src/dvdread/dvd_udf.h =================================================================== --- src/dvdread/dvd_udf.h (revision 1181) +++ src/dvdread/dvd_udf.h (working copy) @@ -39,6 +39,32 @@ extern "C" { #endif + + +/* + * + * UDF defines struct AD, which dvdread unfortunately overloads to refer to + * a file, creating problems with chained ADs. We need to define a generic + * UDF_FILE structure to hold the information required. + * It could be worth a separate struct for Directories, since they are (probably) + * only ever in 1 chain. + * + * Is there a real max number of chains? What can fit in 2048? + * My 4.4GB file example uses 5 chains. A BD ISO can store 50GB of data, so + * potentially we could have a 50GB file, so we would need to be able to + * support 62 chains. + * + */ + +#ifndef UDF_MAX_AD_CHAINS // Allow MAX to be increased by API. +#define UDF_MAX_AD_CHAINS 50 +#endif + + +typedef struct UDF_FILE udf_file_t; + + + /** * 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,8 +72,9 @@ * '/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_t *UDFFindFile( dvd_reader_t *device, char *filename, uint32_t *size ); +void UDFFreeFile( dvd_reader_t *device, udf_file_t *udf_file ); +uint32_t UDFFileBlockPos( udf_file_t *udf_file, uint32_t file_block); void FreeUDFCache(void *cache); int UDFGetVolumeIdentifier(dvd_reader_t *device, char *volid, unsigned int volid_size);