[Mplayer-cvslog] CVS: main/libmpdvdkit2 bswap.h,NONE,1.1 dvd_input.c,NONE,1.1 dvd_input.h,NONE,1.1 dvd_reader.c,NONE,1.1 dvd_reader.h,NONE,1.1 dvd_udf.c,NONE,1.1 dvd_udf.h,NONE,1.1 ifo_print.c,NONE,1.1 ifo_print.h,NONE,1.1 ifo_read.c,NONE,1.1 ifo_read.h,NONE,1.1 ifo_types.h,NONE,1.1 nav_print.c,NONE,1.1 nav_print.h,NONE,1.1 nav_read.c,NONE,1.1 nav_read.h,NONE,1.1 nav_types.h,NONE,1.1

Arpi of Ize arpi at mplayerhq.hu
Sat Aug 17 00:37:51 CEST 2002


Update of /cvsroot/mplayer/main/libmpdvdkit2
In directory mail:/var/tmp.root/cvs-serv17341

Added Files:
	bswap.h dvd_input.c dvd_input.h dvd_reader.c dvd_reader.h 
	dvd_udf.c dvd_udf.h ifo_print.c ifo_print.h ifo_read.c 
	ifo_read.h ifo_types.h nav_print.c nav_print.h nav_read.c 
	nav_read.h nav_types.h 
Log Message:
importing libdvdread 0.9.3 files


--- NEW FILE ---
#ifndef BSWAP_H_INCLUDED
#define BSWAP_H_INCLUDED

/*
 * Copyright (C) 2000, 2001 Billy Biggs <vektor at dumbterm.net>,
 *                          Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * 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 <config.h>

#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 <byteswap.h>
#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 <sys/endian.h>
#define B2N_16(x) BE16TOH(x)
#define B2N_32(x) BE32TOH(x)
#define B2N_64(x) BE64TOH(x)

#elif defined(__OpenBSD__)
#include <sys/endian.h>
#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 <byteswap.h> 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 */

--- NEW FILE ---
/*
 * Copyright (C) 2002 Samuel Hocevar <sam at zoy.org>,
 *                    Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * 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 <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <dlfcn.h>

#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;
  }
}

--- NEW FILE ---
#ifndef DVD_INPUT_H_INCLUDED
#define DVD_INPUT_H_INCLUDED

/*
 * Copyright (C) 2001, 2002 Samuel Hocevar <sam at zoy.org>,
 *                          Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * 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 */

--- NEW FILE ---
/*
 * Copyright (C) 2001, 2002 Billy Biggs <vektor at dumbterm.net>,
 *                          Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * 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 <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h> /* For the timing of dvdcss_title crack. */
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <dirent.h>
 
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__)|| defined(__DARWIN__)
#define SYS_BSD 1
#endif

#if defined(__sun)
#include <sys/mnttab.h>
#elif defined(SYS_BSD)
#include <fstab.h>
#elif defined(__linux__)
#include <mntent.h>
#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/<name> */
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 <file> in directory <path>, ignoring case.
 * Returns 0 and full filename in <filename>.
 *     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;
}

--- NEW FILE ---
#ifndef DVD_READER_H_INCLUDED
#define DVD_READER_H_INCLUDED

/*
 * Copyright (C) 2001, 2002 Billy Biggs <vektor at dumbterm.net>,
 *                          Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * 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 <sys/types.h>

/**
 * 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 */

--- NEW FILE ---
/*
 * This code is based on dvdudf by:
 *   Christian Wolff <scarabaeus at convergence.de>.
 *
 * Modifications by:
 *   Billy Biggs <vektor at dumbterm.net>.
 *
 * 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 at 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <inttypes.h>

#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;
}

--- NEW FILE ---
#ifndef DVD_UDF_H_INCLUDED
#define DVD_UDF_H_INCLUDED

/*
 * This code is based on dvdudf by:
 *   Christian Wolff <scarabaeus at convergence.de>.
 *
 * Modifications by:
 *   Billy Biggs <vektor at dumbterm.net>.
 *
 * 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 at 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 <inttypes.h>

#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 */

--- NEW FILE ---
/* 
 * Copyright (C) 2000, 2001, 2002 Björn Englund <d4bjorn at dtek.chalmers.se>, 
 *                                Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * 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
 */

[...1010 lines suppressed...]
    printf("\nVideo Title Set Menu VOBU address map\n");
    printf(  "-----------------\n");
    if(ifohandle->menu_vobu_admap) {
      ifoPrint_VOBU_ADMAP(ifohandle->menu_vobu_admap);
    } else {
      printf("No Menu VOBU address map present\n");
    }

    printf("\nCell Adress table\n");
    printf(  "-----------------\n");
    ifoPrint_C_ADT(ifohandle->vts_c_adt);

    printf("\nVideo Title Set VOBU address map\n");
    printf(  "-----------------\n");
    ifoPrint_VOBU_ADMAP(ifohandle->vts_vobu_admap);
  } 

  ifoClose(ifohandle);
}


--- NEW FILE ---
#ifndef IFO_PRINT_H_INCLUDED
#define IFO_PRINT_H_INCLUDED

/*
 * Copyright (C) 2000, 2001 Björn Englund <d4bjorn at dtek.chalmers.se>,
 *                          Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * 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 <dvdread/ifo_types.h>
#include <dvdread/dvd_reader.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * This file provides example functions for printing information about the IFO
 * file to stdout.
 */

/**
 * Print the complete parsing information for the given file.
 */

/* ifoPrint(dvd, title); */
void ifoPrint(dvd_reader_t *, int);

void ifoPrint_VMGI_MAT(vmgi_mat_t *);
void ifoPrint_VTSI_MAT(vtsi_mat_t *);

void ifoPrint_PTL_MAIT(ptl_mait_t *);
void ifoPrint_VTS_ATRT(vts_atrt_t *);
void ifoPrint_TT_SRPT(tt_srpt_t *);
void ifoPrint_VTS_PTT_SRPT(vts_ptt_srpt_t *);
void ifoPrint_PGC(pgc_t *);
void ifoPrint_PGCIT(pgcit_t *);
void ifoPrint_PGCI_UT(pgci_ut_t *);
void ifoPrint_C_ADT(c_adt_t *);
void ifoPrint_VOBU_ADMAP(vobu_admap_t *);

#ifdef __cplusplus
};
#endif
#endif /* IFO_PRINT_H_INCLUDED */

--- NEW FILE ---
/*
 * Copyright (C) 2000, 2001, 2002  Björn Englund <d4bjorn at dtek.chalmers.se>, 
 *                                 Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * 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
 */

[...1759 lines suppressed...]
    fprintf(stderr, "libdvdread: Unable to read TXTDT_MGI.\n");
    free(txtdt_mgi);
    ifofile->txtdt_mgi = 0;
    return 0;
  }

  // fprintf(stderr, "-- Not done yet --\n");
  return 1;
}

void ifoFree_TXTDT_MGI(ifo_handle_t *ifofile) {
  if(!ifofile)
    return;
  
  if(ifofile->txtdt_mgi) {
    free(ifofile->txtdt_mgi);
    ifofile->txtdt_mgi = 0;
  }
}


--- NEW FILE ---
#ifndef IFO_READ_H_INCLUDED
#define IFO_READ_H_INCLUDED

/*
 * Copyright (C) 2000, 2001, 2002 Björn Englund <d4bjorn at dtek.chalmers.se>,
 *                                Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * 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 <dvdread/ifo_types.h>
#include <dvdread/dvd_reader.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * handle = ifoOpen(dvd, title);
 *
 * Opens an IFO and reads in all the data for the IFO file corresponding to the
 * given title.  If title 0 is given, the video manager IFO file is read.
 * Returns a handle to a completely parsed structure.
 */
ifo_handle_t *ifoOpen(dvd_reader_t *, int );

/**
 * handle = ifoOpenVMGI(dvd);
 *
 * Opens an IFO and reads in _only_ the vmgi_mat data.  This call can be used
 * together with the calls below to read in each segment of the IFO file on
 * demand.
 */
ifo_handle_t *ifoOpenVMGI(dvd_reader_t *);

/**
 * handle = ifoOpenVTSI(dvd, title);
 *
 * Opens an IFO and reads in _only_ the vtsi_mat data.  This call can be used
 * together with the calls below to read in each segment of the IFO file on
 * demand.
 */
ifo_handle_t *ifoOpenVTSI(dvd_reader_t *, int);

/**
 * ifoClose(ifofile);
 * Cleans up the IFO information.  This will free all data allocated for the
 * substructures.
 */
void ifoClose(ifo_handle_t *);

