From 75e28d06f8b7678bb68699c568051f897a979f99 Mon Sep 17 00:00:00 2001 From: arpi Date: Fri, 16 Aug 2002 22:37:48 +0000 Subject: importing libdvdread 0.9.3 files git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@7030 b3059339-0415-0410-9bf9-f77b7e298cf2 --- libmpdvdkit2/bswap.h | 87 +++ libmpdvdkit2/dvd_input.c | 318 ++++++++ libmpdvdkit2/dvd_input.h | 51 ++ libmpdvdkit2/dvd_reader.c | 898 ++++++++++++++++++++++ libmpdvdkit2/dvd_reader.h | 151 ++++ libmpdvdkit2/dvd_udf.c | 494 +++++++++++++ libmpdvdkit2/dvd_udf.h | 53 ++ libmpdvdkit2/ifo_print.c | 1049 ++++++++++++++++++++++++++ libmpdvdkit2/ifo_print.h | 58 ++ libmpdvdkit2/ifo_read.c | 1798 +++++++++++++++++++++++++++++++++++++++++++++ libmpdvdkit2/ifo_read.h | 217 ++++++ libmpdvdkit2/ifo_types.h | 747 +++++++++++++++++++ libmpdvdkit2/nav_print.c | 279 +++++++ libmpdvdkit2/nav_print.h | 42 ++ libmpdvdkit2/nav_read.c | 186 +++++ libmpdvdkit2/nav_read.h | 41 ++ libmpdvdkit2/nav_types.h | 288 ++++++++ 17 files changed, 6757 insertions(+) create mode 100644 libmpdvdkit2/bswap.h create mode 100644 libmpdvdkit2/dvd_input.c create mode 100644 libmpdvdkit2/dvd_input.h create mode 100644 libmpdvdkit2/dvd_reader.c create mode 100644 libmpdvdkit2/dvd_reader.h create mode 100644 libmpdvdkit2/dvd_udf.c create mode 100644 libmpdvdkit2/dvd_udf.h create mode 100644 libmpdvdkit2/ifo_print.c create mode 100644 libmpdvdkit2/ifo_print.h create mode 100644 libmpdvdkit2/ifo_read.c create mode 100644 libmpdvdkit2/ifo_read.h create mode 100644 libmpdvdkit2/ifo_types.h create mode 100644 libmpdvdkit2/nav_print.c create mode 100644 libmpdvdkit2/nav_print.h create mode 100644 libmpdvdkit2/nav_read.c create mode 100644 libmpdvdkit2/nav_read.h create mode 100644 libmpdvdkit2/nav_types.h diff --git a/libmpdvdkit2/bswap.h b/libmpdvdkit2/bswap.h new file mode 100644 index 0000000000..dd7417059a --- /dev/null +++ b/libmpdvdkit2/bswap.h @@ -0,0 +1,87 @@ +#ifndef BSWAP_H_INCLUDED +#define BSWAP_H_INCLUDED + +/* + * Copyright (C) 2000, 2001 Billy Biggs , + * Håkan Hjort + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#if defined(WORDS_BIGENDIAN) +/* All bigendian systems are fine, just ignore the swaps. */ +#define B2N_16(x) (void)(x) +#define B2N_32(x) (void)(x) +#define B2N_64(x) (void)(x) + +#else + +#if defined(__linux__) +#include +#define B2N_16(x) x = bswap_16(x) +#define B2N_32(x) x = bswap_32(x) +#define B2N_64(x) x = bswap_64(x) + +#elif defined(__NetBSD__) +#include +#define B2N_16(x) BE16TOH(x) +#define B2N_32(x) BE32TOH(x) +#define B2N_64(x) BE64TOH(x) + +#elif defined(__OpenBSD__) +#include +#define B2N_16(x) x = swap16(x) +#define B2N_32(x) x = swap32(x) +#define B2N_64(x) x = swap64(x) + +/* This is a slow but portable implementation, it has multiple evaluation + * problems so beware. + * FreeBSD and Solaris don't have or any other such + * functionality! + */ + +#elif defined(__FreeBSD__) || defined(__sun) || defined(__bsdi__) +#define B2N_16(x) \ + x = ((((x) & 0xff00) >> 8) | \ + (((x) & 0x00ff) << 8)) +#define B2N_32(x) \ + x = ((((x) & 0xff000000) >> 24) | \ + (((x) & 0x00ff0000) >> 8) | \ + (((x) & 0x0000ff00) << 8) | \ + (((x) & 0x000000ff) << 24)) +#define B2N_64(x) \ + x = ((((x) & 0xff00000000000000) >> 56) | \ + (((x) & 0x00ff000000000000) >> 40) | \ + (((x) & 0x0000ff0000000000) >> 24) | \ + (((x) & 0x000000ff00000000) >> 8) | \ + (((x) & 0x00000000ff000000) << 8) | \ + (((x) & 0x0000000000ff0000) << 24) | \ + (((x) & 0x000000000000ff00) << 40) | \ + (((x) & 0x00000000000000ff) << 56)) + +#else + +/* If there isn't a header provided with your system with this functionality + * add the relevant || define( ) to the portable implementation above. + */ +#error "You need to add endian swap macros for you're system" + +#endif + +#endif /* WORDS_BIGENDIAN */ + +#endif /* BSWAP_H_INCLUDED */ diff --git a/libmpdvdkit2/dvd_input.c b/libmpdvdkit2/dvd_input.c new file mode 100644 index 0000000000..c2d788fdb2 --- /dev/null +++ b/libmpdvdkit2/dvd_input.c @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2002 Samuel Hocevar , + * Håkan Hjort + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +#include +#include +#include +#include +#include + +#include "dvd_reader.h" +#include "dvd_input.h" + +/* For libdvdcss */ +typedef struct dvdcss_s *dvdcss_handle; + +dvdcss_handle (*DVDcss_open) (const char *); +int (*DVDcss_close) (dvdcss_handle); +int (*DVDcss_seek) (dvdcss_handle, int, int); +int (*DVDcss_title) (dvdcss_handle, int); +int (*DVDcss_read) (dvdcss_handle, void *, int, int); +char * (*DVDcss_error) (dvdcss_handle); + + +/* The DVDinput handle, add stuff here for new input methods. */ +struct dvd_input_s { + /* libdvdcss handle */ + dvdcss_handle dvdcss; + + /* dummy file input */ + int fd; +}; + + +/** + * initialize and open a DVD device or file. + */ +static dvd_input_t css_open(const char *target) +{ + dvd_input_t dev; + + /* Allocate the handle structure */ + dev = (dvd_input_t) malloc(sizeof(dvd_input_t)); + if(dev == NULL) { + fprintf(stderr, "libdvdread: Could not allocate memory.\n"); + return NULL; + } + + /* Really open it with libdvdcss */ + dev->dvdcss = DVDcss_open(target); + if(dev->dvdcss == 0) { + fprintf(stderr, "libdvdread: Could not open device with libdvdcss.\n"); + free(dev); + return NULL; + } + + return dev; +} + +/** + * return the last error message + */ +static char *css_error(dvd_input_t dev) +{ + return DVDcss_error(dev->dvdcss); +} + +/** + * seek into the device. + */ +static int css_seek(dvd_input_t dev, int blocks, int flags) +{ + return DVDcss_seek(dev->dvdcss, blocks, flags); +} + +/** + * set the block for the begining of a new title (key). + */ +static int css_title(dvd_input_t dev, int block) +{ + return DVDcss_title(dev->dvdcss, block); +} + +/** + * read data from the device. + */ +static int css_read(dvd_input_t dev, void *buffer, int blocks, int flags) +{ + return DVDcss_read(dev->dvdcss, buffer, blocks, flags); +} + +/** + * close the DVD device and clean up the library. + */ +static int css_close(dvd_input_t dev) +{ + int ret; + + ret = DVDcss_close(dev->dvdcss); + + if(ret < 0) + return ret; + + free(dev); + + return 0; +} + + + + + + +/** + * initialize and open a DVD device or file. + */ +static dvd_input_t file_open(const char *target) +{ + dvd_input_t dev; + + /* Allocate the library structure */ + dev = (dvd_input_t) malloc(sizeof(dvd_input_t)); + if(dev == NULL) { + fprintf(stderr, "libdvdread: Could not allocate memory.\n"); + return NULL; + } + + /* Open the device */ + dev->fd = open(target, O_RDONLY); + if(dev->fd < 0) { + perror("libdvdread: Could not open input"); + free(dev); + return NULL; + } + + return dev; +} + +/** + * return the last error message + */ +static char *file_error(dvd_input_t dev) +{ + /* use strerror(errno)? */ + return "unknown error"; +} + +/** + * seek into the device. + */ +static int file_seek(dvd_input_t dev, int blocks, int flags) +{ + off_t pos; + + pos = lseek(dev->fd, (off_t)blocks * (off_t)DVD_VIDEO_LB_LEN, SEEK_SET); + if(pos < 0) { + return pos; + } + /* assert pos % DVD_VIDEO_LB_LEN == 0 */ + return (int) (pos / DVD_VIDEO_LB_LEN); +} + +/** + * set the block for the begining of a new title (key). + */ +static int file_title(dvd_input_t dev, int block) +{ + return -1; +} + +/** + * read data from the device. + */ +static int file_read(dvd_input_t dev, void *buffer, int blocks, int flags) +{ + size_t len; + ssize_t ret; + + len = (size_t)blocks * DVD_VIDEO_LB_LEN; + + while(len > 0) { + + ret = read(dev->fd, buffer, len); + + if(ret < 0) { + /* 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 ret; + } + + if(ret == 0) { + /* Nothing more to read. Return the whole blocks, if any, that we got. + and adjust the file possition back to the previous block boundary. */ + size_t bytes = (size_t)blocks * DVD_VIDEO_LB_LEN - len; + off_t over_read = -(bytes % DVD_VIDEO_LB_LEN); + /*off_t pos =*/ lseek(dev->fd, over_read, SEEK_CUR); + /* should have pos % 2048 == 0 */ + return (int) (bytes / DVD_VIDEO_LB_LEN); + } + + len -= ret; + } + + return blocks; +} + +/** + * close the DVD device and clean up. + */ +static int file_close(dvd_input_t dev) +{ + int ret; + + ret = close(dev->fd); + + if(ret < 0) + return ret; + + free(dev); + + return 0; +} + + +/** + * Setup read functions with either libdvdcss or minimal DVD access. + */ +int DVDInputSetup(void) +{ + void *dvdcss_library = NULL; + char **dvdcss_version = NULL; + + dvdcss_library = dlopen("libdvdcss.so.2", RTLD_LAZY); + + if(dvdcss_library != NULL) { +#if defined(__OpenBSD__) && !defined(__ELF__) +#define U_S "_" +#else +#define U_S +#endif + DVDcss_open = (dvdcss_handle (*)(const char*)) + dlsym(dvdcss_library, U_S "dvdcss_open"); + DVDcss_close = (int (*)(dvdcss_handle)) + dlsym(dvdcss_library, U_S "dvdcss_close"); + DVDcss_title = (int (*)(dvdcss_handle, int)) + dlsym(dvdcss_library, U_S "dvdcss_title"); + DVDcss_seek = (int (*)(dvdcss_handle, int, int)) + dlsym(dvdcss_library, U_S "dvdcss_seek"); + DVDcss_read = (int (*)(dvdcss_handle, void*, int, int)) + dlsym(dvdcss_library, U_S "dvdcss_read"); + DVDcss_error = (char* (*)(dvdcss_handle)) + dlsym(dvdcss_library, U_S "dvdcss_error"); + + dvdcss_version = (char **)dlsym(dvdcss_library, U_S "dvdcss_interface_2"); + + if(dlsym(dvdcss_library, U_S "dvdcss_crack")) { + fprintf(stderr, + "libdvdread: Old (pre-0.0.2) version of libdvdcss found.\n" + "libdvdread: You should get the latest version from " + "http://www.videolan.org/\n" ); + dlclose(dvdcss_library); + dvdcss_library = NULL; + } else if(!DVDcss_open || !DVDcss_close || !DVDcss_title || !DVDcss_seek + || !DVDcss_read || !DVDcss_error || !dvdcss_version) { + fprintf(stderr, "libdvdread: Missing symbols in libdvdcss.so.2, " + "this shouldn't happen !\n"); + dlclose(dvdcss_library); + } + } + + if(dvdcss_library != NULL) { + /* + char *psz_method = getenv( "DVDCSS_METHOD" ); + char *psz_verbose = getenv( "DVDCSS_VERBOSE" ); + fprintf(stderr, "DVDCSS_METHOD %s\n", psz_method); + fprintf(stderr, "DVDCSS_VERBOSE %s\n", psz_verbose); + */ + fprintf(stderr, "libdvdread: Using libdvdcss version %s for DVD access\n", + *dvdcss_version); + + /* libdvdcss wraper functions */ + DVDinput_open = css_open; + DVDinput_close = css_close; + DVDinput_seek = css_seek; + DVDinput_title = css_title; + DVDinput_read = css_read; + DVDinput_error = css_error; + return 1; + + } else { + fprintf(stderr, "libdvdread: Encrypted DVD support unavailable.\n"); + + /* libdvdcss replacement functions */ + DVDinput_open = file_open; + DVDinput_close = file_close; + DVDinput_seek = file_seek; + DVDinput_title = file_title; + DVDinput_read = file_read; + DVDinput_error = file_error; + return 0; + } +} diff --git a/libmpdvdkit2/dvd_input.h b/libmpdvdkit2/dvd_input.h new file mode 100644 index 0000000000..638d60aebf --- /dev/null +++ b/libmpdvdkit2/dvd_input.h @@ -0,0 +1,51 @@ +#ifndef DVD_INPUT_H_INCLUDED +#define DVD_INPUT_H_INCLUDED + +/* + * Copyright (C) 2001, 2002 Samuel Hocevar , + * Håkan Hjort + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +/** + * Defines and flags. Make sure they fit the libdvdcss API! + */ +#define DVDINPUT_NOFLAGS 0 + +#define DVDINPUT_READ_DECRYPT (1 << 0) + +#define DVDINPUT_SEEK_MPEG (1 << 0) +#define DVDINPUT_SEEK_KEY (1 << 1) + + +typedef struct dvd_input_s *dvd_input_t; + +/** + * Pointers which will be filled either the input meathods functions. + */ +dvd_input_t (*DVDinput_open) (const char *); +int (*DVDinput_close) (dvd_input_t); +int (*DVDinput_seek) (dvd_input_t, int, int); +int (*DVDinput_title) (dvd_input_t, int); +int (*DVDinput_read) (dvd_input_t, void *, int, int); +char * (*DVDinput_error) (dvd_input_t); + +/** + * Setup function accessed by dvd_reader.c. Returns 1 if there is CSS support. + */ +int DVDInputSetup(void); + +#endif /* DVD_INPUT_H_INCLUDED */ diff --git a/libmpdvdkit2/dvd_reader.c b/libmpdvdkit2/dvd_reader.c new file mode 100644 index 0000000000..b296a49de3 --- /dev/null +++ b/libmpdvdkit2/dvd_reader.c @@ -0,0 +1,898 @@ +/* + * Copyright (C) 2001, 2002 Billy Biggs , + * Håkan Hjort + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +#include +#include +#include /* For the timing of dvdcss_title crack. */ +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__)|| defined(__DARWIN__) +#define SYS_BSD 1 +#endif + +#if defined(__sun) +#include +#elif defined(SYS_BSD) +#include +#elif defined(__linux__) +#include +#endif + +#include "dvd_udf.h" +#include "dvd_input.h" +#include "dvd_reader.h" + +struct dvd_reader_s { + /* Basic information. */ + int isImageFile; + + /* Hack for keeping track of the css status. + * 0: no css, 1: perhaps (need init of keys), 2: have done init */ + int css_state; + int css_title; /* Last title that we have called DVDinpute_title for. */ + + /* Information required for an image file. */ + dvd_input_t dev; + + /* Information required for a directory path drive. */ + char *path_root; +}; + +struct dvd_file_s { + /* Basic information. */ + dvd_reader_t *dvd; + + /* Hack for selecting the right css title. */ + int css_title; + + /* Information required for an image file. */ + uint32_t lb_start; + uint32_t seek_pos; + + /* Information required for a directory path drive. */ + size_t title_sizes[ 9 ]; + dvd_input_t title_devs[ 9 ]; + + /* Calculated at open-time, size in blocks. */ + ssize_t filesize; +}; + +/* Loop over all titles and call dvdcss_title to crack the keys. */ +static int initAllCSSKeys( dvd_reader_t *dvd ) +{ + struct timeval all_s, all_e; + struct timeval t_s, t_e; + char filename[ MAX_UDF_FILE_NAME_LEN ]; + uint32_t start, len; + int title; + + fprintf( stderr, "\n" ); + fprintf( stderr, "libdvdread: Attempting to retrieve all CSS keys\n" ); + fprintf( stderr, "libdvdread: This can take a _long_ time, " + "please be patient\n\n" ); + + gettimeofday(&all_s, NULL); + + for( title = 0; title < 100; title++ ) { + gettimeofday( &t_s, NULL ); + if( title == 0 ) { + sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" ); + } else { + sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 0 ); + } + start = UDFFindFile( dvd, filename, &len ); + if( start != 0 && 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); + } + gettimeofday( &t_e, NULL ); + fprintf( stderr, "libdvdread: Elapsed time %ld\n", + (long int) t_e.tv_sec - t_s.tv_sec ); + } + + 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; + + /* 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); + } + gettimeofday( &t_e, NULL ); + fprintf( stderr, "libdvdread: Elapsed time %ld\n", + (long int) t_e.tv_sec - t_s.tv_sec ); + } + title--; + + fprintf( stderr, "libdvdread: Found %d VTS's\n", title ); + gettimeofday(&all_e, NULL); + fprintf( stderr, "libdvdread: Elapsed time %ld\n", + (long int) all_e.tv_sec - all_s.tv_sec ); + + return 0; +} + + + +/** + * Open a DVD image or block device file. + */ +static dvd_reader_t *DVDOpenImageFile( const char *location, int have_css ) +{ + dvd_reader_t *dvd; + dvd_input_t dev; + + dev = DVDinput_open( location ); + if( !dev ) { + fprintf( stderr, "libdvdread: Can't open %s for reading\n", location ); + return 0; + } + + dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) ); + if( !dvd ) return 0; + dvd->isImageFile = 1; + dvd->dev = dev; + dvd->path_root = 0; + + if( have_css ) { + /* Only if DVDCSS_METHOD = title, a bit if it's disc or if + * DVDCSS_METHOD = key but region missmatch. Unfortunaly we + * don't have that information. */ + + dvd->css_state = 1; /* Need key init. */ + } + + return dvd; +} + +static dvd_reader_t *DVDOpenPath( const char *path_root ) +{ + dvd_reader_t *dvd; + + dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) ); + if( !dvd ) return 0; + dvd->isImageFile = 0; + dvd->dev = 0; + dvd->path_root = strdup( path_root ); + + return dvd; +} + +#if defined(__sun) +/* /dev/rdsk/c0t6d0s0 (link to /devices/...) + /vol/dev/rdsk/c0t6d0/?? + /vol/rdsk/ */ +static char *sun_block2char( const char *path ) +{ + char *new_path; + + /* Must contain "/dsk/" */ + if( !strstr( path, "/dsk/" ) ) return (char *) strdup( path ); + + /* Replace "/dsk/" with "/rdsk/" */ + new_path = malloc( strlen(path) + 2 ); + strcpy( new_path, path ); + strcpy( strstr( new_path, "/dsk/" ), "" ); + strcat( new_path, "/rdsk/" ); + strcat( new_path, strstr( path, "/dsk/" ) + strlen( "/dsk/" ) ); + + return new_path; +} +#endif + +#if defined(SYS_BSD) +/* FreeBSD /dev/(r)(a)cd0c (a is for atapi), recomended to _not_ use r + OpenBSD /dev/rcd0c, it needs to be the raw device + NetBSD /dev/rcd0[d|c|..] d for x86, c (for non x86), perhaps others + Darwin /dev/rdisk0, it needs to be the raw device + BSD/OS /dev/sr0c (if not mounted) or /dev/rsr0c ('c' any letter will do) */ +static char *bsd_block2char( const char *path ) +{ + char *new_path; + + /* If it doesn't start with "/dev/" or does start with "/dev/r" exit */ + if( !strncmp( path, "/dev/", 5 ) || strncmp( path, "/dev/r", 6 ) ) + return (char *) strdup( path ); + + /* Replace "/dev/" with "/dev/r" */ + new_path = malloc( strlen(path) + 2 ); + strcpy( new_path, "/dev/r" ); + strcat( new_path, path + strlen( "/dev/" ) ); + + return new_path; +} +#endif + +dvd_reader_t *DVDOpen( const char *path ) +{ + struct stat fileinfo; + int ret, have_css; + char *dev_name = 0; + + if( !path ) return 0; + + ret = stat( path, &fileinfo ); + if( ret < 0 ) { + /* If we can't stat the file, give up */ + fprintf( stderr, "libdvdread: Can't stat %s\n", path ); + perror(""); + return 0; + } + + /* Try to open libdvdcss or fall back to standard functions */ + have_css = DVDInputSetup(); + + /* First check if this is a block/char device or a file*/ + if( S_ISBLK( fileinfo.st_mode ) || + S_ISCHR( fileinfo.st_mode ) || + S_ISREG( fileinfo.st_mode ) ) { + + /** + * Block devices and regular files are assumed to be DVD-Video images. + */ +#if defined(__sun) + return DVDOpenImageFile( sun_block2char( path ), have_css ); +#elif defined(SYS_BSD) + return DVDOpenImageFile( bsd_block2char( path ), have_css ); +#else + return DVDOpenImageFile( path, have_css ); +#endif + + } else if( S_ISDIR( fileinfo.st_mode ) ) { + dvd_reader_t *auth_drive = 0; + char *path_copy; +#if defined(SYS_BSD) + struct fstab* fe; +#elif defined(__sun) || defined(__linux__) + FILE *mntfile; +#endif + + /* XXX: We should scream real loud here. */ + if( !(path_copy = strdup( path ) ) ) return 0; + + /* Resolve any symlinks and get the absolut dir name. */ + { + char *new_path; + int cdir = open( ".", O_RDONLY ); + + if( cdir >= 0 ) { + chdir( path_copy ); + new_path = getcwd( NULL, PATH_MAX ); + fchdir( cdir ); + close( cdir ); + if( new_path ) { + free( path_copy ); + path_copy = new_path; + } + } + } + + /** + * If we're being asked to open a directory, check if that directory + * is the mountpoint for a DVD-ROM which we can use instead. + */ + + if( strlen( path_copy ) > 1 ) { + if( path_copy[ strlen( path_copy ) - 1 ] == '/' ) + path_copy[ strlen( path_copy ) - 1 ] = '\0'; + } + + if( strlen( path_copy ) > 9 ) { + if( !strcasecmp( &(path_copy[ strlen( path_copy ) - 9 ]), + "/video_ts" ) ) { + path_copy[ strlen( path_copy ) - 9 ] = '\0'; + } + } + +#if defined(SYS_BSD) + if( ( fe = getfsfile( path_copy ) ) ) { + dev_name = bsd_block2char( fe->fs_spec ); + fprintf( stderr, + "libdvdread: Attempting to use device %s" + " mounted on %s for CSS authentication\n", + dev_name, + fe->fs_file ); + auth_drive = DVDOpenImageFile( dev_name, have_css ); + } +#elif defined(__sun) + mntfile = fopen( MNTTAB, "r" ); + if( mntfile ) { + struct mnttab mp; + int res; + + while( ( res = getmntent( mntfile, &mp ) ) != -1 ) { + if( res == 0 && !strcmp( mp.mnt_mountp, path_copy ) ) { + dev_name = sun_block2char( mp.mnt_special ); + fprintf( stderr, + "libdvdread: Attempting to use device %s" + " mounted on %s for CSS authentication\n", + dev_name, + mp.mnt_mountp ); + auth_drive = DVDOpenImageFile( dev_name, have_css ); + break; + } + } + fclose( mntfile ); + } +#elif defined(__linux__) + mntfile = fopen( MOUNTED, "r" ); + if( mntfile ) { + struct mntent *me; + + while( ( me = getmntent( mntfile ) ) ) { + if( !strcmp( me->mnt_dir, path_copy ) ) { + fprintf( stderr, + "libdvdread: Attempting to use device %s" + " mounted on %s for CSS authentication\n", + me->mnt_fsname, + me->mnt_dir ); + auth_drive = DVDOpenImageFile( me->mnt_fsname, have_css ); + dev_name = strdup(me->mnt_fsname); + break; + } + } + fclose( mntfile ); + } +#endif + if( !dev_name ) { + fprintf( stderr, "libdvdread: Couldn't find device name.\n" ); + } else if( !auth_drive ) { + fprintf( stderr, "libdvdread: Device %s inaccessible, " + "CSS authentication not available.\n", dev_name ); + } + + free( dev_name ); + free( path_copy ); + + /** + * If we've opened a drive, just use that. + */ + if( auth_drive ) return auth_drive; + + /** + * Otherwise, we now try to open the directory tree instead. + */ + return DVDOpenPath( path ); + } + + /* If it's none of the above, screw it. */ + fprintf( stderr, "libdvdread: Could not open %s\n", path ); + return 0; +} + +void DVDClose( dvd_reader_t *dvd ) +{ + if( dvd ) { + if( dvd->dev ) DVDinput_close( dvd->dev ); + if( dvd->path_root ) free( dvd->path_root ); + free( dvd ); + dvd = 0; + } +} + +/** + * Open an unencrypted file on a DVD image file. + */ +static dvd_file_t *DVDOpenFileUDF( dvd_reader_t *dvd, char *filename ) +{ + uint32_t start, len; + dvd_file_t *dvd_file; + + start = UDFFindFile( dvd, filename, &len ); + if( !start ) return 0; + + dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) ); + if( !dvd_file ) return 0; + dvd_file->dvd = dvd; + dvd_file->lb_start = start; + 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; + + return dvd_file; +} + +/** + * Searches for in directory , ignoring case. + * Returns 0 and full filename in . + * or -1 on file not found. + * or -2 on path not found. + */ +static int findDirFile( const char *path, const char *file, char *filename ) +{ + DIR *dir; + struct dirent *ent; + + dir = opendir( path ); + if( !dir ) return -2; + + while( ( ent = readdir( dir ) ) != NULL ) { + if( !strcasecmp( ent->d_name, file ) ) { + sprintf( filename, "%s%s%s", path, + ( ( path[ strlen( path ) - 1 ] == '/' ) ? "" : "/" ), + ent->d_name ); + return 0; + } + } + + return -1; +} + +static int findDVDFile( dvd_reader_t *dvd, const char *file, char *filename ) +{ + char video_path[ PATH_MAX + 1 ]; + const char *nodirfile; + int ret; + + /* Strip off the directory for our search */ + if( !strncasecmp( "/VIDEO_TS/", file, 10 ) ) { + nodirfile = &(file[ 10 ]); + } else { + nodirfile = file; + } + + ret = findDirFile( dvd->path_root, nodirfile, filename ); + if( ret < 0 ) { + /* Try also with adding the path, just in case. */ + sprintf( video_path, "%s/VIDEO_TS/", dvd->path_root ); + ret = findDirFile( video_path, nodirfile, filename ); + if( ret < 0 ) { + /* Try with the path, but in lower case. */ + sprintf( video_path, "%s/video_ts/", dvd->path_root ); + ret = findDirFile( video_path, nodirfile, filename ); + if( ret < 0 ) { + return 0; + } + } + } + + return 1; +} + +/** + * Open an unencrypted file from a DVD directory tree. + */ +static dvd_file_t *DVDOpenFilePath( dvd_reader_t *dvd, char *filename ) +{ + char full_path[ PATH_MAX + 1 ]; + dvd_file_t *dvd_file; + struct stat fileinfo; + dvd_input_t dev; + + /* Get the full path of the file. */ + if( !findDVDFile( dvd, filename, full_path ) ) return 0; + + dev = DVDinput_open( full_path ); + if( !dev ) return 0; + + dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) ); + if( !dvd_file ) return 0; + dvd_file->dvd = dvd; + dvd_file->lb_start = 0; + 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; + + if( stat( full_path, &fileinfo ) < 0 ) { + fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename ); + free( dvd_file ); + return 0; + } + 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 ]; + + return dvd_file; +} + +static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *dvd, int title, int menu ) +{ + char filename[ MAX_UDF_FILE_NAME_LEN ]; + uint32_t start, len; + dvd_file_t *dvd_file; + + if( title == 0 ) { + sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" ); + } else { + sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 ); + } + start = UDFFindFile( dvd, filename, &len ); + if( start == 0 ) return 0; + + dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) ); + if( !dvd_file ) return 0; + dvd_file->dvd = dvd; + /*Hack*/ dvd_file->css_title = title << 1 | menu; + dvd_file->lb_start = start; + 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; + + /* Calculate the complete file size for every file in the VOBS */ + if( !menu ) { + int cur; + + for( cur = 2; cur < 10; cur++ ) { + sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur ); + if( !UDFFindFile( dvd, filename, &len ) ) break; + dvd_file->filesize += len / DVD_VIDEO_LB_LEN; + } + } + + if( dvd->css_state == 1 /* Need key init */ ) { + initAllCSSKeys( dvd ); + dvd->css_state = 2; + } + /* + if( DVDinput_seek( dvd_file->dvd->dev, + (int)start, DVDINPUT_SEEK_KEY ) < 0 ) { + fprintf( stderr, "libdvdread: Error cracking CSS key for %s\n", + filename ); + } + */ + + return dvd_file; +} + +static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *dvd, int title, int menu ) +{ + char filename[ MAX_UDF_FILE_NAME_LEN ]; + char full_path[ PATH_MAX + 1 ]; + struct stat fileinfo; + dvd_file_t *dvd_file; + int i; + + dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) ); + if( !dvd_file ) return 0; + dvd_file->dvd = dvd; + /*Hack*/ dvd_file->css_title = title << 1 | menu; + dvd_file->lb_start = 0; + 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; + + if( menu ) { + dvd_input_t dev; + + if( title == 0 ) { + sprintf( filename, "VIDEO_TS.VOB" ); + } else { + sprintf( filename, "VTS_%02i_0.VOB", title ); + } + if( !findDVDFile( dvd, filename, full_path ) ) { + free( dvd_file ); + return 0; + } + + dev = DVDinput_open( full_path ); + if( dev == NULL ) { + free( dvd_file ); + return 0; + } + + if( stat( full_path, &fileinfo ) < 0 ) { + fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename ); + free( dvd_file ); + return 0; + } + dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN; + dvd_file->title_devs[ 0 ] = dev; + DVDinput_seek( dvd_file->title_devs[0], 0, DVDINPUT_SEEK_KEY ); + dvd_file->filesize = dvd_file->title_sizes[ 0 ]; + + } else { + for( i = 0; i < 9; ++i ) { + + sprintf( filename, "VTS_%02i_%i.VOB", title, i + 1 ); + if( !findDVDFile( dvd, filename, full_path ) ) { + break; + } + + if( stat( full_path, &fileinfo ) < 0 ) { + fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename ); + break; + } + + dvd_file->title_sizes[ i ] = fileinfo.st_size / DVD_VIDEO_LB_LEN; + dvd_file->title_devs[ i ] = DVDinput_open( full_path ); + dvd_file->filesize += dvd_file->title_sizes[ i ]; + } + if( dvd_file->title_devs[ 0 ] ) { + DVDinput_seek( dvd_file->title_devs[ 0 ], 0, DVDINPUT_SEEK_KEY ); + } else { + free( dvd_file ); + return 0; + } + } + + return dvd_file; +} + +dvd_file_t *DVDOpenFile( dvd_reader_t *dvd, int titlenum, + dvd_read_domain_t domain ) +{ + char filename[ MAX_UDF_FILE_NAME_LEN ]; + + 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 DVDOpenVOBUDF( dvd, titlenum, 1 ); + } else { + return DVDOpenVOBPath( dvd, titlenum, 1 ); + } + break; + case DVD_READ_TITLE_VOBS: + if( titlenum == 0 ) return 0; + if( dvd->isImageFile ) { + return DVDOpenVOBUDF( dvd, titlenum, 0 ); + } else { + return DVDOpenVOBPath( dvd, titlenum, 0 ); + } + break; + default: + fprintf( stderr, "libdvdread: Invalid domain for file open.\n" ); + return 0; + } + + if( dvd->isImageFile ) { + return DVDOpenFileUDF( dvd, filename ); + } else { + return DVDOpenFilePath( dvd, filename ); + } +} + +void DVDCloseFile( dvd_file_t *dvd_file ) +{ + int i; + + if( dvd_file ) { + if( dvd_file->dvd->isImageFile ) { + ; + } else { + for( i = 0; i < 9; ++i ) { + if( dvd_file->title_devs[ i ] ) { + DVDinput_close( dvd_file->title_devs[i] ); + } + } + } + + free( dvd_file ); + dvd_file = 0; + } +} + +/* Internal, but used from dvd_udf.c */ +int DVDReadBlocksUDFRaw( dvd_reader_t *device, uint32_t lb_number, + size_t block_count, unsigned char *data, + int encrypted ) +{ + int ret; + + if( !device->dev ) { + fprintf( stderr, "libdvdread: Fatal error in block read.\n" ); + return 0; + } + + ret = DVDinput_seek( device->dev, (int) lb_number, DVDINPUT_NOFLAGS ); + if( ret != (int) lb_number ) { + fprintf( stderr, "libdvdread: Can't seek to block %u\n", lb_number ); + return 0; + } + + return DVDinput_read( device->dev, (char *) data, + (int) block_count, encrypted ); +} + +/* This is using a single input and starting from 'dvd_file->lb_start' offset. + * + * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset' + * into the buffer located at 'data' and if 'encrypted' is set + * descramble the data if it's encrypted. Returning either an + * negative error or the number of blocks read. */ +static int DVDReadBlocksUDF( dvd_file_t *dvd_file, uint32_t offset, + size_t block_count, unsigned char *data, + int encrypted ) +{ + return DVDReadBlocksUDFRaw( dvd_file->dvd, dvd_file->lb_start + offset, + block_count, data, encrypted ); +} + +/* This is using possibly several inputs and starting from an offset of '0'. + * + * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset' + * into the buffer located at 'data' and if 'encrypted' is set + * descramble the data if it's encrypted. Returning either an + * negative error or the number of blocks read. */ +static int DVDReadBlocksPath( dvd_file_t *dvd_file, unsigned int offset, + size_t block_count, unsigned char *data, + int encrypted ) +{ + int i; + int ret, ret2, off; + + ret = 0; + ret2 = 0; + for( i = 0; i < 9; ++i ) { + if( !dvd_file->title_sizes[ i ] ) return 0; /* Past end of file */ + + if( offset < dvd_file->title_sizes[ i ] ) { + if( ( offset + block_count ) <= dvd_file->title_sizes[ i ] ) { + off = DVDinput_seek( dvd_file->title_devs[ i ], + (int)offset, DVDINPUT_NOFLAGS ); + if( off < 0 || off != (int)offset ) { + fprintf( stderr, "libdvdread: Can't seek to block %d\n", + offset ); + return off < 0 ? off : 0; + } + ret = DVDinput_read( dvd_file->title_devs[ i ], data, + (int)block_count, encrypted ); + break; + } else { + size_t part1_size = dvd_file->title_sizes[ i ] - offset; + /* FIXME: Really needs to be a while loop. + * (This is only true if you try and read >1GB at a time) */ + + /* Read part 1 */ + off = DVDinput_seek( dvd_file->title_devs[ i ], + (int)offset, DVDINPUT_NOFLAGS ); + if( off < 0 || off != (int)offset ) { + fprintf( stderr, "libdvdread: Can't seek to block %d\n", + offset ); + return off < 0 ? off : 0; + } + ret = DVDinput_read( dvd_file->title_devs[ i ], data, + (int)part1_size, encrypted ); + if( ret < 0 ) return ret; + /* FIXME: This is wrong if i is the last file in the set. + * also error from this read will not show in ret. */ + + /* Read part 2 */ + off = DVDinput_seek( dvd_file->title_devs[ i + 1 ], + 0, DVDINPUT_NOFLAGS ); + if( off < 0 || off != 0 ) { + fprintf( stderr, "libdvdread: Can't seek to block %d\n", + 0 ); + return off < 0 ? off : 0; + } + ret2 = DVDinput_read( dvd_file->title_devs[ i + 1 ], + data + ( part1_size + * (int64_t)DVD_VIDEO_LB_LEN ), + (int)(block_count - part1_size), + encrypted ); + if( ret2 < 0 ) return ret2; + break; + } + } else { + offset -= dvd_file->title_sizes[ i ]; + } + } + + return ret + ret2; +} + +/* This is broken reading more than 2Gb at a time is ssize_t is 32-bit. */ +ssize_t DVDReadBlocks( dvd_file_t *dvd_file, int offset, + size_t block_count, unsigned char *data ) +{ + int ret; + + /* Hack, and it will still fail for multiple opens in a threaded app ! */ + 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 ); + } else { + DVDinput_title( dvd_file->title_devs[ 0 ], (int)dvd_file->lb_start ); + } + } + + if( dvd_file->dvd->isImageFile ) { + ret = DVDReadBlocksUDF( dvd_file, (uint32_t)offset, + block_count, data, DVDINPUT_READ_DECRYPT ); + } else { + ret = DVDReadBlocksPath( dvd_file, (unsigned int)offset, + block_count, data, DVDINPUT_READ_DECRYPT ); + } + + return (ssize_t)ret; +} + +int32_t DVDFileSeek( dvd_file_t *dvd_file, int32_t offset ) +{ + if( offset > dvd_file->filesize * DVD_VIDEO_LB_LEN ) { + return -1; + } + dvd_file->seek_pos = (uint32_t) offset; + return offset; +} + +ssize_t DVDReadBytes( dvd_file_t *dvd_file, void *data, size_t byte_size ) +{ + unsigned char *secbuf; + unsigned int numsec, seek_sector, seek_byte; + int ret; + + seek_sector = dvd_file->seek_pos / DVD_VIDEO_LB_LEN; + seek_byte = dvd_file->seek_pos % DVD_VIDEO_LB_LEN; + + numsec = ( ( seek_byte + byte_size ) / DVD_VIDEO_LB_LEN ) + 1; + secbuf = (unsigned char *) malloc( numsec * DVD_VIDEO_LB_LEN ); + if( !secbuf ) { + fprintf( stderr, "libdvdread: Can't allocate memory " + "for file read!\n" ); + return 0; + } + + if( dvd_file->dvd->isImageFile ) { + ret = DVDReadBlocksUDF( dvd_file, (uint32_t) seek_sector, + (size_t) numsec, secbuf, DVDINPUT_NOFLAGS ); + } else { + ret = DVDReadBlocksPath( dvd_file, seek_sector, + (size_t) numsec, secbuf, DVDINPUT_NOFLAGS ); + } + + if( ret != (int) numsec ) { + free( secbuf ); + return ret < 0 ? ret : 0; + } + + memcpy( data, &(secbuf[ seek_byte ]), byte_size ); + free( secbuf ); + + dvd_file->seek_pos += byte_size; + return byte_size; +} + +ssize_t DVDFileSize( dvd_file_t *dvd_file ) +{ + return dvd_file->filesize; +} diff --git a/libmpdvdkit2/dvd_reader.h b/libmpdvdkit2/dvd_reader.h new file mode 100644 index 0000000000..75f7182747 --- /dev/null +++ b/libmpdvdkit2/dvd_reader.h @@ -0,0 +1,151 @@ +#ifndef DVD_READER_H_INCLUDED +#define DVD_READER_H_INCLUDED + +/* + * Copyright (C) 2001, 2002 Billy Biggs , + * Håkan Hjort + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +/** + * The length of one Logical Block of a DVD Video. + */ +#define DVD_VIDEO_LB_LEN 2048 + +/** + * Maximum length of filenames for UDF. + */ +#define MAX_UDF_FILE_NAME_LEN 2048 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct dvd_reader_s dvd_reader_t; +typedef struct dvd_file_s dvd_file_t; + +/** + * dvd = DVDOpen(path); + * + * Opens a block device of a DVD-ROM file, or an image file, or a directory + * name for a mounted DVD or HD copy of a DVD. Returns 0 if we can't get any + * of those methods to work. + * + * If the given file is a block device, or is the mountpoint for a block + * device, then that device is used for CSS authentication using libdvdcss. + * If no device is available, then no CSS authentication is performed, + * and we hope that the image is decrypted. + * + * If the path given is a directory, then the files in that directory may be in + * any one of these formats: + * + * path/VIDEO_TS/VTS_01_1.VOB + * path/video_ts/vts_01_1.vob + * path/VTS_01_1.VOB + * path/vts_01_1.vob + */ +dvd_reader_t *DVDOpen( const char * ); + +/** + * DVDClose(dvd); + * + * Closes and cleans up the DVD reader object. You must close all open files + * before calling this function. + */ +void DVDClose( dvd_reader_t * ); + +/** + * INFO_FILE : VIDEO_TS.IFO (manager) + * VTS_XX_0.IFO (title) + * + * INFO_BACKUP_FILE: VIDEO_TS.BUP (manager) + * VTS_XX_0.BUP (title) + * + * MENU_VOBS : VIDEO_TS.VOB (manager) + * VTS_XX_0.VOB (title) + * + * TITLE_VOBS : VTS_XX_[1-9].VOB (title) + * All files in the title set are opened and + * read as a single file. + */ +typedef enum { + DVD_READ_INFO_FILE, + DVD_READ_INFO_BACKUP_FILE, + DVD_READ_MENU_VOBS, + DVD_READ_TITLE_VOBS +} dvd_read_domain_t; + +/** + * dvd_file = DVDOpenFile(dvd, titlenum, domain); + * + * Opens a file on the DVD given the title number and domain. If the title + * number is 0, the video manager information is opened + * (VIDEO_TS.[IFO,BUP,VOB]). Returns a file structure which may be used for + * reads, or 0 if the file was not found. + */ +dvd_file_t *DVDOpenFile( dvd_reader_t *, int, + dvd_read_domain_t ); + +/** + * DVDCloseFile(dvd_file); + * + * Closes a file and frees the associated structure. + */ +void DVDCloseFile( dvd_file_t * ); + +/** + * blocks_read = DVDReadBlocks(dvd_file, offset, block_count, data); + * + * Reads block_count number of blocks from the file at the given block offset. + * Returns number of blocks read on success, -1 on error. This call is only + * for reading VOB data, and should not be used when reading the IFO files. + * When reading from an encrypted drive, blocks are decrypted using libdvdcss + * where required. + */ +ssize_t DVDReadBlocks( dvd_file_t *, int, size_t, unsigned char * ); + +/** + * offset_set = DVDFileSeek(dvd_file, seek_offset); + * + * Seek to the given position in the file. Returns the resulting position in + * bytes from the beginning of the file. The seek position is only used for + * byte reads from the file, the block read call always reads from the given + * offset. + */ +int DVDFileSeek( dvd_file_t *, int ); + +/** + * bytes_read = DVDReadBytes(dvd_file, data, bytes); + * + * Reads the given number of bytes from the file. This call can only be used + * on the information files, and may not be used for reading from a VOB. This + * reads from and increments the currrent seek position for the file. + */ +ssize_t DVDReadBytes( dvd_file_t *, void *, size_t ); + +/** + * blocks = DVDFileSize(dvd_file); + * + * Returns the file size in blocks. + */ +ssize_t DVDFileSize( dvd_file_t * ); + +#ifdef __cplusplus +}; +#endif +#endif /* DVD_READER_H_INCLUDED */ diff --git a/libmpdvdkit2/dvd_udf.c b/libmpdvdkit2/dvd_udf.c new file mode 100644 index 0000000000..1cd2a0d2fe --- /dev/null +++ b/libmpdvdkit2/dvd_udf.c @@ -0,0 +1,494 @@ +/* + * This code is based on dvdudf by: + * Christian Wolff . + * + * Modifications by: + * Billy Biggs . + * + * dvdudf: parse and read the UDF volume information of a DVD Video + * Copyright (C) 1999 Christian Wolff for convergence integrated media + * GmbH The author can be reached at scarabaeus@convergence.de, the + * project's page is at http://linuxtv.org/dvd/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. Or, point your browser to + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dvd_reader.h" +#include "dvd_udf.h" + +/* Private but located in/shared with dvd_reader.c */ +extern int DVDReadBlocksUDFRaw( dvd_reader_t *device, uint32_t lb_number, + size_t block_count, unsigned char *data, + int encrypted ); + +/* It's required to either fail or deliver all the blocks asked for. */ +static int DVDReadLBUDF( dvd_reader_t *device, uint32_t lb_number, + size_t block_count, unsigned char *data, + int encrypted ) +{ + int ret; + size_t count = block_count; + + while(count > 0) { + + ret = DVDReadBlocksUDFRaw(device, lb_number, count, data, encrypted); + + if(ret <= 0) { + /* One of the reads failed or nothing more to read, too bad. + * We won't even bother returning the reads that went ok. */ + return ret; + } + + count -= (size_t)ret; + lb_number += (uint32_t)ret; + } + + return block_count; +} + + +#ifndef NULL +#define NULL ((void *)0) +#endif + +struct Partition { + int valid; + char VolumeDesc[128]; + uint16_t Flags; + uint16_t Number; + char Contents[32]; + uint32_t AccessType; + uint32_t Start; + uint32_t Length; +}; + +struct AD { + uint32_t Location; + uint32_t Length; + uint8_t Flags; + uint16_t Partition; +}; + +/* For direct data access, LSB first */ +#define GETN1(p) ((uint8_t)data[p]) +#define GETN2(p) ((uint16_t)data[p] | ((uint16_t)data[(p) + 1] << 8)) +#define GETN3(p) ((uint32_t)data[p] | ((uint32_t)data[(p) + 1] << 8) \ + | ((uint32_t)data[(p) + 2] << 16)) +#define GETN4(p) ((uint32_t)data[p] \ + | ((uint32_t)data[(p) + 1] << 8) \ + | ((uint32_t)data[(p) + 2] << 16) \ + | ((uint32_t)data[(p) + 3] << 24)) +/* This is wrong with regard to endianess */ +#define GETN(p, n, target) memcpy(target, &data[p], n) + +static int Unicodedecode( uint8_t *data, int len, char *target ) +{ + int p = 1, i = 0; + + if( ( data[ 0 ] == 8 ) || ( data[ 0 ] == 16 ) ) do { + if( data[ 0 ] == 16 ) p++; /* Ignore MSB of unicode16 */ + if( p < len ) { + target[ i++ ] = data[ p++ ]; + } + } while( p < len ); + + target[ i ] = '\0'; + return 0; +} + +static int UDFDescriptor( uint8_t *data, uint16_t *TagID ) +{ + *TagID = GETN2(0); + // TODO: check CRC 'n stuff + return 0; +} + +static int UDFExtentAD( uint8_t *data, uint32_t *Length, uint32_t *Location ) +{ + *Length = GETN4(0); + *Location = GETN4(4); + return 0; +} + +static int UDFShortAD( uint8_t *data, struct AD *ad, + struct Partition *partition ) +{ + ad->Length = GETN4(0); + ad->Flags = ad->Length >> 30; + ad->Length &= 0x3FFFFFFF; + ad->Location = GETN4(4); + ad->Partition = partition->Number; // use number of current partition + return 0; +} + +static int UDFLongAD( uint8_t *data, struct AD *ad ) +{ + ad->Length = GETN4(0); + ad->Flags = ad->Length >> 30; + ad->Length &= 0x3FFFFFFF; + ad->Location = GETN4(4); + ad->Partition = GETN2(8); + //GETN(10, 6, Use); + return 0; +} + +static int UDFExtAD( uint8_t *data, struct AD *ad ) +{ + ad->Length = GETN4(0); + ad->Flags = ad->Length >> 30; + ad->Length &= 0x3FFFFFFF; + ad->Location = GETN4(12); + ad->Partition = GETN2(16); + //GETN(10, 6, Use); + return 0; +} + +static int UDFICB( uint8_t *data, uint8_t *FileType, uint16_t *Flags ) +{ + *FileType = GETN1(11); + *Flags = GETN2(18); + return 0; +} + + +static int UDFPartition( uint8_t *data, uint16_t *Flags, uint16_t *Number, + char *Contents, uint32_t *Start, uint32_t *Length ) +{ + *Flags = GETN2(20); + *Number = GETN2(22); + GETN(24, 32, Contents); + *Start = GETN4(188); + *Length = GETN4(192); + return 0; +} + +/** + * Reads the volume descriptor and checks the parameters. Returns 0 on OK, 1 + * on error. + */ +static int UDFLogVolume( uint8_t *data, char *VolumeDescriptor ) +{ + uint32_t lbsize, MT_L, N_PM; + Unicodedecode(&data[84], 128, VolumeDescriptor); + lbsize = GETN4(212); // should be 2048 + MT_L = GETN4(264); // should be 6 + N_PM = GETN4(268); // should be 1 + if (lbsize != DVD_VIDEO_LB_LEN) return 1; + return 0; +} + +static int UDFFileEntry( uint8_t *data, uint8_t *FileType, + struct Partition *partition, struct AD *ad ) +{ + uint16_t flags; + uint32_t L_EA, L_AD; + unsigned int p; + + 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 + + L_EA = GETN4( 168 ); + L_AD = GETN4( 172 ); + p = 176 + L_EA; + 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; + } + } + return 0; +} + +static int UDFFileIdentifier( uint8_t *data, uint8_t *FileCharacteristics, + char *FileName, struct AD *FileICB ) +{ + uint8_t L_FI; + uint16_t L_IU; + + *FileCharacteristics = GETN1(18); + L_FI = GETN1(19); + UDFLongAD(&data[20], FileICB); + L_IU = GETN2(36); + if (L_FI) Unicodedecode(&data[38 + L_IU], L_FI, FileName); + else FileName[0] = '\0'; + return 4 * ((38 + L_FI + L_IU + 3) / 4); +} + +/** + * Maps ICB to FileAD + * ICB: Location of ICB of directory to scan + * FileType: Type of the file + * File: Location of file the ICB is pointing to + * 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 ) +{ + uint8_t LogBlock[DVD_VIDEO_LB_LEN]; + uint32_t lbnum; + uint16_t TagID; + + lbnum = partition->Start + ICB.Location; + do { + if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) { + TagID = 0; + } else { + UDFDescriptor( LogBlock, &TagID ); + } + + if( TagID == 261 ) { + UDFFileEntry( LogBlock, FileType, partition, File ); + return 1; + }; + } while( ( lbnum <= partition->Start + ICB.Location + ( ICB.Length - 1 ) + / DVD_VIDEO_LB_LEN ) && ( TagID != 261 ) ); + + return 0; +} + +/** + * 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; + */ +static int UDFScanDir( dvd_reader_t *device, struct AD Dir, char *FileName, + struct Partition *partition, struct AD *FileICB ) +{ + char filename[ MAX_UDF_FILE_NAME_LEN ]; + uint8_t directory[ 2 * DVD_VIDEO_LB_LEN ]; + uint32_t lbnum; + uint16_t TagID; + uint8_t filechar; + unsigned int p; + + /* Scan dir for ICB of file */ + lbnum = partition->Start + Dir.Location; + + if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) { + return 0; + } + + p = 0; + while( p < Dir.Length ) { + if( p > DVD_VIDEO_LB_LEN ) { + ++lbnum; + p -= DVD_VIDEO_LB_LEN; + Dir.Length -= DVD_VIDEO_LB_LEN; + if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) { + return 0; + } + } + UDFDescriptor( &directory[ p ], &TagID ); + if( TagID == 257 ) { + p += UDFFileIdentifier( &directory[ p ], &filechar, + filename, FileICB ); + if( !strcasecmp( FileName, filename ) ) { + return 1; + } + } else { + return 0; + } + } + + return 0; +} + +/** + * Looks for partition on the disc. Returns 1 if partition found, 0 on error. + * partnum: Number of the partition, starting at 0. + * part: structure to fill with the partition information + */ +static int UDFFindPartition( dvd_reader_t *device, int partnum, + struct Partition *part ) +{ + uint8_t LogBlock[ DVD_VIDEO_LB_LEN ], Anchor[ DVD_VIDEO_LB_LEN ]; + uint32_t lbnum, MVDS_location, MVDS_length; + uint16_t TagID; + uint32_t lastsector; + int i, terminate, volvalid; + + /* Find Anchor */ + lastsector = 0; + lbnum = 256; /* Try #1, prime anchor */ + terminate = 0; + + for(;;) { + if( DVDReadLBUDF( device, lbnum, 1, Anchor, 0 ) > 0 ) { + UDFDescriptor( Anchor, &TagID ); + } else { + TagID = 0; + } + if (TagID != 2) { + /* Not an anchor */ + if( terminate ) return 0; /* Final try failed */ + + if( lastsector ) { + + /* We already found the last sector. Try #3, alternative + * backup anchor. If that fails, don't try again. + */ + lbnum = lastsector; + terminate = 1; + } else { + /* TODO: Find last sector of the disc (this is optional). */ + if( lastsector ) { + /* Try #2, backup anchor */ + lbnum = lastsector - 256; + } else { + /* Unable to find last sector */ + return 0; + } + } + } else { + /* It's an anchor! We can leave */ + break; + } + } + /* Main volume descriptor */ + UDFExtentAD( &Anchor[ 16 ], &MVDS_length, &MVDS_location ); + + part->valid = 0; + volvalid = 0; + part->VolumeDesc[ 0 ] = '\0'; + i = 1; + do { + /* Find Volume Descriptor */ + lbnum = MVDS_location; + do { + + if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) { + TagID = 0; + } else { + UDFDescriptor( LogBlock, &TagID ); + } + + if( ( TagID == 5 ) && ( !part->valid ) ) { + /* Partition Descriptor */ + UDFPartition( LogBlock, &part->Flags, &part->Number, + part->Contents, &part->Start, &part->Length ); + part->valid = ( partnum == part->Number ); + } else if( ( TagID == 6 ) && ( !volvalid ) ) { + /* Logical Volume Descriptor */ + if( UDFLogVolume( LogBlock, part->VolumeDesc ) ) { + /* TODO: sector size wrong! */ + } else { + volvalid = 1; + } + } + + } while( ( lbnum <= MVDS_location + ( MVDS_length - 1 ) + / DVD_VIDEO_LB_LEN ) && ( TagID != 8 ) + && ( ( !part->valid ) || ( !volvalid ) ) ); + + if( ( !part->valid) || ( !volvalid ) ) { + /* Backup volume descriptor */ + UDFExtentAD( &Anchor[ 24 ], &MVDS_length, &MVDS_location ); + } + } while( i-- && ( ( !part->valid ) || ( !volvalid ) ) ); + + /* We only care for the partition, not the volume */ + return part->valid; +} + +uint32_t UDFFindFile( dvd_reader_t *device, char *filename, + uint32_t *filesize ) +{ + uint8_t LogBlock[ DVD_VIDEO_LB_LEN ]; + uint32_t lbnum; + uint16_t TagID; + struct Partition partition; + struct AD RootICB, File, ICB; + char tokenline[ MAX_UDF_FILE_NAME_LEN ]; + char *token; + uint8_t filetype; + + *filesize = 0; + tokenline[0] = '\0'; + strcat( tokenline, filename ); + + /* Find partition, 0 is the standard location for DVD Video.*/ + if( !UDFFindPartition( device, 0, &partition ) ) return 0; + + /* Find root dir ICB */ + lbnum = partition.Start; + do { + if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) { + TagID = 0; + } else { + UDFDescriptor( LogBlock, &TagID ); + } + + /* File Set Descriptor */ + if( TagID == 256 ) { // File Set Descriptor + UDFLongAD( &LogBlock[ 400 ], &RootICB ); + } + } while( ( lbnum < partition.Start + partition.Length ) + && ( TagID != 8 ) && ( TagID != 256 ) ); + + /* Sanity checks. */ + if( TagID != 256 ) return 0; + if( RootICB.Partition != 0 ) return 0; + + /* Find root dir */ + if( !UDFMapICB( device, RootICB, &filetype, &partition, &File ) ) return 0; + if( filetype != 4 ) return 0; /* Root dir should be dir */ + + /* Tokenize filepath */ + token = strtok(tokenline, "/"); + while( token != NULL ) { + if( !UDFScanDir( device, File, token, &partition, &ICB ) ) return 0; + if( !UDFMapICB( device, ICB, &filetype, &partition, &File ) ) return 0; + token = strtok( NULL, "/" ); + } + + /* Sanity check. */ + if( File.Partition != 0 ) return 0; + + *filesize = File.Length; + /* Hack to not return partition.Start for empty files. */ + if( !File.Location ) + return 0; + else + return partition.Start + File.Location; +} diff --git a/libmpdvdkit2/dvd_udf.h b/libmpdvdkit2/dvd_udf.h new file mode 100644 index 0000000000..960ccf4055 --- /dev/null +++ b/libmpdvdkit2/dvd_udf.h @@ -0,0 +1,53 @@ +#ifndef DVD_UDF_H_INCLUDED +#define DVD_UDF_H_INCLUDED + +/* + * This code is based on dvdudf by: + * Christian Wolff . + * + * Modifications by: + * Billy Biggs . + * + * dvdudf: parse and read the UDF volume information of a DVD Video + * Copyright (C) 1999 Christian Wolff for convergence integrated media + * GmbH The author can be reached at scarabaeus@convergence.de, the + * project's page is at http://linuxtv.org/dvd/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. Or, point your browser to + * http://www.gnu.org/copyleft/gpl.html + */ + +#include + +#include "dvd_reader.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * 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 + * absolute pathname on the UDF filesystem, starting with '/'. For example, + * '/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 ); + +#ifdef __cplusplus +}; +#endif +#endif /* DVD_UDF_H_INCLUDED */ diff --git a/libmpdvdkit2/ifo_print.c b/libmpdvdkit2/ifo_print.c new file mode 100644 index 0000000000..716f6897f7 --- /dev/null +++ b/libmpdvdkit2/ifo_print.c @@ -0,0 +1,1049 @@ +/* + * Copyright (C) 2000, 2001, 2002 Björn Englund , + * Håkan Hjort + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even t