/**
 * The following functions are for reading only part of the VMGI/VTSI files.
 * Returns 1 if the data was successfully read and 0 on error.
 */

/**
 * okay = ifoRead_PLT_MAIT(ifofile);
 *
 * Read in the Parental Management Information table, filling the
 * ifofile->ptl_mait structure and its substructures.  This data is only
 * located in the video manager information file.  This fills the
 * ifofile->ptl_mait structure and all its substructures.
 */
int ifoRead_PTL_MAIT(ifo_handle_t *);

/**
 * okay = ifoRead_VTS_ATRT(ifofile);
 *
 * Read in the attribute table for the main menu vob, filling the
 * ifofile->vts_atrt structure and its substructures.  Only located in the
 * video manager information file.  This fills in the ifofile->vts_atrt
 * structure and all its substructures.
 */
int ifoRead_VTS_ATRT(ifo_handle_t *);

/**
 * okay = ifoRead_TT_SRPT(ifofile);
 *
 * Reads the title info for the main menu, filling the ifofile->tt_srpt
 * structure and its substructures.  This data is only located in the video
 * manager information file.  This structure is mandatory in the IFO file.
 */
int ifoRead_TT_SRPT(ifo_handle_t *);

/**
 * okay = ifoRead_VTS_PTT_SRPT(ifofile);
 *
 * Reads in the part of title search pointer table, filling the
 * ifofile->vts_ptt_srpt structure and its substructures.  This data is only
 * located in the video title set information file.  This structure is
 * mandatory, and must be included in the VTSI file.
 */
int ifoRead_VTS_PTT_SRPT(ifo_handle_t *);

/**
 * okay = ifoRead_FP_PGC(ifofile);
 *
 * Reads in the first play program chain data, filling the
 * ifofile->first_play_pgc structure.  This data is only located in the video
 * manager information file.  This structure is mandatory, and must be included
 * in the VMGI file. **Possibly this is only optional.**
 */
int ifoRead_FP_PGC(ifo_handle_t *);

/**
 * okay = ifoRead_PGCIT(ifofile);
 *
 * Reads in the program chain information table for the video title set.  Fills
 * in the ifofile->vts_pgcit structure and its substructures, which includes
 * the data for each program chain in the set.  This data is only located in
 * the video title set information file.  This structure is mandatory, and must
 * be included in the VTSI file.
 */
int ifoRead_PGCIT(ifo_handle_t *);

/**
 * okay = ifoRead_PGCI_UT(ifofile);
 *
 * Reads in the menu PGCI unit table for the menu VOB.  For the video manager,
 * this corresponds to the VIDEO_TS.VOB file, and for each title set, this
 * corresponds to the VTS_XX_0.VOB file.  This data is located in both the
 * video manager and video title set information files.  For VMGI files, this
 * fills the ifofile->vmgi_pgci_ut structure and all its substructures.  For
 * VTSI files, this fills the ifofile->vtsm_pgci_ut structure.
 */
int ifoRead_PGCI_UT(ifo_handle_t *);

/**
 * okay = ifoRead_C_ADT(ifofile);
 *
 * Reads in the cell address table for the menu VOB.  For the video manager,
 * this corresponds to the VIDEO_TS.VOB file, and for each title set, this
 * corresponds to the VTS_XX_0.VOB file.  This data is located in both the
 * video manager and video title set information files.  For VMGI files, this
 * fills the ifofile->vmgm_c_adt structure and all its substructures.  For VTSI
 * files, this fills the ifofile->vtsm_c_adt structure.
 */
int ifoRead_C_ADT(ifo_handle_t *);

/**
 * okay = ifoRead_TITLE_C_ADT(ifofile);
 *
 * Reads in the cell address table for the video title set corresponding to
 * this IFO file.  This data is only located in the video title set information
 * file.  This structure is mandatory, and must be included in the VTSI file.
 * This call fills the ifofile->vts_c_adt structure and its substructures.
 */
int ifoRead_TITLE_C_ADT(ifo_handle_t *);

/**
 * okay = ifoRead_VOBU_ADMAP(ifofile);
 *
 * Reads in the VOBU address map for the menu VOB.  For the video manager, this
 * corresponds to the VIDEO_TS.VOB file, and for each title set, this
 * corresponds to the VTS_XX_0.VOB file.  This data is located in both the
 * video manager and video title set information files.  For VMGI files, this
 * fills the ifofile->vmgm_vobu_admap structure and all its substructures.  For
 * VTSI files, this fills the ifofile->vtsm_vobu_admap structure.
 */
int ifoRead_VOBU_ADMAP(ifo_handle_t *);

/**
 * okay = ifoRead_TITLE_VOBU_ADMAP(ifofile);
 *
 * Reads in the VOBU address map for the associated video title set.  This data
 * is only located in the video title set information file.  This structure is
 * mandatory, and must be included in the VTSI file.  Fills the
 * ifofile->vts_vobu_admap structure and its substructures.
 */
int ifoRead_TITLE_VOBU_ADMAP(ifo_handle_t *);

/**
 * okay = ifoRead_TXTDT_MGI(ifofile);
 *
 * Reads in the text data strings for the DVD.  Fills the ifofile->txtdt_mgi
 * structure and all its substructures.  This data is only located in the video
 * manager information file.  This structure is mandatory, and must be included
 * in the VMGI file.
 */
int ifoRead_TXTDT_MGI(ifo_handle_t *);

/**
 * The following functions are used for freeing parsed sections of the
 * ifo_handle_t structure and the allocated substructures.  The free calls
 * below are safe:  they will not mind if you attempt to free part of an IFO
 * file which was not read in or which does not exist.
 */
void ifoFree_PTL_MAIT(ifo_handle_t *);
void ifoFree_VTS_ATRT(ifo_handle_t *);
void ifoFree_TT_SRPT(ifo_handle_t *);
void ifoFree_VTS_PTT_SRPT(ifo_handle_t *);
void ifoFree_FP_PGC(ifo_handle_t *);
void ifoFree_PGCIT(ifo_handle_t *);
void ifoFree_PGCI_UT(ifo_handle_t *);
void ifoFree_C_ADT(ifo_handle_t *);
void ifoFree_TITLE_C_ADT(ifo_handle_t *);
void ifoFree_VOBU_ADMAP(ifo_handle_t *);
void ifoFree_TITLE_VOBU_ADMAP(ifo_handle_t *);
void ifoFree_TXTDT_MGI(ifo_handle_t *);

#ifdef __cplusplus
};
#endif
#endif /* IFO_READ_H_INCLUDED */

--- NEW FILE ---
#ifndef IFO_TYPES_H_INCLUDED
#define IFO_TYPES_H_INCLUDED

/*
 * Copyright (C) 2000, 2001 Björn Englund <d4bjorn at dtek.chalmers.se>,
 *                          Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * 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 <inttypes.h>
#include <dvdread/dvd_reader.h>


#undef ATTRIBUTE_PACKED
#undef PRAGMA_PACK_BEGIN 
#undef PRAGMA_PACK_END

#if defined(__GNUC__)
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
#define ATTRIBUTE_PACKED __attribute__ ((packed))
#define PRAGMA_PACK 0
#endif
#endif

#if !defined(ATTRIBUTE_PACKED)
#define ATTRIBUTE_PACKED
#define PRAGMA_PACK 1
#endif

#if PRAGMA_PACK
#pragma pack(1)
#endif


/**
 * Common
 *
 * The following structures are used in both the VMGI and VTSI.
 */


/**
 * DVD Time Information.
 */
typedef struct {
  uint8_t hour;
  uint8_t minute;
  uint8_t second;
  uint8_t frame_u; // The two high bits are the frame rate.
} ATTRIBUTE_PACKED dvd_time_t;

/**
 * Type to store per-command data.
 */
typedef struct {
  uint8_t bytes[8];
} ATTRIBUTE_PACKED vm_cmd_t;
#define COMMAND_DATA_SIZE 8


/**
 * Video Attributes.
 */
typedef struct {
#ifdef WORDS_BIGENDIAN
  unsigned int mpeg_version         : 2;
  unsigned int video_format         : 2;
  unsigned int display_aspect_ratio : 2;
  unsigned int permitted_df         : 2;
  
  unsigned int line21_cc_1          : 1;
  unsigned int line21_cc_2          : 1;
  unsigned int unknown1             : 2;
  
  unsigned int picture_size         : 2;
  unsigned int letterboxed          : 1;
  unsigned int film_mode            : 1;
#else
  unsigned int permitted_df         : 2;
  unsigned int display_aspect_ratio : 2;
  unsigned int video_format         : 2;
  unsigned int mpeg_version         : 2;
  
  unsigned int film_mode            : 1;
  unsigned int letterboxed          : 1;
  unsigned int picture_size         : 2;
  
  unsigned int unknown1             : 2;
  unsigned int line21_cc_2          : 1;
  unsigned int line21_cc_1          : 1;
#endif
} ATTRIBUTE_PACKED video_attr_t;

/**
 * Audio Attributes. (Incomplete/Wrong?)
 */
typedef struct {
#ifdef WORDS_BIGENDIAN
  unsigned int audio_format           : 3;
  unsigned int multichannel_extension : 1;
  unsigned int lang_type              : 2;
  unsigned int application_mode       : 2;
  
  unsigned int quantization           : 2;
  unsigned int sample_frequency       : 2;
  unsigned int unknown1               : 1;
  unsigned int channels               : 3;
#else
  unsigned int application_mode       : 2;
  unsigned int lang_type              : 2;
  unsigned int multichannel_extension : 1;
  unsigned int audio_format           : 3;
  
  unsigned int channels               : 3;
  unsigned int unknown1               : 1;
  unsigned int sample_frequency       : 2;
  unsigned int quantization           : 2;
#endif
  uint16_t lang_code;
  uint8_t  lang_code2; // ??
  uint8_t  lang_extension;
  uint16_t unknown2;
} ATTRIBUTE_PACKED audio_attr_t;

/**
 * Subpicture Attributes.(Incomplete/Wrong)
 */
typedef struct {
  /*
   * type: 0 not specified
   *       1 language
   *       2 other
   * coding mode: 0 run length
   *              1 extended
   *              2 other
   * language: indicates language if type == 1
   * lang extension: if type == 1 contains the lang extension
   */
  uint8_t type;
  uint8_t zero1;
  uint16_t lang_code;
  uint8_t lang_extension;
  uint8_t zero2;
} ATTRIBUTE_PACKED subp_attr_t;



/**
 * PGC Command Table.
 */ 
typedef struct {
  uint16_t nr_of_pre;
  uint16_t nr_of_post;
  uint16_t nr_of_cell;
  uint16_t zero_1;
  vm_cmd_t *pre_cmds;
  vm_cmd_t *post_cmds;
  vm_cmd_t *cell_cmds;
} ATTRIBUTE_PACKED pgc_command_tbl_t;
#define PGC_COMMAND_TBL_SIZE 8

/**
 * PGC Program Map
 */
typedef uint8_t pgc_program_map_t; 

/**
 * Cell Playback Information.
 */
typedef struct {
#ifdef WORDS_BIGENDIAN
  unsigned int block_mode       : 2;
  unsigned int block_type       : 2;
  unsigned int seamless_play    : 1;
  unsigned int interleaved      : 1;
  unsigned int stc_discontinuity: 1;
  unsigned int seamless_angle   : 1;
  
  unsigned int unknown1         : 1;
  unsigned int restricted       : 1;
  unsigned int unknown2         : 6;
#else
  unsigned int seamless_angle   : 1;
  unsigned int stc_discontinuity: 1;
  unsigned int interleaved      : 1;
  unsigned int seamless_play    : 1;
  unsigned int block_type       : 2;
  unsigned int block_mode       : 2;
  
  unsigned int unknown2         : 6;
  unsigned int restricted       : 1;
  unsigned int unknown1         : 1;
#endif
  uint8_t still_time;
  uint8_t cell_cmd_nr;
  dvd_time_t playback_time;
  uint32_t first_sector;
  uint32_t first_ilvu_end_sector;
  uint32_t last_vobu_start_sector;
  uint32_t last_sector;
} ATTRIBUTE_PACKED cell_playback_t;

#define BLOCK_TYPE_NONE         0x0
#define BLOCK_TYPE_ANGLE_BLOCK  0x1

#define BLOCK_MODE_NOT_IN_BLOCK 0x0
#define BLOCK_MODE_FIRST_CELL   0x1
#define BLOCK_MODE_IN_BLOCK     0x2
#define BLOCK_MODE_LAST_CELL    0x3

/**
 * Cell Position Information.
 */
typedef struct {
  uint16_t vob_id_nr;
  uint8_t  zero_1;
  uint8_t  cell_nr;
} ATTRIBUTE_PACKED cell_position_t;

/**
 * User Operations.
 */
typedef struct {
#ifdef WORDS_BIGENDIAN
  unsigned int zero                           : 7; // 25-31
  unsigned int video_pres_mode_change         : 1; // 24
  
  unsigned int karaoke_audio_pres_mode_change : 1; // 23
  unsigned int angle_change                   : 1; // 22
  unsigned int subpic_stream_change           : 1; // 21
  unsigned int audio_stream_change            : 1; // 20
  unsigned int pause_on                       : 1; // 19
  unsigned int still_off                      : 1; // 18
  unsigned int button_select_or_activate      : 1; // 17
  unsigned int resume                         : 1; // 16
  
  unsigned int chapter_menu_call              : 1; // 15
  unsigned int angle_menu_call                : 1; // 14
  unsigned int audio_menu_call                : 1; // 13
  unsigned int subpic_menu_call               : 1; // 12
  unsigned int root_menu_call                 : 1; // 11
  unsigned int title_menu_call                : 1; // 10
  unsigned int backward_scan                  : 1; // 9
  unsigned int forward_scan                   : 1; // 8
  
  unsigned int next_pg_search                 : 1; // 7
  unsigned int prev_or_top_pg_search          : 1; // 6
  unsigned int time_or_chapter_search         : 1; // 5
  unsigned int go_up                          : 1; // 4
  unsigned int stop                           : 1; // 3
  unsigned int title_play                     : 1; // 2
  unsigned int chapter_search_or_play         : 1; // 1
  unsigned int title_or_time_play             : 1; // 0
#else
  unsigned int video_pres_mode_change         : 1; // 24
  unsigned int zero                           : 7; // 25-31
  
  unsigned int resume                         : 1; // 16
  unsigned int button_select_or_activate      : 1; // 17
  unsigned int still_off                      : 1; // 18
  unsigned int pause_on                       : 1; // 19
  unsigned int audio_stream_change            : 1; // 20
  unsigned int subpic_stream_change           : 1; // 21
  unsigned int angle_change                   : 1; // 22
  unsigned int karaoke_audio_pres_mode_change : 1; // 23
  
  unsigned int forward_scan                   : 1; // 8
  unsigned int backward_scan                  : 1; // 9
  unsigned int title_menu_call                : 1; // 10
  unsigned int root_menu_call                 : 1; // 11
  unsigned int subpic_menu_call               : 1; // 12
  unsigned int audio_menu_call                : 1; // 13
  unsigned int angle_menu_call                : 1; // 14
  unsigned int chapter_menu_call              : 1; // 15
  
  unsigned int title_or_time_play             : 1; // 0
  unsigned int chapter_search_or_play         : 1; // 1
  unsigned int title_play                     : 1; // 2
  unsigned int stop                           : 1; // 3
  unsigned int go_up                          : 1; // 4
  unsigned int time_or_chapter_search         : 1; // 5
  unsigned int prev_or_top_pg_search          : 1; // 6
  unsigned int next_pg_search                 : 1; // 7
#endif
} ATTRIBUTE_PACKED user_ops_t;

/**
 * Program Chain Information.
 */
typedef struct {
  uint16_t zero_1;
  uint8_t  nr_of_programs;
  uint8_t  nr_of_cells;
  dvd_time_t playback_time;
  user_ops_t prohibited_ops;
  uint16_t audio_control[8]; /* New type? */
  uint32_t subp_control[32]; /* New type? */
  uint16_t next_pgc_nr;
  uint16_t prev_pgc_nr;
  uint16_t goup_pgc_nr;
  uint8_t  still_time;
  uint8_t  pg_playback_mode;
  uint32_t palette[16]; /* New type struct {zero_1, Y, Cr, Cb} ? */
  uint16_t command_tbl_offset;
  uint16_t program_map_offset;
  uint16_t cell_playback_offset;
  uint16_t cell_position_offset;
  pgc_command_tbl_t *command_tbl;
  pgc_program_map_t  *program_map;
  cell_playback_t *cell_playback;
  cell_position_t *cell_position;
} ATTRIBUTE_PACKED pgc_t;
#define PGC_SIZE 236

/**
 * Program Chain Information Search Pointer.
 */
typedef struct {
  uint8_t  entry_id;
#ifdef WORDS_BIGENDIAN
  unsigned int block_mode : 2;
  unsigned int block_type : 2;
  unsigned int unknown1   : 4;
#else
  unsigned int unknown1   : 4;
  unsigned int block_type : 2;
  unsigned int block_mode : 2;
#endif  
  uint16_t ptl_id_mask;
  uint32_t pgc_start_byte;
  pgc_t *pgc;
} ATTRIBUTE_PACKED pgci_srp_t;
#define PGCI_SRP_SIZE 8

/**
 * Program Chain Information Table.
 */
typedef struct {
  uint16_t nr_of_pgci_srp;
  uint16_t zero_1;
  uint32_t last_byte;
  pgci_srp_t *pgci_srp;
} ATTRIBUTE_PACKED pgcit_t;
#define PGCIT_SIZE 8

/**
 * Menu PGCI Language Unit.
 */
typedef struct {
  uint16_t lang_code;
  uint8_t  zero_1;
  uint8_t  exists;
  uint32_t lang_start_byte;
  pgcit_t *pgcit;
} ATTRIBUTE_PACKED pgci_lu_t;
#define PGCI_LU_SIZE 8

/**
 * Menu PGCI Unit Table.
 */
typedef struct {
  uint16_t nr_of_lus;
  uint16_t zero_1;
  uint32_t last_byte;
  pgci_lu_t *lu;
} ATTRIBUTE_PACKED pgci_ut_t;
#define PGCI_UT_SIZE 8

/**
 * Cell Address Information.
 */
typedef struct {
  uint16_t vob_id;
  uint8_t  cell_id;
  uint8_t  zero_1;
  uint32_t start_sector;
  uint32_t last_sector;
} ATTRIBUTE_PACKED cell_adr_t;

/**
 * Cell Address Table.
 */
typedef struct {
  uint16_t nr_of_vobs; /* VOBs */
  uint16_t zero_1;
  uint32_t last_byte;
  cell_adr_t *cell_adr_table;
} ATTRIBUTE_PACKED c_adt_t;
#define C_ADT_SIZE 8

/**
 * VOBU Address Map.
 */
typedef struct {
  uint32_t last_byte;
  uint32_t *vobu_start_sectors;
} ATTRIBUTE_PACKED vobu_admap_t;
#define VOBU_ADMAP_SIZE 4




/**
 * VMGI
 *
 * The following structures relate to the Video Manager.
 */

/**
 * Video Manager Information Management Table.
 */
typedef struct {
  char     vmg_identifier[12];
  uint32_t vmg_last_sector;
  uint8_t  zero_1[12];
  uint32_t vmgi_last_sector;
  uint8_t  zero_2;
  uint8_t  specification_version;
  uint32_t vmg_category;
  uint16_t vmg_nr_of_volumes;
  uint16_t vmg_this_volume_nr;
  uint8_t  disc_side;
  uint8_t  zero_3[19];
  uint16_t vmg_nr_of_title_sets;  /* Number of VTSs. */
  char     provider_identifier[32];
  uint64_t vmg_pos_code;
  uint8_t  zero_4[24];
  uint32_t vmgi_last_byte;
  uint32_t first_play_pgc;
  uint8_t  zero_5[56];
  uint32_t vmgm_vobs;             /* sector */
  uint32_t tt_srpt;               /* sector */
  uint32_t vmgm_pgci_ut;          /* sector */
  uint32_t ptl_mait;              /* sector */
  uint32_t vts_atrt;              /* sector */
  uint32_t txtdt_mgi;             /* sector */
  uint32_t vmgm_c_adt;            /* sector */
  uint32_t vmgm_vobu_admap;       /* sector */
  uint8_t  zero_6[32];
  
  video_attr_t vmgm_video_attr;
  uint8_t  zero_7;
  uint8_t  nr_of_vmgm_audio_streams; // should be 0 or 1
  audio_attr_t vmgm_audio_attr;
  audio_attr_t zero_8[7];
  uint8_t  zero_9[17];
  uint8_t  nr_of_vmgm_subp_streams; // should be 0 or 1
  subp_attr_t  vmgm_subp_attr;
  subp_attr_t  zero_10[27];  /* XXX: how much 'padding' here? */
} ATTRIBUTE_PACKED vmgi_mat_t;

typedef struct {
#ifdef WORDS_BIGENDIAN
  unsigned int zero_1                    : 1;
  unsigned int multi_or_random_pgc_title : 1; // 0 == one sequential pgc title
  unsigned int jlc_exists_in_cell_cmd    : 1;
  unsigned int jlc_exists_in_prepost_cmd : 1;
  unsigned int jlc_exists_in_button_cmd  : 1;
  unsigned int jlc_exists_in_tt_dom      : 1;
  unsigned int chapter_search_or_play    : 1; // UOP 1
  unsigned int title_or_time_play        : 1; // UOP 0
#else
  unsigned int title_or_time_play        : 1; // UOP 0
  unsigned int chapter_search_or_play    : 1; // UOP 1
  unsigned int jlc_exists_in_tt_dom      : 1;
  unsigned int jlc_exists_in_button_cmd  : 1;
  unsigned int jlc_exists_in_prepost_cmd : 1;
  unsigned int jlc_exists_in_cell_cmd    : 1;
  unsigned int multi_or_random_pgc_title : 1; // 0 == one sequential pgc title
  unsigned int zero_1                    : 1;
#endif
} ATTRIBUTE_PACKED playback_type_t;

/**
 * Title Information.
 */
typedef struct {
  playback_type_t pb_ty;
  uint8_t  nr_of_angles;
  uint16_t nr_of_ptts;
  uint16_t parental_id;
  uint8_t  title_set_nr;
  uint8_t  vts_ttn;
  uint32_t title_set_sector;
} ATTRIBUTE_PACKED title_info_t;

/**
 * PartOfTitle Search Pointer Table.
 */
typedef struct {
  uint16_t nr_of_srpts;
  uint16_t zero_1;
  uint32_t last_byte;
  title_info_t *title;
} ATTRIBUTE_PACKED tt_srpt_t;
#define TT_SRPT_SIZE 8

/**
 * Parental Management Information Unit Table.
 */
typedef struct {
  uint16_t country_code;
  uint16_t zero_1;
  uint16_t pf_ptl_mai_start_byte;
  uint16_t zero_2;
  /* uint16_t *pf_ptl_mai // table of nr_of_vtss+1 x 8 */
} ATTRIBUTE_PACKED ptl_mait_country_t;
#define PTL_MAIT_COUNTRY_SIZE 8

/**
 * Parental Management Information Table.
 */
typedef struct {
  uint16_t nr_of_countries;
  uint16_t nr_of_vtss;
  uint32_t last_byte;
  ptl_mait_country_t *countries;
} ATTRIBUTE_PACKED ptl_mait_t;
#define PTL_MAIT_SIZE 8

/**
 * Video Title Set Attributes.
 */
typedef struct {
  uint32_t last_byte;
  uint32_t vts_cat;
  
  video_attr_t vtsm_vobs_attr;
  uint8_t  zero_1;
  uint8_t  nr_of_vtsm_audio_streams; // should be 0 or 1
  audio_attr_t vtsm_audio_attr;
  audio_attr_t zero_2[7];  
  uint8_t  zero_3[16];
  uint8_t  zero_4;
  uint8_t  nr_of_vtsm_subp_streams; // should be 0 or 1
  subp_attr_t vtsm_subp_attr;
  subp_attr_t zero_5[27];
  
  uint8_t  zero_6[2];
  
  video_attr_t vtstt_vobs_video_attr;
  uint8_t  zero_7;
  uint8_t  nr_of_vtstt_audio_streams;
  audio_attr_t vtstt_audio_attr[8];
  uint8_t  zero_8[16];
  uint8_t  zero_9;
  uint8_t  nr_of_vtstt_subp_streams;
  subp_attr_t vtstt_subp_attr[32];
} ATTRIBUTE_PACKED vts_attributes_t;
#define VTS_ATTRIBUTES_SIZE 542
#define VTS_ATTRIBUTES_MIN_SIZE 356

/**
 * Video Title Set Attribute Table.
 */
typedef struct {
  uint16_t nr_of_vtss;
  uint16_t zero_1;
  uint32_t last_byte;
  vts_attributes_t *vts;
} ATTRIBUTE_PACKED vts_atrt_t;
#define VTS_ATRT_SIZE 8

/**
 * Text Data. (Incomplete)
 */
typedef struct {
  uint32_t last_byte;    /* offsets are relative here */
  uint16_t offsets[100]; /* == nr_of_srpts + 1 (first is disc title) */
#if 0  
  uint16_t unknown; // 0x48 ?? 0x48 words (16bit) info following
  uint16_t zero_1;
  
  uint8_t type_of_info;//?? 01 == disc, 02 == Title, 04 == Title part 
  uint8_t unknown1;
  uint8_t unknown2;
  uint8_t unknown3;
  uint8_t unknown4;//?? allways 0x30 language?, text format?
  uint8_t unknown5;
  uint16_t offset; // from first 
  
  char text[12]; // ended by 0x09
#endif
} ATTRIBUTE_PACKED txtdt_t;

/**
 * Text Data Language Unit. (Incomplete)
 */ 
typedef struct {
  uint16_t lang_code;
  uint16_t unknown;      /* 0x0001, title 1? disc 1? side 1? */
  uint32_t txtdt_start_byte;  /* prt, rel start of vmg_txtdt_mgi  */
  txtdt_t  *txtdt;
} ATTRIBUTE_PACKED txtdt_lu_t;
#define TXTDT_LU_SIZE 8

/**
 * Text Data Manager Information. (Incomplete)
 */
typedef struct {
  char disc_name[14];            /* how many bytes?? */
  uint16_t nr_of_language_units; /* 32bit??          */
  uint32_t last_byte;
  txtdt_lu_t *lu;
} ATTRIBUTE_PACKED txtdt_mgi_t;
#define TXTDT_MGI_SIZE 20


/**
 * VTS
 *
 * Structures relating to the Video Title Set (VTS).
 */

/**
 * Video Title Set Information Management Table.
 */
typedef struct {
  char vts_identifier[12];
  uint32_t vts_last_sector;
  uint8_t  zero_1[12];
  uint32_t vtsi_last_sector;
  uint8_t  zero_2;
  uint8_t  specification_version;
  uint32_t vts_category;
  uint16_t zero_3;
  uint16_t zero_4;
  uint8_t  zero_5;
  uint8_t  zero_6[19];
  uint16_t zero_7;
  uint8_t  zero_8[32];
  uint64_t zero_9;
  uint8_t  zero_10[24];
  uint32_t vtsi_last_byte;
  uint32_t zero_11;
  uint8_t  zero_12[56];
  uint32_t vtsm_vobs;       /* sector */
  uint32_t vtstt_vobs;      /* sector */
  uint32_t vts_ptt_srpt;    /* sector */
  uint32_t vts_pgcit;       /* sector */
  uint32_t vtsm_pgci_ut;    /* sector */
  uint32_t vts_tmapt;       /* sector */  // XXX: FIXME TODO Implement
  uint32_t vtsm_c_adt;      /* sector */
  uint32_t vtsm_vobu_admap; /* sector */
  uint32_t vts_c_adt;       /* sector */
  uint32_t vts_vobu_admap;  /* sector */
  uint8_t  zero_13[24];
  
  video_attr_t vtsm_video_attr;
  uint8_t  zero_14;
  uint8_t  nr_of_vtsm_audio_streams; // should be 0 or 1
  audio_attr_t vtsm_audio_attr;
  audio_attr_t zero_15[7];
  uint8_t  zero_16[17];
  uint8_t  nr_of_vtsm_subp_streams; // should be 0 or 1
  subp_attr_t vtsm_subp_attr;
  subp_attr_t zero_17[27];
  uint8_t  zero_18[2];
  
  video_attr_t vts_video_attr;
  uint8_t  zero_19;
  uint8_t  nr_of_vts_audio_streams;
  audio_attr_t vts_audio_attr[8];
  uint8_t  zero_20[17];
  uint8_t  nr_of_vts_subp_streams;
  subp_attr_t vts_subp_attr[32];
  /* XXX: how much 'padding' here, if any? */
} ATTRIBUTE_PACKED vtsi_mat_t;

/**
 * PartOfTitle Unit Information.
 */
typedef struct {
  uint16_t pgcn;
  uint16_t pgn;
} ATTRIBUTE_PACKED ptt_info_t;

/**
 * PartOfTitle Information.
 */
typedef struct {
  uint16_t nr_of_ptts;
  ptt_info_t *ptt;
} ATTRIBUTE_PACKED ttu_t;

/**
 * PartOfTitle Search Pointer Table.
 */
typedef struct {
  uint16_t nr_of_srpts;
  uint16_t zero_1;
  uint32_t last_byte;
  ttu_t  *title;
} ATTRIBUTE_PACKED vts_ptt_srpt_t;
#define VTS_PTT_SRPT_SIZE 8


#if PRAGMA_PACK
#pragma pack()
#endif


/**
 * The following structure defines an IFO file.  The structure is divided into
 * two parts, the VMGI, or Video Manager Information, which is read from the
 * VIDEO_TS.[IFO,BUP] file, and the VTSI, or Video Title Set Information, which
 * is read in from the VTS_XX_0.[IFO,BUP] files.
 */
typedef struct {
  dvd_file_t *file;
  
  /* VMGI */
  vmgi_mat_t     *vmgi_mat;
  tt_srpt_t      *tt_srpt;
  pgc_t          *first_play_pgc;    
  ptl_mait_t     *ptl_mait;
  vts_atrt_t     *vts_atrt;
  txtdt_mgi_t    *txtdt_mgi;
  
  /* Common */
  pgci_ut_t      *pgci_ut;
  c_adt_t        *menu_c_adt;
  vobu_admap_t   *menu_vobu_admap;
  
  /* VTSI */
  vtsi_mat_t     *vtsi_mat;
  vts_ptt_srpt_t *vts_ptt_srpt;
  pgcit_t        *vts_pgcit;
  int            *vts_tmapt; // FIXME add/correct the type
  c_adt_t        *vts_c_adt;
  vobu_admap_t   *vts_vobu_admap;
} ifo_handle_t;

#endif /* IFO_TYPES_H_INCLUDED */

--- NEW FILE ---
/*
 * Copyright (C) 2000, 2001, 2002 Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * Much of the contents in this file is based on VOBDUMP.
 *
 * VOBDUMP: a program for examining DVD .VOB filse
 *
 * Copyright 1998, 1999 Eric Smith <eric at brouhaha.com>
 *
 * VOBDUMP is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.  Note that I am not
 * granting permission to redistribute or modify VOBDUMP under the
 * terms of any later version of the General Public License.
 *
 * This program is distributed in the hope that it will be useful (or
 * at least amusing), 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 <stdio.h>
#include <inttypes.h>
#include <assert.h>

#include "config.h" // Needed for WORDS_BIGENDIAN
#include "nav_types.h"
#include "nav_print.h"


static void print_time(dvd_time_t *dtime) {
  const char *rate;
  assert((dtime->hour>>4) < 0xa && (dtime->hour&0xf) < 0xa);
  assert((dtime->minute>>4) < 0x7 && (dtime->minute&0xf) < 0xa);
  assert((dtime->second>>4) < 0x7 && (dtime->second&0xf) < 0xa);
  assert((dtime->frame_u&0xf) < 0xa);
  
  printf("%02x:%02x:%02x.%02x", 
	 dtime->hour,
	 dtime->minute,
	 dtime->second,
	 dtime->frame_u & 0x3f);
  switch((dtime->frame_u & 0xc0) >> 6) {
  case 1:
    rate = "25.00";
    break;
  case 3:
    rate = "29.97";
    break;
  default:
    rate = "(please send a bug report)";
    break;
  } 
  printf(" @ %s fps", rate);
}


static void navPrint_PCI_GI(pci_gi_t *pci_gi) {
  int i;

  printf("pci_gi:\n");
  printf("nv_pck_lbn    0x%08x\n", pci_gi->nv_pck_lbn);
  printf("vobu_cat      0x%04x\n", pci_gi->vobu_cat);
  printf("vobu_uop_ctl  0x%08x\n", *(uint32_t*)&pci_gi->vobu_uop_ctl);
  printf("vobu_s_ptm    0x%08x\n", pci_gi->vobu_s_ptm);
  printf("vobu_e_ptm    0x%08x\n", pci_gi->vobu_e_ptm);
  printf("vobu_se_e_ptm 0x%08x\n", pci_gi->vobu_se_e_ptm);
  printf("e_eltm        ");
  print_time(&pci_gi->e_eltm);
  printf("\n");
  
  printf("vobu_isrc     \"");
  for(i = 0; i < 32; i++) {
    char c = pci_gi->vobu_isrc[i];
    if((c >= ' ') && (c <= '~'))
      printf("%c", c);
    else
      printf(".");
  }
  printf("\"\n");
}

static void navPrint_NSML_AGLI(nsml_agli_t *nsml_agli) {
  int i, j = 0;
  
  for(i = 0; i < 9; i++)
    j |= nsml_agli->nsml_agl_dsta[i];
  if(j == 0)
    return;
  
  printf("nsml_agli:\n");
  for(i = 0; i < 9; i++)
    if(nsml_agli->nsml_agl_dsta[i])
      printf("nsml_agl_c%d_dsta  0x%08x\n", i + 1, 
	     nsml_agli->nsml_agl_dsta[i]);
}

static void navPrint_HL_GI(hl_gi_t *hl_gi, int *btngr_ns, int *btn_ns) {
  
  if((hl_gi->hli_ss & 0x03) == 0)
    return;
  
  printf("hl_gi:\n");
  printf("hli_ss        0x%01x\n", hl_gi->hli_ss & 0x03);
  printf("hli_s_ptm     0x%08x\n", hl_gi->hli_s_ptm);
  printf("hli_e_ptm     0x%08x\n", hl_gi->hli_e_ptm);
  printf("btn_se_e_ptm  0x%08x\n", hl_gi->btn_se_e_ptm);

  *btngr_ns = hl_gi->btngr_ns;
  printf("btngr_ns      %d\n",  hl_gi->btngr_ns);
  printf("btngr%d_dsp_ty    0x%02x\n", 1, hl_gi->btngr1_dsp_ty);
  printf("btngr%d_dsp_ty    0x%02x\n", 2, hl_gi->btngr2_dsp_ty);
  printf("btngr%d_dsp_ty    0x%02x\n", 3, hl_gi->btngr3_dsp_ty);
  
  printf("btn_ofn       %d\n", hl_gi->btn_ofn);
  *btn_ns = hl_gi->btn_ns;
  printf("btn_ns        %d\n", hl_gi->btn_ns);
  printf("nsl_btn_ns    %d\n", hl_gi->nsl_btn_ns);
  printf("fosl_btnn     %d\n", hl_gi->fosl_btnn);
  printf("foac_btnn     %d\n", hl_gi->foac_btnn);
}

static void navPrint_BTN_COLIT(btn_colit_t *btn_colit) {
  int i, j;
  
  j = 0;
  for(i = 0; i < 6; i++)
    j |= btn_colit->btn_coli[i/2][i&1];
  if(j == 0)
    return;
  
  printf("btn_colit:\n");
  for(i = 0; i < 3; i++)
    for(j = 0; j < 2; j++)
      printf("btn_cqoli %d  %s_coli:  %08x\n",
	     i, (j == 0) ? "sl" : "ac",
	     btn_colit->btn_coli[i][j]);
}

static void navPrint_BTNIT(btni_t *btni_table, int btngr_ns, int btn_ns) {
  int i, j;
  
  printf("btnit:\n");
  printf("btngr_ns: %i\n", btngr_ns);
  printf("btn_ns: %i\n", btn_ns);
  
  if(btngr_ns == 0)
    return;
  
  for(i = 0; i < btngr_ns; i++) {
    for(j = 0; j < (36 / btngr_ns); j++) {
      if(j < btn_ns) {
	btni_t *btni = &btni_table[(36 / btngr_ns) * i + j];
	
	printf("group %d btni %d:  ", i+1, j+1);
	printf("btn_coln %d, auto_action_mode %d\n",
	       btni->btn_coln, btni->auto_action_mode);
	printf("coords   (%d, %d) .. (%d, %d)\n",
	       btni->x_start, btni->y_start, btni->x_end, btni->y_end);
	
	printf("up %d, ", btni->up);
	printf("down %d, ", btni->down);
	printf("left %d, ", btni->left);
	printf("right %d\n", btni->right);
	
	// ifoPrint_COMMAND(&btni->cmd);
	printf("\n");
      }
    }
  }
}

static void navPrint_HLI(hli_t *hli) {
  int btngr_ns = 0, btn_ns = 0;
  
  printf("hli:\n");
  navPrint_HL_GI(&hli->hl_gi, & btngr_ns, & btn_ns);
  navPrint_BTN_COLIT(&hli->btn_colit);
  navPrint_BTNIT(hli->btnit, btngr_ns, btn_ns);
}

void navPrint_PCI(pci_t *pci) {
  printf("pci packet:\n");
  navPrint_PCI_GI(&pci->pci_gi);
  navPrint_NSML_AGLI(&pci->nsml_agli);
  navPrint_HLI(&pci->hli);
}

static void navPrint_DSI_GI(dsi_gi_t *dsi_gi) {
  printf("dsi_gi:\n");
  printf("nv_pck_scr     0x%08x\n", dsi_gi->nv_pck_scr);
  printf("nv_pck_lbn     0x%08x\n", dsi_gi->nv_pck_lbn );
  printf("vobu_ea        0x%08x\n", dsi_gi->vobu_ea);
  printf("vobu_1stref_ea 0x%08x\n", dsi_gi->vobu_1stref_ea);
  printf("vobu_2ndref_ea 0x%08x\n", dsi_gi->vobu_2ndref_ea);
  printf("vobu_3rdref_ea 0x%08x\n", dsi_gi->vobu_3rdref_ea);
  printf("vobu_vob_idn   0x%04x\n", dsi_gi->vobu_vob_idn);
  printf("vobu_c_idn     0x%02x\n", dsi_gi->vobu_c_idn);
  printf("c_eltm         ");
  print_time(&dsi_gi->c_eltm);
  printf("\n");
}

static void navPrint_SML_PBI(sml_pbi_t *sml_pbi) {
  printf("sml_pbi:\n");
  printf("category 0x%04x\n", sml_pbi->category);
  if(sml_pbi->category & 0x8000)
    printf("VOBU is in preunit\n");
  if(sml_pbi->category & 0x4000)
    printf("VOBU is in ILVU\n");
  if(sml_pbi->category & 0x2000)
    printf("VOBU at the beginning of ILVU\n");
  if(sml_pbi->category & 0x1000)
    printf("VOBU at end of PREU of ILVU\n");
  
  printf("ilvu_ea       0x%08x\n", sml_pbi->ilvu_ea);
  printf("nxt_ilvu_sa   0x%08x\n", sml_pbi->ilvu_sa);
  printf("nxt_ilvu_size 0x%04x\n", sml_pbi->size);
  
  printf("vob_v_s_s_ptm 0x%08x\n", sml_pbi->vob_v_s_s_ptm);
  printf("vob_v_e_e_ptm 0x%08x\n", sml_pbi->vob_v_e_e_ptm);
  
  /* $$$ more code needed here */
}

static void navPrint_SML_AGLI(sml_agli_t *sml_agli) {
  int i;
  printf("sml_agli:\n");
  for(i = 0; i < 9; i++) {
    printf("agl_c%d address: 0x%08x size 0x%04x\n", i,
	   sml_agli->data[i].address, sml_agli->data[i].size);
  }
}

static void navPrint_VOBU_SRI(vobu_sri_t *vobu_sri) {
  int i;
  int stime[19] = { 240, 120, 60, 20, 15, 14, 13, 12, 11, 
		     10,   9,  8,  7,  6,  5,  4,  3,  2, 1};
  printf("vobu_sri:\n");
  printf("Next VOBU with Video %08x\n", vobu_sri->next_video);
  for(i = 0; i < 19; i++) {
    printf("%3.1f %08x ", stime[i]/2.0, vobu_sri->fwda[i]);
  }
  printf("\n");
  printf("Next VOBU %08x\n", vobu_sri->next_vobu);
  printf("--\n");
  printf("Prev VOBU %08x\n", vobu_sri->prev_vobu);
  for(i = 0; i < 19; i++) {
    printf("%3.1f %08x ", stime[18 - i]/2.0, vobu_sri->bwda[i]);
  }
  printf("\n");
  printf("Prev VOBU with Video %08x\n", vobu_sri->prev_video);
}

static void navPrint_SYNCI(synci_t *synci) {
  int i;
  
  printf("synci:\n");
  /* $$$ more code needed here */
  for(i = 0; i < 8; i++)
    printf("%04x ", synci->a_synca[i]);
  for(i = 0; i < 32; i++)
    printf("%08x ", synci->sp_synca[i]);
}

void navPrint_DSI(dsi_t *dsi) {
  printf("dsi packet:\n");
  navPrint_DSI_GI(&dsi->dsi_gi);
  navPrint_SML_PBI(&dsi->sml_pbi);
  navPrint_SML_AGLI(&dsi->sml_agli);
  navPrint_VOBU_SRI(&dsi->vobu_sri);
  navPrint_SYNCI(&dsi->synci);
}



--- NEW FILE ---
#ifndef NAV_PRINT_H_INCLUDED
#define NAV_PRINT_H_INCLUDED

/*
 * Copyright (C) 2001 Billy Biggs <vektor at dumbterm.net>,
 *                    Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * 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 <dvdread/nav_types.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * Prints information contained in the PCI to stdout.
 */
void navPrint_PCI(pci_t *);
  
/**
 * Prints information contained in the DSI to stdout.
 */
void navPrint_DSI(dsi_t *);

#ifdef __cplusplus
};
#endif
#endif /* NAV_PRINT_H_INCLUDED */

--- NEW FILE ---
/*
 * Copyright (C) 2000, 2001, 2002 Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * 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 <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>

#include "config.h" // Needed for WORDS_BIGENDIAN
#include "bswap.h"
#include "nav_types.h"
#include "nav_read.h"

void navRead_PCI(pci_t *pci, unsigned char *buffer) {
  int i, j, k;

  assert(sizeof(pci_t) == PCI_BYTES - 1); // -1 for substream id
  
  memcpy(pci, buffer, sizeof(pci_t));

  /* Endian conversions  */

  /* pci pci_gi */
  B2N_32(pci->pci_gi.nv_pck_lbn);
  B2N_16(pci->pci_gi.vobu_cat);
  B2N_32(pci->pci_gi.vobu_s_ptm);
  B2N_32(pci->pci_gi.vobu_e_ptm);
  B2N_32(pci->pci_gi.vobu_se_e_ptm);

  /* pci nsml_agli */
  for(i = 0; i < 9; i++)
    B2N_32(pci->nsml_agli.nsml_agl_dsta[i]);

  /* pci hli hli_gi */
  B2N_16(pci->hli.hl_gi.hli_ss);
  B2N_32(pci->hli.hl_gi.hli_s_ptm);
  B2N_32(pci->hli.hl_gi.hli_e_ptm);
  B2N_32(pci->hli.hl_gi.btn_se_e_ptm);

  /* pci hli btn_colit */
  for(i = 0; i < 3; i++)
    for(j = 0; j < 2; j++)
      B2N_32(pci->hli.btn_colit.btn_coli[i][j]);

#if !defined(WORDS_BIGENDIAN)
  /* pci hli btni */
  for(i = 0; i < 36; i++) {
    char tmp[6], swap;
    memcpy(tmp, &(pci->hli.btnit[i]), 6);
    /* This is a B2N_24() */
    swap = tmp[0]; tmp[0] = tmp[2]; tmp[2] = swap;
    /* This is a B2N_24() */
    swap = tmp[3]; tmp[3] = tmp[5]; tmp[5] = swap;
    memcpy(&(pci->hli.btnit[i]), tmp, 6);
  }
#endif


  /* Asserts */

  /* pci pci gi */ 
  assert(pci->pci_gi.zero1 == 0);

  /* pci hli hli_gi */
  assert(pci->hli.hl_gi.zero1 == 0);
  assert(pci->hli.hl_gi.zero2 == 0);
  assert(pci->hli.hl_gi.zero3 == 0);
  assert(pci->hli.hl_gi.zero4 == 0);
  assert(pci->hli.hl_gi.zero5 == 0);

  /* Are there buttons defined here? */
  if((pci->hli.hl_gi.hli_ss & 0x03) != 0) {
    assert(pci->hli.hl_gi.btn_ns != 0); 
    assert(pci->hli.hl_gi.btngr_ns != 0); 
  } else {
    assert((pci->hli.hl_gi.btn_ns != 0 && pci->hli.hl_gi.btngr_ns != 0) 
	   || (pci->hli.hl_gi.btn_ns == 0 && pci->hli.hl_gi.btngr_ns == 0));
  }

  /* pci hli btnit */
  for(i = 0; i < pci->hli.hl_gi.btngr_ns; i++) {
    for(j = 0; j < (36 / pci->hli.hl_gi.btngr_ns); j++) {
      int n = (36 / pci->hli.hl_gi.btngr_ns) * i + j;
      assert(pci->hli.btnit[n].zero1 == 0);
      assert(pci->hli.btnit[n].zero2 == 0);
      assert(pci->hli.btnit[n].zero3 == 0);
      assert(pci->hli.btnit[n].zero4 == 0);
      assert(pci->hli.btnit[n].zero5 == 0);
      assert(pci->hli.btnit[n].zero6 == 0);
      
      if (j < pci->hli.hl_gi.btn_ns) {	
	assert(pci->hli.btnit[n].x_start <= pci->hli.btnit[n].x_end);
	assert(pci->hli.btnit[n].y_start <= pci->hli.btnit[n].y_end);
	assert(pci->hli.btnit[n].up <= pci->hli.hl_gi.btn_ns);
	assert(pci->hli.btnit[n].down <= pci->hli.hl_gi.btn_ns);
	assert(pci->hli.btnit[n].left <= pci->hli.hl_gi.btn_ns);
	assert(pci->hli.btnit[n].right <= pci->hli.hl_gi.btn_ns);
	//vmcmd_verify(pci->hli.btnit[n].cmd);
      } else {
	assert(pci->hli.btnit[n].btn_coln == 0);
	assert(pci->hli.btnit[n].auto_action_mode == 0);
	assert(pci->hli.btnit[n].x_start == 0);
	assert(pci->hli.btnit[n].y_start == 0);
	assert(pci->hli.btnit[n].x_end == 0);
	assert(pci->hli.btnit[n].y_end == 0);
	assert(pci->hli.btnit[n].up == 0);
	assert(pci->hli.btnit[n].down == 0);
	assert(pci->hli.btnit[n].left == 0);
	assert(pci->hli.btnit[n].right == 0);
	for (k = 0; k < 8; k++)
	  assert(pci->hli.btnit[n].cmd.bytes[k] == 0); //CHECK_ZERO?
      }
    }
  }
}

void navRead_DSI(dsi_t *dsi, unsigned char *buffer) {
  int i;

  assert(sizeof(dsi_t) == DSI_BYTES - 1); // -1 for substream id
  
  memcpy(dsi, buffer, sizeof(dsi_t));

  /* Endian conversions */

  /* dsi dsi gi */
  B2N_32(dsi->dsi_gi.nv_pck_scr);
  B2N_32(dsi->dsi_gi.nv_pck_lbn);
  B2N_32(dsi->dsi_gi.vobu_ea);
  B2N_32(dsi->dsi_gi.vobu_1stref_ea);
  B2N_32(dsi->dsi_gi.vobu_2ndref_ea);
  B2N_32(dsi->dsi_gi.vobu_3rdref_ea);
  B2N_16(dsi->dsi_gi.vobu_vob_idn);

  /* dsi sml pbi */
  B2N_16(dsi->sml_pbi.category);
  B2N_32(dsi->sml_pbi.ilvu_ea);
  B2N_32(dsi->sml_pbi.ilvu_sa);
  B2N_16(dsi->sml_pbi.size);
  B2N_32(dsi->sml_pbi.vob_v_s_s_ptm);
  B2N_32(dsi->sml_pbi.vob_v_e_e_ptm);

  /* dsi sml agli */
  for(i = 0; i < 9; i++) {
    B2N_32(dsi->sml_agli.data[ i ].address);
    B2N_16(dsi->sml_agli.data[ i ].size);
  }

  /* dsi vobu sri */
  B2N_32(dsi->vobu_sri.next_video);
  for(i = 0; i < 19; i++)
    B2N_32(dsi->vobu_sri.fwda[i]);
  B2N_32(dsi->vobu_sri.next_vobu);
  B2N_32(dsi->vobu_sri.prev_vobu);
  for(i = 0; i < 19; i++)
    B2N_32(dsi->vobu_sri.bwda[i]);
  B2N_32(dsi->vobu_sri.prev_video);

  /* dsi synci */
  for(i = 0; i < 8; i++)
    B2N_16(dsi->synci.a_synca[i]);
  for(i = 0; i < 32; i++)
    B2N_32(dsi->synci.sp_synca[i]);

  
  /* Asserts */

  /* dsi dsi gi */
  assert(dsi->dsi_gi.zero1 == 0);
}


--- NEW FILE ---
#ifndef NAV_READ_H_INCLUDED
#define NAV_READ_H_INCLUDED

/*
 * Copyright (C) 2000, 2001 Håkan Hjort <d95hjort at dtek.chalmers.se>.
 *
 * 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 <dvdread/nav_types.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * Reads the PCI packet data pointed to into pci struct.
 */
void navRead_PCI(pci_t *, unsigned char *);

/**
 * Reads the DSI packet data pointed to into dsi struct.
 */
void navRead_DSI(dsi_t *, unsigned char *);

#ifdef __cplusplus
};
#endif
#endif /* NAV_READ_H_INCLUDED */

--- NEW FILE ---
#ifndef NAV_TYPES_H_INCLUDED
#define NAV_TYPES_H_INCLUDED

/*
 * Copyright (C) 2000, 2001 Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * The data structures in this file should represent the layout of the
 * pci and dsi packets as they are stored in the stream.  Information
 * found by reading the source to VOBDUMP is the base for the structure
 * and names of these data types.
 *
 * VOBDUMP: a program for examining DVD .VOB files.
 * Copyright 1998, 1999 Eric Smith <eric at brouhaha.com>
 *
 * VOBDUMP is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.  Note that I am not
 * granting permission to redistribute or modify VOBDUMP under the terms
 * of any later version of the General Public License.
 *
 * This program is distributed in the hope that it will be useful (or at
 * least amusing), 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 <inttypes.h>
#include <dvdread/ifo_types.h> // only dvd_time_t, vm_cmd_t and user_ops_t


#undef ATTRIBUTE_PACKED
#undef PRAGMA_PACK_BEGIN 
#undef PRAGMA_PACK_END

#if defined(__GNUC__)
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
#define ATTRIBUTE_PACKED __attribute__ ((packed))
#define PRAGMA_PACK 0
#endif
#endif

#if !defined(ATTRIBUTE_PACKED)
#define ATTRIBUTE_PACKED
#define PRAGMA_PACK 1
#endif


/* The length including the substream id byte. */
#define PCI_BYTES 0x3d4
#define DSI_BYTES 0x3fa

#define PS2_PCI_SUBSTREAM_ID 0x00
#define PS2_DSI_SUBSTREAM_ID 0x01

/* Remove this */
#define DSI_START_BYTE 1031


#if PRAGMA_PACK
#pragma pack(1)
#endif


/**
 * PCI General Information 
 */
typedef struct {
  uint32_t nv_pck_lbn;
  uint16_t vobu_cat;
  uint16_t zero1;
  user_ops_t vobu_uop_ctl;
  uint32_t vobu_s_ptm;
  uint32_t vobu_e_ptm;
  uint32_t vobu_se_e_ptm;
  dvd_time_t e_eltm;
  char vobu_isrc[32];
} ATTRIBUTE_PACKED pci_gi_t;

/**
 * Non Seamless Angle Information
 */
typedef struct {
  uint32_t nsml_agl_dsta[9]; 
} ATTRIBUTE_PACKED nsml_agli_t;

/** 
 * Highlight General Information 
 */
typedef struct {
  uint16_t hli_ss; ///< only low 2 bits
  uint32_t hli_s_ptm;
  uint32_t hli_e_ptm;
  uint32_t btn_se_e_ptm;
#ifdef WORDS_BIGENDIAN
  unsigned int zero1 : 2;
  unsigned int btngr_ns : 2;
  unsigned int zero2 : 1;
  unsigned int btngr1_dsp_ty : 3;
  unsigned int zero3 : 1;
  unsigned int btngr2_dsp_ty : 3;
  unsigned int zero4 : 1;
  unsigned int btngr3_dsp_ty : 3;
#else
  unsigned int btngr1_dsp_ty : 3;
  unsigned int zero2 : 1;
  unsigned int btngr_ns : 2;
  unsigned int zero1 : 2;
  unsigned int btngr3_dsp_ty : 3;
  unsigned int zero4 : 1;
  unsigned int btngr2_dsp_ty : 3;
  unsigned int zero3 : 1;
#endif
  uint8_t btn_ofn;
  uint8_t btn_ns;     ///< only low 6 bits
  uint8_t nsl_btn_ns; ///< only low 6 bits
  uint8_t zero5;
  uint8_t fosl_btnn;  ///< only low 6 bits
  uint8_t foac_btnn;  ///< only low 6 bits
} ATTRIBUTE_PACKED hl_gi_t;


/** 
 * Button Color Information Table 
 */
typedef struct {
  uint32_t btn_coli[3][2];
} ATTRIBUTE_PACKED btn_colit_t;

/** 
 * Button Information
 */
typedef struct {
#ifdef WORDS_BIGENDIAN
  unsigned int btn_coln         : 2;
  unsigned int x_start          : 10;
  unsigned int zero1            : 2;
  unsigned int x_end            : 10;
  unsigned int auto_action_mode : 2;
  unsigned int y_start          : 10;
  unsigned int zero2            : 2;
  unsigned int y_end            : 10;

  unsigned int zero3            : 2;
  unsigned int up               : 6;
  unsigned int zero4            : 2;
  unsigned int down             : 6;
  unsigned int zero5            : 2;
  unsigned int left             : 6;
  unsigned int zero6            : 2;
  unsigned int right            : 6;
#else
  unsigned int x_end            : 10;
  unsigned int zero1            : 2;
  unsigned int x_start          : 10;
  unsigned int btn_coln         : 2;
  unsigned int y_end            : 10;
  unsigned int zero2            : 2;
  unsigned int y_start          : 10;
  unsigned int auto_action_mode : 2;

  unsigned int up               : 6;
  unsigned int zero3            : 2;
  unsigned int down             : 6;
  unsigned int zero4            : 2;
  unsigned int left             : 6;
  unsigned int zero5            : 2;
  unsigned int right            : 6;
  unsigned int zero6            : 2;
#endif
  vm_cmd_t cmd;
} ATTRIBUTE_PACKED btni_t;

/**
 * Highlight Information 
 */
typedef struct {
  hl_gi_t     hl_gi;
  btn_colit_t btn_colit;
  btni_t      btnit[36];
} ATTRIBUTE_PACKED hli_t;

/**
 * PCI packet
 */
typedef struct {
  pci_gi_t    pci_gi;
  nsml_agli_t nsml_agli;
  hli_t       hli;
  uint8_t     zero1[189];
} ATTRIBUTE_PACKED pci_t;




/**
 * DSI General Information 
 */
typedef struct {
  uint32_t nv_pck_scr;
  uint32_t nv_pck_lbn;
  uint32_t vobu_ea;
  uint32_t vobu_1stref_ea;
  uint32_t vobu_2ndref_ea;
  uint32_t vobu_3rdref_ea;
  uint16_t vobu_vob_idn;
  uint8_t  zero1;
  uint8_t  vobu_c_idn;
  dvd_time_t c_eltm;
} ATTRIBUTE_PACKED dsi_gi_t;

/**
 * Seamless Playback Information
 */
typedef struct {
  uint16_t category; ///< category of seamless VOBU
  uint32_t ilvu_ea;  ///< end address of interleaved Unit (sectors)
  uint32_t ilvu_sa;  ///< start address of next interleaved unit (sectors)
  uint16_t size;     ///< size of next interleaved unit (sectors)
  uint32_t vob_v_s_s_ptm; ///< video start ptm in vob
  uint32_t vob_v_e_e_ptm; ///< video end ptm in vob
  struct {
    uint32_t stp_ptm1;
    uint32_t stp_ptm2;
    uint32_t gap_len1;
    uint32_t gap_len2;      
  } vob_a[8];
} ATTRIBUTE_PACKED sml_pbi_t;

/**
 * Seamless Angle Infromation for one angle
 */
typedef struct {
    uint32_t address; ///< Sector offset to next ILVU, high bit is before/after
    uint16_t size;    ///< Byte size of the ILVU poited to by address.
} ATTRIBUTE_PACKED sml_agl_data_t;

/**
 * Seamless Angle Infromation
 */
typedef struct {
  sml_agl_data_t data[9];
} ATTRIBUTE_PACKED sml_agli_t;

/**
 * VOBU Search Information 
 */
typedef struct {
  uint32_t next_video; ///< Next vobu that contains video
  uint32_t fwda[19];   ///< Forwards, time
  uint32_t next_vobu;
  uint32_t prev_vobu;
  uint32_t bwda[19];   ///< Backwards, time
  uint32_t prev_video;
} ATTRIBUTE_PACKED vobu_sri_t;

#define SRI_END_OF_CELL 0x3fffffff

/**
 * Synchronous Information
 */ 
typedef struct {
  uint16_t a_synca[8];   ///< Sector offset to first audio packet for this VOBU
  uint32_t sp_synca[32]; ///< Sector offset to first subpicture packet
} ATTRIBUTE_PACKED synci_t;

/**
 * DSI packet
 */
typedef struct {
  dsi_gi_t   dsi_gi;
  sml_pbi_t  sml_pbi;
  sml_agli_t sml_agli;
  vobu_sri_t vobu_sri;
  synci_t    synci;
  uint8_t    zero1[471];
} ATTRIBUTE_PACKED dsi_t;


#if PRAGMA_PACK
#pragma pack()
#endif

#endif /* NAV_TYPES_H_INCLUDED */




More information about the MPlayer-cvslog mailing list