changeset 7029:9db58ffbd73c

importing libdvdread 0.9.3 files
author arpi
date Fri, 16 Aug 2002 22:37:48 +0000
parents 9d4273713562
children 660a8439c679
files libmpdvdkit2/bswap.h libmpdvdkit2/dvd_input.c libmpdvdkit2/dvd_input.h libmpdvdkit2/dvd_reader.c libmpdvdkit2/dvd_reader.h libmpdvdkit2/dvd_udf.c libmpdvdkit2/dvd_udf.h libmpdvdkit2/ifo_print.c libmpdvdkit2/ifo_print.h libmpdvdkit2/ifo_read.c libmpdvdkit2/ifo_read.h libmpdvdkit2/ifo_types.h libmpdvdkit2/nav_print.c libmpdvdkit2/nav_print.h libmpdvdkit2/nav_read.c libmpdvdkit2/nav_read.h libmpdvdkit2/nav_types.h
diffstat 17 files changed, 6757 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdvdkit2/bswap.h	Fri Aug 16 22:37:48 2002 +0000
@@ -0,0 +1,87 @@
+#ifndef BSWAP_H_INCLUDED
+#define BSWAP_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001 Billy Biggs <vektor@dumbterm.net>,
+ *                          Håkan Hjort <d95hjort@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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdvdkit2/dvd_input.c	Fri Aug 16 22:37:48 2002 +0000
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2002 Samuel Hocevar <sam@zoy.org>,
+ *                    Håkan Hjort <d95hjort@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;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdvdkit2/dvd_input.h	Fri Aug 16 22:37:48 2002 +0000
@@ -0,0 +1,51 @@
+#ifndef DVD_INPUT_H_INCLUDED
+#define DVD_INPUT_H_INCLUDED
+
+/*
+ * Copyright (C) 2001, 2002 Samuel Hocevar <sam@zoy.org>,
+ *                          Håkan Hjort <d95hjort@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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdvdkit2/dvd_reader.c	Fri Aug 16 22:37:48 2002 +0000
@@ -0,0 +1,898 @@
+/*
+ * Copyright (C) 2001, 2002 Billy Biggs <vektor@dumbterm.net>,
+ *                          Håkan Hjort <d95hjort@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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdvdkit2/dvd_reader.h	Fri Aug 16 22:37:48 2002 +0000
@@ -0,0 +1,151 @@
+#ifndef DVD_READER_H_INCLUDED
+#define DVD_READER_H_INCLUDED
+
+/*
+ * Copyright (C) 2001, 2002 Billy Biggs <vektor@dumbterm.net>,
+ *                          Håkan Hjort <d95hjort@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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdvdkit2/dvd_udf.c	Fri Aug 16 22:37:48 2002 +0000
@@ -0,0 +1,494 @@
+/*
+ * This code is based on dvdudf by:
+ *   Christian Wolff <scarabaeus@convergence.de>.
+ *
+ * Modifications by:
+ *   Billy Biggs <vektor@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@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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdvdkit2/dvd_udf.h	Fri Aug 16 22:37:48 2002 +0000
@@ -0,0 +1,53 @@
+#ifndef DVD_UDF_H_INCLUDED
+#define DVD_UDF_H_INCLUDED
+
+/*
+ * This code is based on dvdudf by:
+ *   Christian Wolff <scarabaeus@convergence.de>.
+ *
+ * Modifications by:
+ *   Billy Biggs <vektor@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@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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdvdkit2/ifo_print.c	Fri Aug 16 22:37:48 2002 +0000
@@ -0,0 +1,1049 @@
+/* 
+ * Copyright (C) 2000, 2001, 2002 Björn Englund <d4bjorn@dtek.chalmers.se>, 
+ *                                Håkan Hjort <d95hjort@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 <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "config.h" // Needed for WORDS_BIGENDIAN
+#include "ifo_types.h"
+#include "ifo_read.h"
+#include "ifo_print.h"
+
+/* Put this in some other file / package?  It's used in nav_print too. */
+static void ifoPrint_time(int level, 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:
+    if(dtime->hour == 0 && dtime->minute == 0 
+       && dtime->second == 0 && dtime->frame_u == 0)
+      rate = "no";
+    else
+      rate = "(please send a bug report)";
+    break;
+  } 
+  printf(" @ %s fps", rate);
+}
+
+/* Put this in some other file / package?  It's used in nav_print too.
+   Possibly also by the vm / navigator. */
+static void ifoPrint_CMD(int row, vm_cmd_t *command) {
+  int i;
+
+  printf("(%03d) ", row + 1);
+  for(i=0;i<8;i++)
+    printf("%02x ", command->bytes[i]);
+  printf("| ");
+
+  //vmcmd(command);
+  printf("\n");
+}
+
+static void ifoPrint_video_attributes(int level, video_attr_t *attr) {
+  
+  /* The following test is shorter but not correct ISO C,
+     memcmp(attr,my_friendly_zeros, sizeof(video_attr_t)) */
+  if(attr->mpeg_version == 0 
+     && attr->video_format == 0 
+     && attr->display_aspect_ratio == 0 
+     && attr->permitted_df == 0 
+     && attr->unknown1 == 0 
+     && attr->line21_cc_1 == 0 
+     && attr->line21_cc_2 == 0 
+     && attr->video_format == 0 
+     && attr->letterboxed == 0 
+     && attr->film_mode == 0) {
+    printf("-- Unspecified --");
+    return;
+  }
+  
+  switch(attr->mpeg_version) {
+  case 0:
+    printf("mpeg1 ");
+    break;
+  case 1:
+    printf("mpeg2 ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+  
+  switch(attr->video_format) {
+  case 0:
+    printf("ntsc ");
+    break;
+  case 1:
+    printf("pal ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+  
+  switch(attr->display_aspect_ratio) {
+  case 0:
+    printf("4:3 ");
+    break;
+  case 3:
+    printf("16:9 ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+  
+  // Wide is allways allowed..!!!
+  switch(attr->permitted_df) {
+  case 0:
+    printf("pan&scan+letterboxed ");
+    break;
+  case 1:
+    printf("only pan&scan "); //??
+    break;
+  case 2:
+    printf("only letterboxed ");
+    break;
+  case 3:
+    // not specified
+    break;
+  default:
+    printf("(please send a bug report)");
+  }
+  
+  printf("U%x ", attr->unknown1);
+  assert(!attr->unknown1);
+  
+  if(attr->line21_cc_1 || attr->line21_cc_2) {
+    printf("NTSC CC ");
+    if(attr->line21_cc_1)
+      printf("1 ");
+    if(attr->line21_cc_2)
+      printf("2 ");
+  }
+  
+  {
+    int height = 480;
+    if(attr->video_format != 0) 
+      height = 576;
+    switch(attr->picture_size) {
+    case 0:
+      printf("720x%d ", height);
+      break;
+    case 1:
+      printf("704x%d ", height);
+      break;
+    case 2:
+      printf("352x%d ", height);
+      break;
+    case 3:
+      printf("352x%d ", height/2);
+      break;      
+    default:
+      printf("(please send a bug report) ");
+    }
+  }
+
+  if(attr->letterboxed) {
+    printf("source letterboxed ");
+  }
+  
+  if(attr->film_mode) {
+    printf("film");
+  } else {
+    printf("video"); //camera
+  }
+}
+
+static void ifoPrint_audio_attributes(int level, audio_attr_t *attr) {
+  
+  if(attr->audio_format == 0
+     && attr->multichannel_extension == 0
+     && attr->lang_type == 0
+     && attr->application_mode == 0
+     && attr->quantization == 0
+     && attr->sample_frequency == 0
+     && attr->channels == 0
+     && attr->lang_extension == 0
+     && attr->unknown1 == 0
+     && attr->unknown1 == 0) {
+    printf("-- Unspecified --");
+    return;
+  }
+  
+  switch(attr->audio_format) {
+  case 0:
+    printf("ac3 ");
+    break;
+  case 1:
+    printf("(please send a bug report) ");
+    break;
+  case 2:
+    printf("mpeg1 ");
+    break;
+  case 3:
+    printf("mpeg2ext ");
+    break;
+  case 4:
+    printf("lpcm ");
+    break;
+  case 5:
+    printf("(please send a bug report) ");
+    break;
+  case 6:
+    printf("dts ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+  
+  if(attr->multichannel_extension)
+    printf("multichannel_extension ");
+  
+  switch(attr->lang_type) {
+  case 0:
+    // not specified
+    assert(attr->lang_code == 0 || attr->lang_code == 0xffff);
+    break;
+  case 1:
+    printf("%c%c ", attr->lang_code>>8, attr->lang_code & 0xff);
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+
+  switch(attr->application_mode) {
+  case 0:
+    // not specified
+    break;
+  case 1:
+    printf("karaoke mode ");
+    break;
+  case 2:
+    printf("surround sound mode ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+  
+  switch(attr->quantization) {
+  case 0:
+    printf("16bit ");
+    break;
+  case 1:
+    printf("20bit ");
+    break;
+  case 2:
+    printf("24bit ");
+    break;
+  case 3:
+    printf("drc ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+  
+  switch(attr->sample_frequency) {
+  case 0:
+    printf("48kHz ");
+    break;
+  case 1:
+    printf("??kHz ");
+    break;
+  default:
+    printf("sample_frequency %i (please send a bug report) ", 
+	   attr->sample_frequency);
+  }
+  
+  printf("%dCh ", attr->channels + 1);
+  
+  switch(attr->lang_extension) {
+  case 0:
+    printf("Not specified ");
+    break;
+  case 1: // Normal audio
+    printf("Normal Caption ");
+    break;
+  case 2: // visually imparied
+    printf("Audio for visually impaired ");
+    break;
+  case 3: // Directors 1
+    printf("Director's comments 1 ");
+    break;
+  case 4: // Directors 2
+    printf("Director's comments 2 ");
+    break;
+    //case 4: // Music score ?    
+  default:
+    printf("(please send a bug report) ");
+  }
+    
+  printf("%d ", attr->unknown1);
+  printf("%d ", attr->unknown2);
+}
+
+static void ifoPrint_subp_attributes(int level, subp_attr_t *attr) {
+  
+  if(attr->type == 0
+     && attr->lang_code == 0
+     && attr->zero1 == 0
+     && attr->zero2 == 0
+     && attr->lang_extension== 0) {
+    printf("-- Unspecified --");
+    return;
+  }
+  
+  printf("type %02x ", attr->type);
+  
+  if(isalpha((int)(attr->lang_code >> 8))
+     && isalpha((int)(attr->lang_code & 0xff))) {
+    printf("%c%c ", attr->lang_code >> 8, attr->lang_code & 0xff);
+  } else {
+    printf("%02x%02x ", 0xff & (unsigned)(attr->lang_code >> 8), 
+	   0xff & (unsigned)(attr->lang_code & 0xff));
+  }
+  
+  printf("%d ", attr->zero1);
+  printf("%d ", attr->zero2);
+
+  switch(attr->lang_extension) {
+  case 0:
+    printf("Not specified ");
+    break;
+  case 1:
+    printf("Caption with normal size character ");
+    break;
+  case 2:
+    printf("Caption with bigger size character ");
+    break;
+  case 3: 
+    printf("Caption for children ");
+    break;
+  case 4:
+    printf("reserved ");
+    break;
+  case 5:
+    printf("Closed Caption with normal size character ");
+    break;
+  case 6:
+    printf("Closed Caption with bigger size character ");
+    break;
+  case 7:
+    printf("Closed Caption for children ");
+    break;
+  case 8:
+    printf("reserved ");
+    break;
+  case 9:
+    printf("Forced Caption");
+    break;
+  case 10:
+    printf("reserved ");
+    break;
+  case 11:
+    printf("reserved ");
+    break;
+  case 12:
+    printf("reserved ");
+    break;
+  case 13:
+    printf("Director's comments with normal size character ");
+    break;
+  case 14:
+    printf("Director's comments with bigger size character ");
+    break;
+  case 15:
+    printf("Director's comments for children ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+
+}
+
+
+static void ifoPrint_USER_OPS(user_ops_t *user_ops) {
+  uint32_t uops;
+  unsigned char *ptr = (unsigned char *)user_ops;
+  
+  uops  = (*ptr++ << 24);
+  uops |= (*ptr++ << 16);
+  uops |= (*ptr++ << 8);
+  uops |= (*ptr++);
+  
+  if(uops == 0) {
+    printf("None\n");
+  } else if(uops == 0x01ffffff) {
+    printf("All\n");
+  } else {
+    if(user_ops->title_or_time_play)
+      printf("Title or Time Play, ");
+    if(user_ops->chapter_search_or_play)
+      printf("Chapter Search or Play, ");
+    if(user_ops->title_play)
+      printf("Title Play, ");
+    if(user_ops->stop)
+      printf("Stop, ");
+    if(user_ops->go_up)
+      printf("Go Up, ");
+    if(user_ops->time_or_chapter_search)
+      printf("Time or Chapter Search, ");
+    if(user_ops->prev_or_top_pg_search)
+      printf("Prev or Top PG Search, ");
+    if(user_ops->next_pg_search)
+      printf("Next PG Search, ");
+    if(user_ops->forward_scan)
+      printf("Forward Scan, ");
+    if(user_ops->backward_scan)
+      printf("Backward Scan, ");
+    if(user_ops->title_menu_call)
+      printf("Title Menu Call, ");
+    if(user_ops->root_menu_call)
+      printf("Root Menu Call, ");
+    if(user_ops->subpic_menu_call)
+      printf("SubPic Menu Call, ");
+    if(user_ops->audio_menu_call)
+      printf("Audio Menu Call, ");
+    if(user_ops->angle_menu_call)
+      printf("Angle Menu Call, ");
+    if(user_ops->chapter_menu_call)
+      printf("Chapter Menu Call, ");
+    if(user_ops->resume)
+      printf("Resume, ");
+    if(user_ops->button_select_or_activate)
+      printf("Button Select or Activate, ");
+    if(user_ops->still_off)
+      printf("Still Off, ");
+    if(user_ops->pause_on)
+      printf("Pause On, ");
+    if(user_ops->audio_stream_change)
+      printf("Audio Stream Change, ");
+    if(user_ops->subpic_stream_change)
+      printf("SubPic Stream Change, ");
+    if(user_ops->angle_change)
+      printf("Angle Change, ");
+    if(user_ops->karaoke_audio_pres_mode_change)
+      printf("Karaoke Audio Pres Mode Change, ");
+    if(user_ops->video_pres_mode_change)
+      printf("Video Pres Mode Change, ");
+    printf("\n");
+  }
+}
+
+
+void ifoPrint_VMGI_MAT(vmgi_mat_t *vmgi_mat) {
+  
+  printf("VMG Identifier: %.12s\n", vmgi_mat->vmg_identifier);
+  printf("Last Sector of VMG: %08x\n", vmgi_mat->vmg_last_sector);
+  printf("Last Sector of VMGI: %08x\n", vmgi_mat->vmgi_last_sector);
+  printf("Specification version number: %01x.%01x\n", 
+	 vmgi_mat->specification_version >> 4, 
+	 vmgi_mat->specification_version & 0xf);
+  /* Byte 2 of 'VMG Category' (00xx0000) is the Region Code */
+  printf("VMG Category: %08x\n", vmgi_mat->vmg_category);
+  printf("VMG Number of Volumes: %i\n", vmgi_mat->vmg_nr_of_volumes);
+  printf("VMG This Volume: %i\n", vmgi_mat->vmg_this_volume_nr);
+  printf("Disc side %i\n", vmgi_mat->disc_side);
+  printf("VMG Number of Title Sets %i\n", vmgi_mat->vmg_nr_of_title_sets);
+  printf("Provider ID: %.32s\n", vmgi_mat->provider_identifier);
+  printf("VMG POS Code: %08x", (uint32_t)(vmgi_mat->vmg_pos_code >> 32));
+  printf("%08x\n", (uint32_t)vmgi_mat->vmg_pos_code);
+  printf("End byte of VMGI_MAT: %08x\n", vmgi_mat->vmgi_last_byte);
+  printf("Start byte of First Play PGC FP PGC: %08x\n", 
+	 vmgi_mat->first_play_pgc);
+  printf("Start sector of VMGM_VOBS: %08x\n", vmgi_mat->vmgm_vobs);
+  printf("Start sector of TT_SRPT: %08x\n", vmgi_mat->tt_srpt);
+  printf("Start sector of VMGM_PGCI_UT: %08x\n", vmgi_mat->vmgm_pgci_ut);
+  printf("Start sector of PTL_MAIT: %08x\n", vmgi_mat->ptl_mait);
+  printf("Start sector of VTS_ATRT: %08x\n", vmgi_mat->vts_atrt);
+  printf("Start sector of TXTDT_MG: %08x\n", vmgi_mat->txtdt_mgi);
+  printf("Start sector of VMGM_C_ADT: %08x\n", vmgi_mat->vmgm_c_adt);
+  printf("Start sector of VMGM_VOBU_ADMAP: %08x\n", 
+	 vmgi_mat->vmgm_vobu_admap);
+  printf("Video attributes of VMGM_VOBS: ");
+  ifoPrint_video_attributes(5, &vmgi_mat->vmgm_video_attr);
+  printf("\n");
+  printf("VMGM Number of Audio attributes: %i\n", 
+	 vmgi_mat->nr_of_vmgm_audio_streams);
+  if(vmgi_mat->nr_of_vmgm_audio_streams > 0) {
+    printf("\tstream %i status: ", 1);
+    ifoPrint_audio_attributes(5, &vmgi_mat->vmgm_audio_attr);
+    printf("\n");
+  }
+  printf("VMGM Number of Sub-picture attributes: %i\n", 
+	 vmgi_mat->nr_of_vmgm_subp_streams);
+  if(vmgi_mat->nr_of_vmgm_subp_streams > 0) {
+    printf("\tstream %2i status: ", 1);
+    ifoPrint_subp_attributes(5, &vmgi_mat->vmgm_subp_attr);
+    printf("\n");
+  }
+}
+
+
+void ifoPrint_VTSI_MAT(vtsi_mat_t *vtsi_mat) {
+  int i;
+
+  printf("VTS Identifier: %.12s\n", vtsi_mat->vts_identifier);
+  printf("Last Sector of VTS: %08x\n", vtsi_mat->vts_last_sector);
+  printf("Last Sector of VTSI: %08x\n", vtsi_mat->vtsi_last_sector);
+  printf("Specification version number: %01x.%01x\n", 
+	 vtsi_mat->specification_version>>4, 
+	 vtsi_mat->specification_version&0xf);
+  printf("VTS Category: %08x\n", vtsi_mat->vts_category);
+  printf("End byte of VTSI_MAT: %08x\n", vtsi_mat->vtsi_last_byte);
+  printf("Start sector of VTSM_VOBS:  %08x\n", vtsi_mat->vtsm_vobs);
+  printf("Start sector of VTSTT_VOBS: %08x\n", vtsi_mat->vtstt_vobs);
+  printf("Start sector of VTS_PTT_SRPT: %08x\n", vtsi_mat->vts_ptt_srpt);
+  printf("Start sector of VTS_PGCIT:    %08x\n", vtsi_mat->vts_pgcit);
+  printf("Start sector of VTSM_PGCI_UT: %08x\n", vtsi_mat->vtsm_pgci_ut);
+  printf("Start sector of VTS_TMAPT:    %08x\n", vtsi_mat->vts_tmapt);
+  printf("Start sector of VTSM_C_ADT:      %08x\n", vtsi_mat->vtsm_c_adt);
+  printf("Start sector of VTSM_VOBU_ADMAP: %08x\n",vtsi_mat->vtsm_vobu_admap);
+  printf("Start sector of VTS_C_ADT:       %08x\n", vtsi_mat->vts_c_adt);
+  printf("Start sector of VTS_VOBU_ADMAP:  %08x\n", vtsi_mat->vts_vobu_admap);
+
+  printf("Video attributes of VTSM_VOBS: ");
+  ifoPrint_video_attributes(5, &vtsi_mat->vtsm_video_attr);
+  printf("\n");
+  
+  printf("VTSM Number of Audio attributes: %i\n", 
+	 vtsi_mat->nr_of_vtsm_audio_streams);
+  if(vtsi_mat->nr_of_vtsm_audio_streams > 0) {
+    printf("\tstream %i status: ", 1);
+    ifoPrint_audio_attributes(5, &vtsi_mat->vtsm_audio_attr);
+    printf("\n");
+  }
+  
+  printf("VTSM Number of Sub-picture attributes: %i\n", 
+	 vtsi_mat->nr_of_vtsm_subp_streams);
+  if(vtsi_mat->nr_of_vtsm_subp_streams > 0) {
+    printf("\tstream %2i status: ", 1);
+    ifoPrint_subp_attributes(5, &vtsi_mat->vtsm_subp_attr);
+    printf("\n");
+  }
+  
+  printf("Video attributes of VTS_VOBS: ");
+  ifoPrint_video_attributes(5, &vtsi_mat->vts_video_attr);
+  printf("\n");
+  
+  printf("VTS Number of Audio attributes: %i\n", 
+	 vtsi_mat->nr_of_vts_audio_streams);
+  for(i = 0; i < vtsi_mat->nr_of_vts_audio_streams; i++) {
+    printf("\tstream %i status: ", i);
+    ifoPrint_audio_attributes(5, &vtsi_mat->vts_audio_attr[i]);
+    printf("\n");
+  }
+  
+  printf("VTS Number of Subpicture attributes: %i\n", 
+	 vtsi_mat->nr_of_vts_subp_streams);
+  for(i = 0; i < vtsi_mat->nr_of_vts_subp_streams; i++) {
+    printf("\tstream %2i status: ", i);
+    ifoPrint_subp_attributes(5, &vtsi_mat->vts_subp_attr[i]);
+    printf("\n");
+  }
+}
+
+
+static void ifoPrint_PGC_COMMAND_TBL(pgc_command_tbl_t *cmd_tbl) {
+  int i;
+  
+  if(cmd_tbl == NULL) {
+    printf("No Command table present\n");
+    return;
+  }
+  
+  printf("Number of Pre commands: %i\n", cmd_tbl->nr_of_pre);
+  for(i = 0; i < cmd_tbl->nr_of_pre; i++) {
+    ifoPrint_CMD(i, &cmd_tbl->pre_cmds[i]);
+  }
+
+  printf("Number of Post commands: %i\n", cmd_tbl->nr_of_post);
+  for(i = 0; i < cmd_tbl->nr_of_post; i++) {
+    ifoPrint_CMD(i, &cmd_tbl->post_cmds[i]);
+  }
+
+  printf("Number of Cell commands: %i\n", cmd_tbl->nr_of_cell);
+  for(i = 0; i < cmd_tbl->nr_of_cell; i++) {
+    ifoPrint_CMD(i, &cmd_tbl->cell_cmds[i]);
+  }
+}
+
+
+static void ifoPrint_PGC_PROGRAM_MAP(pgc_program_map_t *program_map, int nr) {
+  int i;
+  
+  if(program_map == NULL) {
+    printf("No Program map present\n");
+    return;
+  }
+  
+  for(i = 0; i < nr; i++) {
+    printf("Program %3i Entry Cell: %3i\n", i + 1, program_map[i]);
+  }
+}
+
+
+static void ifoPrint_CELL_PLAYBACK(cell_playback_t *cell_playback, int nr) {
+  int i;
+  
+  if(cell_playback == NULL) {
+    printf("No Cell Playback info present\n");
+    return;
+  }
+  
+  for(i=0;i<nr;i++) {
+    printf("Cell: %3i ", i + 1);
+
+    ifoPrint_time(5, &cell_playback[i].playback_time);
+    printf("\t");
+
+    if(cell_playback[i].block_mode || cell_playback[i].block_type) {
+      const char *s;
+      switch(cell_playback[i].block_mode) {
+      case 0:
+	s = "not a"; break;
+      case 1:
+	s = "the first"; break;
+      case 2:
+      default:
+	s = ""; break;
+      case 3:
+	s = "last"; break;
+      }
+      printf("%s cell in the block ", s);
+      
+      switch(cell_playback[i].block_type) {
+      case 0:
+	printf("not part of the block ");
+	break;
+      case 1:
+	printf("angle block ");
+	break;
+      case 2:
+      case 3:
+	printf("(send bug repport) ");
+	break;
+      }
+    }
+    if(cell_playback[i].seamless_play)
+      printf("presented seamlessly ");
+    if(cell_playback[i].interleaved)
+      printf("cell is interleaved ");
+    if(cell_playback[i].stc_discontinuity)
+      printf("STC_discontinuty ");
+    if(cell_playback[i].seamless_angle)
+      printf("only seamless angle ");
+    if(cell_playback[i].restricted)
+      printf("restricted cell ");
+    
+    if(cell_playback[i].still_time)
+      printf("still time %d ", cell_playback[i].still_time);
+    if(cell_playback[i].cell_cmd_nr)
+      printf("cell command %d", cell_playback[i].cell_cmd_nr);
+    
+    printf("\n\tStart sector: %08x\tFirst ILVU end  sector: %08x\n", 
+	   cell_playback[i].first_sector, 
+	   cell_playback[i].first_ilvu_end_sector);
+    printf("\tEnd   sector: %08x\tLast VOBU start sector: %08x\n", 
+	   cell_playback[i].last_sector, 
+	   cell_playback[i].last_vobu_start_sector);
+  }
+}
+
+static void ifoPrint_CELL_POSITION(cell_position_t *cell_position, int nr) {
+  int i;
+  
+  if(cell_position == NULL) {
+    printf("No Cell Position info present\n");
+    return;
+  }
+  
+  for(i=0;i<nr;i++) {
+    printf("Cell: %3i has VOB ID: %3i, Cell ID: %3i\n", i + 1, 
+	   cell_position[i].vob_id_nr, cell_position[i].cell_nr);
+  }
+}
+
+
+void ifoPrint_PGC(pgc_t *pgc) {
+  int i;
+  
+  printf("Number of Programs: %i\n", pgc->nr_of_programs);
+  printf("Number of Cells: %i\n", pgc->nr_of_cells);
+  /* Check that time is 0:0:0:0 also if nr_of_programs==0 */
+  printf("Playback time: ");
+  ifoPrint_time(5, &pgc->playback_time); printf("\n");
+
+  /* If no programs/no time then does this mean anything? */
+  printf("Prohibited user operations: ");
+  ifoPrint_USER_OPS(&pgc->prohibited_ops);
+  
+    for(i = 0; i < 8; i++) {
+      if(pgc->audio_control[i] & 0x8000) { /* The 'is present' bit */
+	printf("Audio stream %i control: %04x\n", 
+	       i, pgc->audio_control[i]);
+      }
+    }
+  
+  for(i = 0; i < 32; i++) {
+    if(pgc->subp_control[i] & 0x80000000) { /* The 'is present' bit */
+      printf("Subpicture stream %2i control: %08x\n", 
+	     i, pgc->subp_control[i]);
+    }
+  }
+  
+  printf("Next PGC number: %i\n", pgc->next_pgc_nr);
+  printf("Prev PGC number: %i\n", pgc->prev_pgc_nr);
+  printf("GoUp PGC number: %i\n", pgc->goup_pgc_nr);
+  if(pgc->nr_of_programs != 0) {
+    printf("Still time: %i seconds (255=inf)\n", pgc->still_time);
+    printf("PG Playback mode %02x\n", pgc->pg_playback_mode);
+  }
+  
+  if(pgc->nr_of_programs != 0) {
+    for(i = 0; i < 16; i++) {
+      printf("Color %2i: %08x\n", i, pgc->palette[i]);
+    }
+  }
+  
+  /* Memmory offsets to div. tables. */
+  ifoPrint_PGC_COMMAND_TBL(pgc->command_tbl);
+  ifoPrint_PGC_PROGRAM_MAP(pgc->program_map, pgc->nr_of_programs);
+  ifoPrint_CELL_PLAYBACK(pgc->cell_playback, pgc->nr_of_cells);
+  ifoPrint_CELL_POSITION(pgc->cell_position, pgc->nr_of_cells);
+}
+
+
+void ifoPrint_TT_SRPT(tt_srpt_t *tt_srpt) {
+  int i;
+  
+  printf("Number of TitleTrack search pointers: %i\n",
+	 tt_srpt->nr_of_srpts);
+  for(i=0;i<tt_srpt->nr_of_srpts;i++) {
+    printf("Title Track index %i\n", i + 1);
+    printf("\tTitle set number (VTS): %i", 
+	   tt_srpt->title[i].title_set_nr);
+    printf("\tVTS_TTN: %i\n", tt_srpt->title[i].vts_ttn);
+    printf("\tNumber of PTTs: %i\n", tt_srpt->title[i].nr_of_ptts);
+    printf("\tNumber of angles: %i\n", 
+	   tt_srpt->title[i].nr_of_angles);
+    printf("\tTitle playback type: %02x\n",     /* XXX: TODO FIXME */
+	   *(uint8_t *)&(tt_srpt->title[i].pb_ty));
+    printf("\tParental ID field: %04x\n",
+	   tt_srpt->title[i].parental_id);
+    printf("\tTitle set starting sector %08x\n", 
+	   tt_srpt->title[i].title_set_sector);
+  }
+}
+
+
+void ifoPrint_VTS_PTT_SRPT(vts_ptt_srpt_t *vts_ptt_srpt) {
+  int i, j;
+  printf(" nr_of_srpts %i last byte %i\n", 
+	 vts_ptt_srpt->nr_of_srpts, 
+	 vts_ptt_srpt->last_byte);
+  for(i=0;i<vts_ptt_srpt->nr_of_srpts;i++) {
+    for(j=0;j<vts_ptt_srpt->title[i].nr_of_ptts;j++) {
+      printf("VTS_PTT_SRPT - Title %3i part %3i: PGC: %3i PG: %3i\n",
+	     i + 1, j + 1, 
+	     vts_ptt_srpt->title[i].ptt[j].pgcn,
+	     vts_ptt_srpt->title[i].ptt[j].pgn );
+    }
+  }
+}
+
+
+static void hexdump(uint8_t *ptr, int len) {
+  while(len--)
+    printf("%02x ", *ptr++);
+}
+
+void ifoPrint_PTL_MAIT(ptl_mait_t *ptl_mait) {
+  int i, j;
+  
+  printf("Number of Countries: %i\n", ptl_mait->nr_of_countries);
+  printf("Number of VTSs: %i\n", ptl_mait->nr_of_vtss);
+  //printf("Last byte: %i\n", ptl_mait->last_byte);
+  
+  for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+    printf("Country code: %c%c\n", 
+	   ptl_mait->countries[i].country_code >> 8,
+	   ptl_mait->countries[i].country_code & 0xff);
+    /*
+      printf("Start byte: %04x %i\n", 
+      ptl_mait->countries[i].pf_ptl_mai_start_byte, 
+      ptl_mait->countries[i].pf_ptl_mai_start_byte);
+    */
+    /* This seems to be pointing at a array with 8 2byte fields per VTS
+       ? and one extra for the menu? always an odd number of VTSs on
+       all the dics I tested so it might be padding to even also.
+       If it is for the menu it probably the first entry.  */
+    for(j=0;j<8;j++) {
+      hexdump( (uint8_t *)ptl_mait->countries - PTL_MAIT_COUNTRY_SIZE 
+	       + ptl_mait->countries[i].pf_ptl_mai_start_byte
+	       + j*(ptl_mait->nr_of_vtss+1)*2, (ptl_mait->nr_of_vtss+1)*2);
+      printf("\n");
+    }
+  }
+}
+
+
+void ifoPrint_C_ADT(c_adt_t *c_adt) {
+  int i, entries;
+  
+  printf("Number of VOBs in this VOBS: %i\n", c_adt->nr_of_vobs);
+  //entries = c_adt->nr_of_vobs;
+  entries = (c_adt->last_byte + 1 - C_ADT_SIZE)/sizeof(c_adt_t);
+  
+  for(i = 0; i < entries; i++) {
+    printf("VOB ID: %3i, Cell ID: %3i   ", 
+	   c_adt->cell_adr_table[i].vob_id, c_adt->cell_adr_table[i].cell_id);
+    printf("Sector (first): 0x%08x   (last): 0x%08x\n",
+	   c_adt->cell_adr_table[i].start_sector, 
+	   c_adt->cell_adr_table[i].last_sector);
+  }
+}
+
+
+void ifoPrint_VOBU_ADMAP(vobu_admap_t *vobu_admap) {
+  int i, entries;
+  
+  entries = (vobu_admap->last_byte + 1 - VOBU_ADMAP_SIZE)/4;
+  for(i = 0; i < entries; i++) {
+    printf("VOBU %5i  First sector: 0x%08x\n", i + 1,
+	   vobu_admap->vobu_start_sectors[i]);
+  }
+}
+
+
+void ifoPrint_PGCIT(pgcit_t *pgcit) {
+  int i;
+  
+  for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+    printf("\nProgram (PGC): %3i\t", i + 1);
+    printf("PGC Category: Entry id 0x%02x, ", pgcit->pgci_srp[i].entry_id);
+    printf("Parental ID mask 0x%04x\n", pgcit->pgci_srp[i].ptl_id_mask);
+    ifoPrint_PGC(pgcit->pgci_srp[i].pgc);
+  }
+}
+
+
+void ifoPrint_PGCI_UT(pgci_ut_t *pgci_ut) {
+  int i;
+  
+  printf("Number of Menu Language Units (PGCI_LU): %3i\n", pgci_ut->nr_of_lus);
+  for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+    printf("\nMenu Language Code: %c%c\n",
+	   pgci_ut->lu[i].lang_code >> 8,
+	   pgci_ut->lu[i].lang_code & 0xff);
+    printf("Menu Existence: %02x\n", pgci_ut->lu[i].exists);
+    ifoPrint_PGCIT(pgci_ut->lu[i].pgcit);
+  }
+}
+
+
+static void ifoPrint_VTS_ATTRIBUTES(vts_attributes_t *vts_attributes) {
+  int i;
+  
+  printf("VTS_CAT Application type: %08x\n", vts_attributes->vts_cat);
+ 
+  printf("Video attributes of VTSM_VOBS: ");
+  ifoPrint_video_attributes(5, &vts_attributes->vtsm_vobs_attr);
+  printf("\n");
+  printf("Number of Audio streams: %i\n", 
+	 vts_attributes->nr_of_vtsm_audio_streams);
+  if(vts_attributes->nr_of_vtsm_audio_streams > 0) {
+    printf("\tstream %i attributes: ", 1);
+    ifoPrint_audio_attributes(5, &vts_attributes->vtsm_audio_attr);
+    printf("\n");
+  }
+  printf("Number of Subpicture streams: %i\n", 
+	 vts_attributes->nr_of_vtsm_subp_streams);
+  if(vts_attributes->nr_of_vtsm_subp_streams > 0) {
+    printf("\tstream %2i attributes: ", 1);
+    ifoPrint_subp_attributes(5, &vts_attributes->vtsm_subp_attr);
+    printf("\n");
+  }
+   
+  printf("Video attributes of VTSTT_VOBS: ");
+  ifoPrint_video_attributes(5, &vts_attributes->vtstt_vobs_video_attr);
+  printf("\n");
+  printf("Number of Audio streams: %i\n", 
+	 vts_attributes->nr_of_vtstt_audio_streams);
+  for(i = 0; i < vts_attributes->nr_of_vtstt_audio_streams; i++) {
+    printf("\tstream %i attributes: ", i);
+    ifoPrint_audio_attributes(5, &vts_attributes->vtstt_audio_attr[i]);
+    printf("\n");
+  }
+  
+  printf("Number of Subpicture streams: %i\n", 
+	 vts_attributes->nr_of_vtstt_subp_streams);
+  for(i = 0; i < vts_attributes->nr_of_vtstt_subp_streams; i++) {
+    printf("\tstream %2i attributes: ", i);    
+    ifoPrint_subp_attributes(5, &vts_attributes->vtstt_subp_attr[i]);
+    printf("\n");
+  }
+}
+
+
+void ifoPrint_VTS_ATRT(vts_atrt_t *vts_atrt) {
+  int i;
+  
+  printf("Number of Video Title Sets: %3i\n", vts_atrt->nr_of_vtss);
+  for(i = 0; i < vts_atrt->nr_of_vtss; i++) {
+    printf("\nVideo Title Set %i\n", i + 1);
+    ifoPrint_VTS_ATTRIBUTES(&vts_atrt->vts[i]);
+  }
+}
+
+
+void ifoPrint(dvd_reader_t *dvd, int title) {
+  ifo_handle_t *ifohandle;
+
+  ifohandle = ifoOpen(dvd, title);
+  if(!ifohandle) {
+    fprintf(stderr, "Can't open info file for title %d\n", title);
+    return;
+  }
+  
+  
+  if(ifohandle->vmgi_mat) {
+
+    printf("VMG top level\n-------------\n");
+    ifoPrint_VMGI_MAT(ifohandle->vmgi_mat);
+
+    printf("\nFirst Play PGC\n--------------\n");
+    ifoPrint_PGC(ifohandle->first_play_pgc);
+
+    printf("\nTitle Track search pointer table\n");
+    printf(  "------------------------------------------------\n");
+    ifoPrint_TT_SRPT(ifohandle->tt_srpt);
+
+    printf("\nMenu PGCI Unit table\n");
+    printf(  "--------------------\n");
+    if(ifohandle->pgci_ut) {
+      ifoPrint_PGCI_UT(ifohandle->pgci_ut);
+    } else {
+      printf("No PGCI Unit table present\n");
+    }
+
+    printf("\nParental Manegment Information table\n");
+    printf(  "------------------------------------\n");
+    if(ifohandle->ptl_mait) {
+      ifoPrint_PTL_MAIT(ifohandle->ptl_mait);
+    } else {
+      printf("No Parental Management Information present\n");
+    }
+
+    printf("\nVideo Title Set Attribute Table\n");
+    printf(  "-------------------------------\n");
+    ifoPrint_VTS_ATRT(ifohandle->vts_atrt);
+    
+    printf("\nText Data Manager Information\n");
+    printf(  "-----------------------------\n");
+    if(ifohandle->txtdt_mgi) {
+      //ifoPrint_TXTDT_MGI(&(vmgi->txtdt_mgi));
+    } else {
+      printf("No Text Data Manager Information present\n");
+    }
+
+    printf("\nMenu Cell Adress table\n");
+    printf(  "-----------------\n");
+    if(ifohandle->menu_c_adt) {
+      ifoPrint_C_ADT(ifohandle->menu_c_adt);
+    } else {
+      printf("No Menu Cell Adress table present\n");
+    }
+
+    printf("\nVideo Manager 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");   
+    }
+  }
+
+
+  if(ifohandle->vtsi_mat) {
+
+    printf("VTS top level\n-------------\n");
+    ifoPrint_VTSI_MAT(ifohandle->vtsi_mat);
+
+    printf("\nPart of Title Track search pointer table\n");
+    printf(  "----------------------------------------------\n");
+    ifoPrint_VTS_PTT_SRPT(ifohandle->vts_ptt_srpt);
+
+    printf("\nPGCI Unit table\n");
+    printf(  "--------------------\n");
+    ifoPrint_PGCIT(ifohandle->vts_pgcit);
+
+    printf("\nMenu PGCI Unit table\n");
+    printf(  "--------------------\n");
+    if(ifohandle->pgci_ut) {
+      ifoPrint_PGCI_UT(ifohandle->pgci_ut);
+    } else {
+      printf("No Menu PGCI Unit table present\n");
+    }
+
+    printf("\nMenu Cell Adress table\n");
+    printf(  "-----------------\n");
+    if(ifohandle->menu_c_adt) {
+      ifoPrint_C_ADT(ifohandle->menu_c_adt);
+    } else {
+      printf("No Cell Adress table present\n");
+    }
+
+    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);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdvdkit2/ifo_print.h	Fri Aug 16 22:37:48 2002 +0000
@@ -0,0 +1,58 @@
+#ifndef IFO_PRINT_H_INCLUDED
+#define IFO_PRINT_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001 Björn Englund <d4bjorn@dtek.chalmers.se>,
+ *                          Håkan Hjort <d95hjort@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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdvdkit2/ifo_read.c	Fri Aug 16 22:37:48 2002 +0000
@@ -0,0 +1,1798 @@
+/*
+ * Copyright (C) 2000, 2001, 2002  Björn Englund <d4bjorn@dtek.chalmers.se>, 
+ *                                 Håkan Hjort <d95hjort@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 <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <string.h>
+#include <assert.h>
+
+#include "dvd_reader.h"
+
+#include "config.h" // Needed for WORDS_BIGENDIAN
+#include "bswap.h"
+#include "ifo_types.h"
+#include "ifo_read.h"
+
+#ifndef DVD_BLOCK_LEN
+#define DVD_BLOCK_LEN 2048
+#endif
+
+#ifndef NDEBUG
+#define CHECK_ZERO(arg) \
+  if(memcmp(my_friendly_zeros, &arg, sizeof(arg))) { \
+    unsigned int i_CZ; \
+    fprintf(stderr, "*** Zero check failed in %s:%i\n    for %s = 0x", \
+            __FILE__, __LINE__, # arg ); \
+    for(i_CZ = 0; i_CZ < sizeof(arg); i_CZ++) \
+      fprintf(stderr, "%02x", *((uint8_t *)&arg + i_CZ)); \
+    fprintf(stderr, "\n"); \
+  }
+static const uint8_t my_friendly_zeros[2048];
+#else
+#define CHECK_ZERO(arg) (void)(arg)
+#endif
+
+
+/* Prototypes for internal functions */
+static int ifoRead_VMG(ifo_handle_t *ifofile);
+static int ifoRead_VTS(ifo_handle_t *ifofile);
+static int ifoRead_PGC(ifo_handle_t *ifofile, pgc_t *pgc, unsigned int offset);
+static int ifoRead_PGC_COMMAND_TBL(ifo_handle_t *ifofile, 
+                                   pgc_command_tbl_t *cmd_tbl, 
+				   unsigned int offset);
+static int ifoRead_PGC_PROGRAM_MAP(ifo_handle_t *ifofile, 
+                                   pgc_program_map_t *program_map, 
+                                   unsigned int nr, unsigned int offset);
+static int ifoRead_CELL_PLAYBACK_TBL(ifo_handle_t *ifofile, 
+                                     cell_playback_t *cell_playback, 
+                                     unsigned int nr, unsigned int offset);
+static int ifoRead_CELL_POSITION_TBL(ifo_handle_t *ifofile, 
+                                     cell_position_t *cell_position, 
+                                     unsigned int nr, unsigned int offset);
+static int ifoRead_VTS_ATTRIBUTES(ifo_handle_t *ifofile, 
+                                  vts_attributes_t *vts_attributes, 
+                                  unsigned int offset);
+static int ifoRead_C_ADT_internal(ifo_handle_t *ifofile, c_adt_t *c_adt, 
+                                  unsigned int sector);
+static int ifoRead_VOBU_ADMAP_internal(ifo_handle_t *ifofile, 
+                                       vobu_admap_t *vobu_admap, 
+				       unsigned int sector);
+static int ifoRead_PGCIT_internal(ifo_handle_t *ifofile, pgcit_t *pgcit, 
+                                  unsigned int offset);
+
+static void ifoFree_PGC(pgc_t *pgc);
+static void ifoFree_PGC_COMMAND_TBL(pgc_command_tbl_t *cmd_tbl);
+static void ifoFree_PGCIT_internal(pgcit_t *pgcit);
+
+
+static int DVDFileSeek_( dvd_file_t *dvd_file, uint32_t offset ) {
+  return (DVDFileSeek(dvd_file, (int)offset) == (int)offset);
+}
+
+
+ifo_handle_t *ifoOpen(dvd_reader_t *dvd, int title) {
+  ifo_handle_t *ifofile;
+
+  ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
+  if(!ifofile)
+    return 0;
+
+  memset(ifofile, 0, sizeof(ifo_handle_t));
+
+  ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_FILE);
+  if(!ifofile->file) {
+    if(title) {
+      fprintf(stderr, "libdvdread: Can't open file VTS_%02d_0.IFO.\n", title);
+    } else {
+      fprintf(stderr, "libdvdread: Can't open file VIDEO_TS.IFO.\n");
+    }
+    free(ifofile);
+    return 0;
+  }
+
+  /* First check if this is a VMGI file. */
+  if(ifoRead_VMG(ifofile)) {
+
+    /* These are both mandatory. */
+    if(!ifoRead_FP_PGC(ifofile) || !ifoRead_TT_SRPT(ifofile)) {
+      fprintf(stderr, "libdvdread: Invalid main menu IFO (VIDEO_TS.IFO).\n");
+      ifoClose(ifofile);
+      return 0;
+    }
+
+    ifoRead_PGCI_UT(ifofile);
+    ifoRead_PTL_MAIT(ifofile);
+
+    /* This is also mandatory. */
+    if(!ifoRead_VTS_ATRT(ifofile)) {
+      fprintf(stderr, "libdvdread: Invalid main menu IFO (VIDEO_TS.IFO).\n");
+      ifoClose(ifofile);
+      return 0;
+    }
+
+    ifoRead_TXTDT_MGI(ifofile);
+    ifoRead_C_ADT(ifofile);
+    ifoRead_VOBU_ADMAP(ifofile);
+
+    return ifofile;
+  }
+
+  if(ifoRead_VTS(ifofile)) {
+
+    if(!ifoRead_VTS_PTT_SRPT(ifofile) || !ifoRead_PGCIT(ifofile)) {
+      fprintf(stderr, "libdvdread: Invalid title IFO (VTS_%02d_0.IFO).\n",
+              title);
+      ifoClose(ifofile);
+      return 0;
+    }
+
+
+    ifoRead_PGCI_UT(ifofile);
+    ifoRead_C_ADT(ifofile);
+    ifoRead_VOBU_ADMAP(ifofile);
+
+    if(!ifoRead_TITLE_C_ADT(ifofile) || !ifoRead_TITLE_VOBU_ADMAP(ifofile)) {
+      fprintf(stderr, "libdvdread: Invalid title IFO (VTS_%02d_0.IFO).\n",
+              title);
+      ifoClose(ifofile);
+      return 0;
+    }
+
+    return ifofile;
+  }
+
+  if(title) {
+    fprintf(stderr, "libdvdread: Invalid IFO for title %d (VTS_%02d_0.IFO).\n",
+	    title, title);
+  } else {
+    fprintf(stderr, "libdvdread: Invalid IFO for VMGM (VIDEO_TS.IFO).\n");
+  }
+  ifoClose(ifofile);
+  return 0;
+}
+
+
+ifo_handle_t *ifoOpenVMGI(dvd_reader_t *dvd) {
+  ifo_handle_t *ifofile;
+
+  ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
+  if(!ifofile)
+    return 0;
+
+  memset(ifofile, 0, sizeof(ifo_handle_t));
+
+  ifofile->file = DVDOpenFile(dvd, 0, DVD_READ_INFO_FILE);
+  if(!ifofile->file) {
+    fprintf(stderr, "libdvdread: Can't open file VIDEO_TS.IFO.\n");
+    free(ifofile);
+    return 0;
+  }
+
+  if(ifoRead_VMG(ifofile))
+    return ifofile;
+
+  fprintf(stderr, "libdvdread: Invalid main menu IFO (VIDEO_TS.IFO).\n");
+  ifoClose(ifofile);
+  return 0;
+}
+
+
+ifo_handle_t *ifoOpenVTSI(dvd_reader_t *dvd, int title) {
+  ifo_handle_t *ifofile;
+  
+  ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
+  if(!ifofile)
+    return 0;
+
+  memset(ifofile, 0, sizeof(ifo_handle_t));
+  
+  if(title <= 0 || title > 99) {
+    fprintf(stderr, "libdvdread: ifoOpenVTSI invalid title (%d).\n", title);
+    free(ifofile);
+    return 0;
+  }
+    
+  ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_FILE);
+  if(!ifofile->file) {
+    fprintf(stderr, "libdvdread: Can't open file VTS_%02d_0.IFO.\n", title);
+    free(ifofile);
+    return 0;
+  }
+
+  ifoRead_VTS(ifofile);
+  if(ifofile->vtsi_mat)
+    return ifofile;
+
+  fprintf(stderr, "libdvdread: Invalid IFO for title %d (VTS_%02d_0.IFO).\n",
+          title, title);
+  ifoClose(ifofile);
+  return 0;
+}
+
+
+void ifoClose(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  ifoFree_VOBU_ADMAP(ifofile);
+  ifoFree_TITLE_VOBU_ADMAP(ifofile);
+  ifoFree_C_ADT(ifofile);
+  ifoFree_TITLE_C_ADT(ifofile);
+  ifoFree_TXTDT_MGI(ifofile);
+  ifoFree_VTS_ATRT(ifofile);
+  ifoFree_PTL_MAIT(ifofile);
+  ifoFree_PGCI_UT(ifofile);
+  ifoFree_TT_SRPT(ifofile);
+  ifoFree_FP_PGC(ifofile);
+  ifoFree_PGCIT(ifofile);
+  ifoFree_VTS_PTT_SRPT(ifofile);
+
+  if(ifofile->vmgi_mat)
+    free(ifofile->vmgi_mat);
+
+  if(ifofile->vtsi_mat)
+    free(ifofile->vtsi_mat);
+
+  DVDCloseFile(ifofile->file);
+  ifofile->file = 0;
+  free(ifofile);
+  ifofile = 0;
+}
+
+
+static int ifoRead_VMG(ifo_handle_t *ifofile) {
+  vmgi_mat_t *vmgi_mat;
+
+  vmgi_mat = (vmgi_mat_t *)malloc(sizeof(vmgi_mat_t));
+  if(!vmgi_mat)
+    return 0;
+
+  ifofile->vmgi_mat = vmgi_mat;
+
+  if(!DVDFileSeek_(ifofile->file, 0)) {
+    free(ifofile->vmgi_mat);
+    ifofile->vmgi_mat = 0;
+    return 0;
+  }
+
+  if(!DVDReadBytes(ifofile->file, vmgi_mat, sizeof(vmgi_mat_t))) {
+    free(ifofile->vmgi_mat);
+    ifofile->vmgi_mat = 0;
+    return 0;
+  }
+
+  if(strncmp("DVDVIDEO-VMG", vmgi_mat->vmg_identifier, 12) != 0) {
+    free(ifofile->vmgi_mat);
+    ifofile->vmgi_mat = 0;
+    return 0;
+  }
+  
+  B2N_32(vmgi_mat->vmg_last_sector);
+  B2N_32(vmgi_mat->vmgi_last_sector);
+  B2N_32(vmgi_mat->vmg_category);
+  B2N_16(vmgi_mat->vmg_nr_of_volumes);
+  B2N_16(vmgi_mat->vmg_this_volume_nr);
+  B2N_16(vmgi_mat->vmg_nr_of_title_sets);
+  B2N_64(vmgi_mat->vmg_pos_code);
+  B2N_32(vmgi_mat->vmgi_last_byte);
+  B2N_32(vmgi_mat->first_play_pgc);
+  B2N_32(vmgi_mat->vmgm_vobs);
+  B2N_32(vmgi_mat->tt_srpt);
+  B2N_32(vmgi_mat->vmgm_pgci_ut);
+  B2N_32(vmgi_mat->ptl_mait);
+  B2N_32(vmgi_mat->vts_atrt);
+  B2N_32(vmgi_mat->txtdt_mgi);
+  B2N_32(vmgi_mat->vmgm_c_adt);
+  B2N_32(vmgi_mat->vmgm_vobu_admap);
+  B2N_16(vmgi_mat->vmgm_audio_attr.lang_code);
+  B2N_16(vmgi_mat->vmgm_subp_attr.lang_code);
+
+
+  CHECK_ZERO(vmgi_mat->zero_1);
+  CHECK_ZERO(vmgi_mat->zero_2);
+  CHECK_ZERO(vmgi_mat->zero_3);
+  CHECK_ZERO(vmgi_mat->zero_4);
+  CHECK_ZERO(vmgi_mat->zero_5);
+  CHECK_ZERO(vmgi_mat->zero_6);
+  CHECK_ZERO(vmgi_mat->zero_7);
+  CHECK_ZERO(vmgi_mat->zero_8);
+  CHECK_ZERO(vmgi_mat->zero_9);
+  CHECK_ZERO(vmgi_mat->zero_10);  
+  assert(vmgi_mat->vmg_last_sector != 0);
+  assert(vmgi_mat->vmgi_last_sector != 0);
+  assert(vmgi_mat->vmgi_last_sector * 2 <= vmgi_mat->vmg_last_sector);
+  assert(vmgi_mat->vmg_nr_of_volumes != 0);
+  assert(vmgi_mat->vmg_this_volume_nr != 0);
+  assert(vmgi_mat->vmg_this_volume_nr <= vmgi_mat->vmg_nr_of_volumes);
+  assert(vmgi_mat->disc_side == 1 || vmgi_mat->disc_side == 2);
+  assert(vmgi_mat->vmg_nr_of_title_sets != 0);
+  assert(vmgi_mat->vmgi_last_byte >= 341);
+  assert(vmgi_mat->vmgi_last_byte / DVD_BLOCK_LEN <= 
+         vmgi_mat->vmgi_last_sector);
+  /* It seems that first_play_pgc might be optional. */
+  assert(vmgi_mat->first_play_pgc != 0 && 
+         vmgi_mat->first_play_pgc < vmgi_mat->vmgi_last_byte);
+  assert(vmgi_mat->vmgm_vobs == 0 || 
+        (vmgi_mat->vmgm_vobs > vmgi_mat->vmgi_last_sector &&
+         vmgi_mat->vmgm_vobs < vmgi_mat->vmg_last_sector));
+  assert(vmgi_mat->tt_srpt <= vmgi_mat->vmgi_last_sector);
+  assert(vmgi_mat->vmgm_pgci_ut <= vmgi_mat->vmgi_last_sector);
+  assert(vmgi_mat->ptl_mait <= vmgi_mat->vmgi_last_sector);
+  assert(vmgi_mat->vts_atrt <= vmgi_mat->vmgi_last_sector);
+  assert(vmgi_mat->txtdt_mgi <= vmgi_mat->vmgi_last_sector);
+  assert(vmgi_mat->vmgm_c_adt <= vmgi_mat->vmgi_last_sector);
+  assert(vmgi_mat->vmgm_vobu_admap <= vmgi_mat->vmgi_last_sector);
+
+  assert(vmgi_mat->nr_of_vmgm_audio_streams <= 1);
+  assert(vmgi_mat->nr_of_vmgm_subp_streams <= 1);
+
+  return 1;
+}
+
+
+static int ifoRead_VTS(ifo_handle_t *ifofile) {
+  vtsi_mat_t *vtsi_mat;
+  int i;
+
+  vtsi_mat = (vtsi_mat_t *)malloc(sizeof(vtsi_mat_t));
+  if(!vtsi_mat)
+    return 0;
+  
+  ifofile->vtsi_mat = vtsi_mat;
+
+  if(!DVDFileSeek_(ifofile->file, 0)) {
+    free(ifofile->vtsi_mat);
+    ifofile->vtsi_mat = 0;
+    return 0;
+  }
+
+  if(!(DVDReadBytes(ifofile->file, vtsi_mat, sizeof(vtsi_mat_t)))) {
+    free(ifofile->vtsi_mat);
+    ifofile->vtsi_mat = 0;
+    return 0;
+  }
+
+  if(strncmp("DVDVIDEO-VTS", vtsi_mat->vts_identifier, 12) != 0) {
+    free(ifofile->vtsi_mat);
+    ifofile->vtsi_mat = 0;
+    return 0;
+  }
+
+  B2N_32(vtsi_mat->vts_last_sector);
+  B2N_32(vtsi_mat->vtsi_last_sector);
+  B2N_32(vtsi_mat->vts_category);
+  B2N_32(vtsi_mat->vtsi_last_byte);
+  B2N_32(vtsi_mat->vtsm_vobs);
+  B2N_32(vtsi_mat->vtstt_vobs);
+  B2N_32(vtsi_mat->vts_ptt_srpt);
+  B2N_32(vtsi_mat->vts_pgcit);
+  B2N_32(vtsi_mat->vtsm_pgci_ut);
+  B2N_32(vtsi_mat->vts_tmapt);
+  B2N_32(vtsi_mat->vtsm_c_adt);
+  B2N_32(vtsi_mat->vtsm_vobu_admap);
+  B2N_32(vtsi_mat->vts_c_adt);
+  B2N_32(vtsi_mat->vts_vobu_admap);
+  B2N_16(vtsi_mat->vtsm_audio_attr.lang_code);
+  B2N_16(vtsi_mat->vtsm_subp_attr.lang_code);
+  for(i = 0; i < 8; i++)
+    B2N_16(vtsi_mat->vts_audio_attr[i].lang_code);
+  for(i = 0; i < 32; i++)
+    B2N_16(vtsi_mat->vts_subp_attr[i].lang_code);
+
+
+  CHECK_ZERO(vtsi_mat->zero_1);
+  CHECK_ZERO(vtsi_mat->zero_2);
+  CHECK_ZERO(vtsi_mat->zero_3);
+  CHECK_ZERO(vtsi_mat->zero_4);
+  CHECK_ZERO(vtsi_mat->zero_5);
+  CHECK_ZERO(vtsi_mat->zero_6);
+  CHECK_ZERO(vtsi_mat->zero_7);
+  CHECK_ZERO(vtsi_mat->zero_8);
+  CHECK_ZERO(vtsi_mat->zero_9);
+  CHECK_ZERO(vtsi_mat->zero_10);
+  CHECK_ZERO(vtsi_mat->zero_11);
+  CHECK_ZERO(vtsi_mat->zero_12);
+  CHECK_ZERO(vtsi_mat->zero_13);
+  CHECK_ZERO(vtsi_mat->zero_14);
+  CHECK_ZERO(vtsi_mat->zero_15);
+  CHECK_ZERO(vtsi_mat->zero_16);
+  CHECK_ZERO(vtsi_mat->zero_17);
+  CHECK_ZERO(vtsi_mat->zero_18);
+  CHECK_ZERO(vtsi_mat->zero_19);
+  CHECK_ZERO(vtsi_mat->zero_20);
+  assert(vtsi_mat->vtsi_last_sector*2 <= vtsi_mat->vts_last_sector);
+  assert(vtsi_mat->vtsi_last_byte/DVD_BLOCK_LEN <= vtsi_mat->vtsi_last_sector);
+  assert(vtsi_mat->vtsm_vobs == 0 || 
+        (vtsi_mat->vtsm_vobs > vtsi_mat->vtsi_last_sector &&
+         vtsi_mat->vtsm_vobs < vtsi_mat->vts_last_sector));
+  assert(vtsi_mat->vtstt_vobs == 0 || 
+        (vtsi_mat->vtstt_vobs > vtsi_mat->vtsi_last_sector &&
+         vtsi_mat->vtstt_vobs < vtsi_mat->vts_last_sector));
+  assert(vtsi_mat->vts_ptt_srpt <= vtsi_mat->vtsi_last_sector);
+  assert(vtsi_mat->vts_pgcit <= vtsi_mat->vtsi_last_sector);
+  assert(vtsi_mat->vtsm_pgci_ut <= vtsi_mat->vtsi_last_sector);
+  assert(vtsi_mat->vts_tmapt <= vtsi_mat->vtsi_last_sector);
+  assert(vtsi_mat->vtsm_c_adt <= vtsi_mat->vtsi_last_sector);
+  assert(vtsi_mat->vtsm_vobu_admap <= vtsi_mat->vtsi_last_sector);
+  assert(vtsi_mat->vts_c_adt <= vtsi_mat->vtsi_last_sector);
+  assert(vtsi_mat->vts_vobu_admap <= vtsi_mat->vtsi_last_sector);
+  
+  assert(vtsi_mat->nr_of_vtsm_audio_streams <= 1);
+  assert(vtsi_mat->nr_of_vtsm_subp_streams <= 1);
+
+  assert(vtsi_mat->nr_of_vts_audio_streams <= 8);
+  for(i = vtsi_mat->nr_of_vts_audio_streams; i < 8; i++)
+    CHECK_ZERO(vtsi_mat->vts_audio_attr[i]);
+
+  assert(vtsi_mat->nr_of_vts_subp_streams <= 32);
+  for(i = vtsi_mat->nr_of_vts_subp_streams; i < 32; i++)
+    CHECK_ZERO(vtsi_mat->vts_subp_attr[i]);      
+
+  return 1;
+}
+
+
+static int ifoRead_PGC_COMMAND_TBL(ifo_handle_t *ifofile, 
+                                   pgc_command_tbl_t *cmd_tbl, 
+				   unsigned int offset) {
+  
+  memset(cmd_tbl, 0, sizeof(pgc_command_tbl_t));
+  
+  if(!DVDFileSeek_(ifofile->file, offset))
+    return 0;
+
+  if(!(DVDReadBytes(ifofile->file, cmd_tbl, PGC_COMMAND_TBL_SIZE)))
+    return 0;
+
+  B2N_16(cmd_tbl->nr_of_pre);
+  B2N_16(cmd_tbl->nr_of_post);
+  B2N_16(cmd_tbl->nr_of_cell);
+
+  assert(cmd_tbl->nr_of_pre + cmd_tbl->nr_of_post + cmd_tbl->nr_of_cell<= 255);
+     
+  if(cmd_tbl->nr_of_pre != 0) {
+    unsigned int pre_cmds_size  = cmd_tbl->nr_of_pre * COMMAND_DATA_SIZE;
+    cmd_tbl->pre_cmds = (vm_cmd_t *)malloc(pre_cmds_size);
+    if(!cmd_tbl->pre_cmds)
+      return 0;
+
+    if(!(DVDReadBytes(ifofile->file, cmd_tbl->pre_cmds, pre_cmds_size))) {
+      free(cmd_tbl->pre_cmds);
+      return 0;
+    }
+  }
+  
+  if(cmd_tbl->nr_of_post != 0) {
+    unsigned int post_cmds_size = cmd_tbl->nr_of_post * COMMAND_DATA_SIZE;
+    cmd_tbl->post_cmds = (vm_cmd_t *)malloc(post_cmds_size);
+    if(!cmd_tbl->post_cmds) {
+      if(cmd_tbl->pre_cmds) 
+	free(cmd_tbl->pre_cmds);
+      return 0;
+    }
+    if(!(DVDReadBytes(ifofile->file, cmd_tbl->post_cmds, post_cmds_size))) {
+      if(cmd_tbl->pre_cmds) 
+	free(cmd_tbl->pre_cmds);
+      free(cmd_tbl->post_cmds);
+      return 0;
+    }
+  }
+
+  if(cmd_tbl->nr_of_cell != 0) {
+    unsigned int cell_cmds_size = cmd_tbl->nr_of_cell * COMMAND_DATA_SIZE;
+    cmd_tbl->cell_cmds = (vm_cmd_t *)malloc(cell_cmds_size);
+    if(!cmd_tbl->cell_cmds) {
+      if(cmd_tbl->pre_cmds)
+	free(cmd_tbl->pre_cmds);
+      if(cmd_tbl->post_cmds)
+	free(cmd_tbl->post_cmds);
+      return 0;
+    }
+    if(!(DVDReadBytes(ifofile->file, cmd_tbl->cell_cmds, cell_cmds_size))) {
+      if(cmd_tbl->pre_cmds) 
+	free(cmd_tbl->pre_cmds);
+      if(cmd_tbl->post_cmds) 
+	free(cmd_tbl->post_cmds);
+      free(cmd_tbl->cell_cmds);
+      return 0;
+    }
+  }
+  
+  /* 
+   * Make a run over all the commands and see that we can interpret them all?
+   */
+  return 1;
+}
+
+
+static void ifoFree_PGC_COMMAND_TBL(pgc_command_tbl_t *cmd_tbl) {
+  if(cmd_tbl) {
+    if(cmd_tbl->nr_of_pre && cmd_tbl->pre_cmds)
+      free(cmd_tbl->pre_cmds);
+    if(cmd_tbl->nr_of_post && cmd_tbl->post_cmds)
+      free(cmd_tbl->post_cmds);
+    if(cmd_tbl->nr_of_cell && cmd_tbl->cell_cmds)
+      free(cmd_tbl->cell_cmds);
+    free(cmd_tbl);
+  }
+}
+
+static int ifoRead_PGC_PROGRAM_MAP(ifo_handle_t *ifofile, 
+                                   pgc_program_map_t *program_map, 
+				   unsigned int nr, unsigned int offset) {
+  unsigned int size = nr * sizeof(pgc_program_map_t);
+
+  if(!DVDFileSeek_(ifofile->file, offset))
+    return 0;
+ 
+  if(!(DVDReadBytes(ifofile->file, program_map, size)))
+    return 0;
+
+  return 1;
+}
+
+static int ifoRead_CELL_PLAYBACK_TBL(ifo_handle_t *ifofile, 
+                                     cell_playback_t *cell_playback,
+                                     unsigned int nr, unsigned int offset) {
+  unsigned int i;
+  unsigned int size = nr * sizeof(cell_playback_t);
+
+  if(!DVDFileSeek_(ifofile->file, offset))
+    return 0;
+
+  if(!(DVDReadBytes(ifofile->file, cell_playback, size)))
+    return 0;
+
+  for(i = 0; i < nr; i++) {
+    B2N_32(cell_playback[i].first_sector);
+    B2N_32(cell_playback[i].first_ilvu_end_sector);
+    B2N_32(cell_playback[i].last_vobu_start_sector);
+    B2N_32(cell_playback[i].last_sector);
+    
+    /* Changed < to <= because this was false in the movie 'Pi'. */
+    assert(cell_playback[i].last_vobu_start_sector <= 
+           cell_playback[i].last_sector);
+    assert(cell_playback[i].first_sector <= 
+           cell_playback[i].last_vobu_start_sector);
+  }
+
+  return 1;
+}
+
+
+static int ifoRead_CELL_POSITION_TBL(ifo_handle_t *ifofile, 
+                                     cell_position_t *cell_position, 
+                                     unsigned int nr, unsigned int offset) {
+  unsigned int i;
+  unsigned int size = nr * sizeof(cell_position_t);
+
+  if(!DVDFileSeek_(ifofile->file, offset))
+    return 0;
+
+  if(!(DVDReadBytes(ifofile->file, cell_position, size)))
+    return 0;
+
+  for(i = 0; i < nr; i++) {
+    B2N_16(cell_position[i].vob_id_nr);
+    CHECK_ZERO(cell_position[i].zero_1);
+  }
+
+  return 1;
+}
+
+static int ifoRead_PGC(ifo_handle_t *ifofile, pgc_t *pgc, unsigned int offset) {
+  unsigned int i;
+
+  if(!DVDFileSeek_(ifofile->file, offset))
+    return 0;
+ 
+  if(!(DVDReadBytes(ifofile->file, pgc, PGC_SIZE)))
+    return 0;
+
+  B2N_16(pgc->next_pgc_nr);
+  B2N_16(pgc->prev_pgc_nr);
+  B2N_16(pgc->goup_pgc_nr);
+  B2N_16(pgc->command_tbl_offset);
+  B2N_16(pgc->program_map_offset);
+  B2N_16(pgc->cell_playback_offset);
+  B2N_16(pgc->cell_position_offset);
+
+  for(i = 0; i < 8; i++)
+    B2N_16(pgc->audio_control[i]);
+  for(i = 0; i < 32; i++)
+    B2N_32(pgc->subp_control[i]);
+  for(i = 0; i < 16; i++)
+    B2N_32(pgc->palette[i]);
+  
+  CHECK_ZERO(pgc->zero_1);
+  assert(pgc->nr_of_programs <= pgc->nr_of_cells);
+
+  /* verify time (look at print_time) */
+  for(i = 0; i < 8; i++)
+    if(!pgc->audio_control[i] & 0x8000) /* The 'is present' bit */
+      CHECK_ZERO(pgc->audio_control[i]);
+  for(i = 0; i < 32; i++)
+    if(!pgc->subp_control[i] & 0x80000000) /* The 'is present' bit */
+      CHECK_ZERO(pgc->subp_control[i]);
+  
+  /* Check that time is 0:0:0:0 also if nr_of_programs == 0 */
+  if(pgc->nr_of_programs == 0) {
+    CHECK_ZERO(pgc->still_time);
+    CHECK_ZERO(pgc->pg_playback_mode); // ??
+    assert(pgc->program_map_offset == 0);
+    assert(pgc->cell_playback_offset == 0);
+    assert(pgc->cell_position_offset == 0);
+  } else {
+    assert(pgc->program_map_offset != 0);
+    assert(pgc->cell_playback_offset != 0);
+    assert(pgc->cell_position_offset != 0);
+  }
+  
+  if(pgc->command_tbl_offset != 0) {
+    pgc->command_tbl = malloc(sizeof(pgc_command_tbl_t));
+    if(!pgc->command_tbl)
+      return 0;
+
+    if(!ifoRead_PGC_COMMAND_TBL(ifofile, pgc->command_tbl, 
+                                offset + pgc->command_tbl_offset)) {
+      free(pgc->command_tbl);
+      return 0;
+    }
+  } else {
+    pgc->command_tbl = NULL;
+  }
+  
+  if(pgc->program_map_offset != 0) {
+    pgc->program_map = malloc(pgc->nr_of_programs * sizeof(pgc_program_map_t));
+    if(!pgc->program_map) {
+      ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+      return 0;
+    }
+    if(!ifoRead_PGC_PROGRAM_MAP(ifofile, pgc->program_map,pgc->nr_of_programs,
+                                offset + pgc->program_map_offset)) {
+      ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+      free(pgc->program_map);
+      return 0;
+    }
+  } else {
+    pgc->program_map = NULL;
+  }
+  
+  if(pgc->cell_playback_offset != 0) {
+    pgc->cell_playback = malloc(pgc->nr_of_cells * sizeof(cell_playback_t));
+    if(!pgc->cell_playback) {
+      ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+      if(pgc->program_map)
+	free(pgc->program_map);
+      return 0;
+    }
+    if(!ifoRead_CELL_PLAYBACK_TBL(ifofile, pgc->cell_playback, 
+				  pgc->nr_of_cells,
+                                  offset + pgc->cell_playback_offset)) {
+      ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+      if(pgc->program_map)
+	free(pgc->program_map);
+      free(pgc->cell_playback);
+      return 0;
+    }
+  } else {
+    pgc->cell_playback = NULL;
+  }
+  
+  if(pgc->cell_position_offset != 0) {
+    pgc->cell_position = malloc(pgc->nr_of_cells * sizeof(cell_position_t));
+    if(!pgc->cell_position) {
+      ifoFree_PGC(pgc);
+      return 0;
+    }
+    if(!ifoRead_CELL_POSITION_TBL(ifofile, pgc->cell_position, 
+				  pgc->nr_of_cells,
+                                  offset + pgc->cell_position_offset)) {
+      ifoFree_PGC(pgc);
+      return 0;
+    }
+  } else {
+    pgc->cell_position = NULL;
+  }
+
+  return 1;
+}
+
+int ifoRead_FP_PGC(ifo_handle_t *ifofile) {
+
+  if(!ifofile)
+    return 0;
+
+  if(!ifofile->vmgi_mat)
+    return 0;
+  
+  /* It seems that first_play_pgc might be optional after all. */
+  if(ifofile->vmgi_mat->first_play_pgc == 0) { /* mandatory */
+    ifofile->first_play_pgc = 0;
+    return 0; /* change this to a 1 if it's optional. */
+  }
+  
+  ifofile->first_play_pgc = (pgc_t *)malloc(sizeof(pgc_t));
+  if(!ifofile->first_play_pgc)
+    return 0;
+  
+  if(!ifoRead_PGC(ifofile, ifofile->first_play_pgc, 
+                  ifofile->vmgi_mat->first_play_pgc)) {
+    free(ifofile->first_play_pgc);
+    ifofile->first_play_pgc = 0;
+    return 0;
+  }
+
+  return 1;
+}
+
+static void ifoFree_PGC(pgc_t *pgc) {
+  if(pgc) {
+    ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+    if(pgc->program_map)
+      free(pgc->program_map);
+    if(pgc->cell_playback)
+      free(pgc->cell_playback);
+    if(pgc->cell_position)
+      free(pgc->cell_position);
+  }
+}
+
+void ifoFree_FP_PGC(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  if(ifofile->first_play_pgc) {
+    ifoFree_PGC(ifofile->first_play_pgc);
+    free(ifofile->first_play_pgc);
+    ifofile->first_play_pgc = 0;
+  }
+}
+
+
+int ifoRead_TT_SRPT(ifo_handle_t *ifofile) {
+  tt_srpt_t *tt_srpt;
+  int i, info_length;
+
+  if(!ifofile)
+    return 0;
+
+  if(!ifofile->vmgi_mat)
+    return 0;
+
+  if(ifofile->vmgi_mat->tt_srpt == 0) /* mandatory */
+    return 0;
+
+  if(!DVDFileSeek_(ifofile->file, ifofile->vmgi_mat->tt_srpt * DVD_BLOCK_LEN))
+    return 0;
+
+  tt_srpt = (tt_srpt_t *)malloc(sizeof(tt_srpt_t));
+  if(!tt_srpt)
+    return 0;
+
+  ifofile->tt_srpt = tt_srpt;
+  
+  if(!(DVDReadBytes(ifofile->file, tt_srpt, TT_SRPT_SIZE))) {
+    fprintf(stderr, "libdvdread: Unable to read read TT_SRPT.\n");
+    free(tt_srpt);
+    return 0;
+  }
+
+  B2N_16(tt_srpt->nr_of_srpts);
+  B2N_32(tt_srpt->last_byte);
+  
+  info_length = tt_srpt->last_byte + 1 - TT_SRPT_SIZE;
+
+  tt_srpt->title = (title_info_t *)malloc(info_length); 
+  if(!tt_srpt->title) {
+    free(tt_srpt);
+    ifofile->tt_srpt = 0;
+    return 0;
+  }
+  if(!(DVDReadBytes(ifofile->file, tt_srpt->title, info_length))) {
+    fprintf(stderr, "libdvdread: Unable to read read TT_SRPT.\n");
+    ifoFree_TT_SRPT(ifofile);
+    return 0;
+  }
+
+  for(i =  0; i < tt_srpt->nr_of_srpts; i++) {
+    B2N_16(tt_srpt->title[i].nr_of_ptts);
+    B2N_16(tt_srpt->title[i].parental_id);
+    B2N_32(tt_srpt->title[i].title_set_sector);
+  }
+  
+
+  CHECK_ZERO(tt_srpt->zero_1);
+  assert(tt_srpt->nr_of_srpts != 0);
+  assert(tt_srpt->nr_of_srpts < 100); // ??
+  assert((int)tt_srpt->nr_of_srpts * sizeof(title_info_t) <= info_length);
+  
+  for(i = 0; i < tt_srpt->nr_of_srpts; i++) {
+    assert(tt_srpt->title[i].pb_ty.zero_1 == 0);
+    assert(tt_srpt->title[i].nr_of_angles != 0);
+    assert(tt_srpt->title[i].nr_of_angles < 10);
+    //assert(tt_srpt->title[i].nr_of_ptts != 0);
+    // XXX: this assertion breaks Ghostbusters:
+    assert(tt_srpt->title[i].nr_of_ptts < 1000); // ??
+    assert(tt_srpt->title[i].title_set_nr != 0);
+    assert(tt_srpt->title[i].title_set_nr < 100); // ??
+    assert(tt_srpt->title[i].vts_ttn != 0);
+    assert(tt_srpt->title[i].vts_ttn < 100); // ??
+    //assert(tt_srpt->title[i].title_set_sector != 0);
+  }
+  
+  // Make this a function
+#if 0
+  if(memcmp((uint8_t *)tt_srpt->title + 
+            tt_srpt->nr_of_srpts * sizeof(title_info_t), 
+            my_friendly_zeros, 
+            info_length - tt_srpt->nr_of_srpts * sizeof(title_info_t))) {
+    fprintf(stderr, "VMG_PTT_SRPT slack is != 0, ");
+    hexdump((uint8_t *)tt_srpt->title + 
+            tt_srpt->nr_of_srpts * sizeof(title_info_t), 
+            info_length - tt_srpt->nr_of_srpts * sizeof(title_info_t));
+  }
+#endif
+
+  return 1;
+}
+
+
+void ifoFree_TT_SRPT(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  if(ifofile->tt_srpt) {
+    free(ifofile->tt_srpt->title);
+    free(ifofile->tt_srpt);
+    ifofile->tt_srpt = 0;
+  }
+}
+
+
+int ifoRead_VTS_PTT_SRPT(ifo_handle_t *ifofile) {
+  vts_ptt_srpt_t *vts_ptt_srpt;
+  int info_length, i, j;
+  uint32_t *data;
+
+  if(!ifofile)
+    return 0;
+  
+  if(!ifofile->vtsi_mat)
+    return 0;
+
+  if(ifofile->vtsi_mat->vts_ptt_srpt == 0) /* mandatory */
+    return 0;
+    
+  if(!DVDFileSeek_(ifofile->file,
+		   ifofile->vtsi_mat->vts_ptt_srpt * DVD_BLOCK_LEN))
+    return 0;
+
+  vts_ptt_srpt = (vts_ptt_srpt_t *)malloc(sizeof(vts_ptt_srpt_t));
+  if(!vts_ptt_srpt)
+    return 0;
+
+  ifofile->vts_ptt_srpt = vts_ptt_srpt;
+
+  if(!(DVDReadBytes(ifofile->file, vts_ptt_srpt, VTS_PTT_SRPT_SIZE))) {
+    fprintf(stderr, "libdvdread: Unable to read PTT search table.\n");
+    free(vts_ptt_srpt);
+    return 0;
+  }
+
+  B2N_16(vts_ptt_srpt->nr_of_srpts);
+  B2N_32(vts_ptt_srpt->last_byte);
+
+  CHECK_ZERO(vts_ptt_srpt->zero_1);
+  assert(vts_ptt_srpt->nr_of_srpts != 0);
+  assert(vts_ptt_srpt->nr_of_srpts < 100); // ??
+  
+  info_length = vts_ptt_srpt->last_byte + 1 - VTS_PTT_SRPT_SIZE;
+  
+  data = (uint32_t *)malloc(info_length); 
+  if(!data) {
+    free(vts_ptt_srpt);
+    ifofile->vts_ptt_srpt = 0;
+    return 0;
+  }
+  if(!(DVDReadBytes(ifofile->file, data, info_length))) {
+    fprintf(stderr, "libdvdread: Unable to read PTT search table.\n");
+    free(vts_ptt_srpt);
+    free(data);
+    ifofile->vts_ptt_srpt = 0;
+    return 0;
+  }
+
+  for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+    B2N_32(data[i]);
+    /* assert(data[i] + sizeof(ptt_info_t) <= vts_ptt_srpt->last_byte + 1);
+       Magic Knight Rayearth Daybreak is mastered very strange and has 
+       Titles with 0 PTTs. They all have a data[i] offsets beyond the end of
+       of the vts_ptt_srpt structure. */
+    assert(data[i] + sizeof(ptt_info_t) <= vts_ptt_srpt->last_byte + 1 + 4);
+  }
+  
+  vts_ptt_srpt->title = malloc(vts_ptt_srpt->nr_of_srpts * sizeof(ttu_t));
+  if(!vts_ptt_srpt->title) {
+    free(vts_ptt_srpt);
+    free(data);
+    ifofile->vts_ptt_srpt = 0;
+    return 0;
+  }
+  for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+    int n;
+    if(i < vts_ptt_srpt->nr_of_srpts - 1)
+      n = (data[i+1] - data[i]);
+    else
+      n = (vts_ptt_srpt->last_byte + 1 - data[i]);
+    /* assert(n > 0 && (n % 4) == 0);
+       Magic Knight Rayearth Daybreak is mastered very strange and has 
+       Titles with 0 PTTs. */
+    if(n < 0) n = 0;
+    assert(n % 4 == 0);
+    
+    vts_ptt_srpt->title[i].nr_of_ptts = n / 4;
+    vts_ptt_srpt->title[i].ptt = malloc(n * sizeof(ptt_info_t));
+    if(!vts_ptt_srpt->title[i].ptt) {
+      for(n = 0; n < i; n++)
+        free(vts_ptt_srpt->title[n].ptt);
+      free(vts_ptt_srpt);
+      free(data);
+      ifofile->vts_ptt_srpt = 0;
+      return 0;
+    }
+    for(j = 0; j < vts_ptt_srpt->title[i].nr_of_ptts; j++) {
+      /* The assert placed here because of Magic Knight Rayearth Daybreak */
+      assert(data[i] + sizeof(ptt_info_t) <= vts_ptt_srpt->last_byte + 1);
+      vts_ptt_srpt->title[i].ptt[j].pgcn 
+        = *(uint16_t*)(((char *)data) + data[i] + 4*j - VTS_PTT_SRPT_SIZE);
+      vts_ptt_srpt->title[i].ptt[j].pgn 
+        = *(uint16_t*)(((char *)data) + data[i] + 4*j + 2 - VTS_PTT_SRPT_SIZE);
+    }
+  }
+  free(data);
+  
+  for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+    for(j = 0; j < vts_ptt_srpt->title[i].nr_of_ptts; j++) {
+      B2N_16(vts_ptt_srpt->title[i].ptt[j].pgcn);
+      B2N_16(vts_ptt_srpt->title[i].ptt[j].pgn);
+    }
+  }
+  
+  for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+    assert(vts_ptt_srpt->title[i].nr_of_ptts < 1000); // ??
+    for(j = 0; j < vts_ptt_srpt->title[i].nr_of_ptts; j++) {
+      assert(vts_ptt_srpt->title[i].ptt[j].pgcn != 0 );
+      assert(vts_ptt_srpt->title[i].ptt[j].pgcn < 1000); // ??
+      assert(vts_ptt_srpt->title[i].ptt[j].pgn != 0);
+      assert(vts_ptt_srpt->title[i].ptt[j].pgn < 100); // ??
+    }
+  }
+
+  return 1;
+}
+
+
+void ifoFree_VTS_PTT_SRPT(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  if(ifofile->vts_ptt_srpt) {
+    int i;
+    for(i = 0; i < ifofile->vts_ptt_srpt->nr_of_srpts; i++)
+      free(ifofile->vts_ptt_srpt->title[i].ptt);
+    free(ifofile->vts_ptt_srpt->title);
+    free(ifofile->vts_ptt_srpt);
+    ifofile->vts_ptt_srpt = 0;
+  }
+}
+
+
+int ifoRead_PTL_MAIT(ifo_handle_t *ifofile) {
+  ptl_mait_t *ptl_mait;
+  int info_length;
+  unsigned int i;
+
+  if(!ifofile)
+    return 0;
+  
+  if(!ifofile->vmgi_mat)
+    return 0;
+  
+  if(ifofile->vmgi_mat->ptl_mait == 0)
+    return 1;
+
+  if(!DVDFileSeek_(ifofile->file, 
+		  ifofile->vmgi_mat->ptl_mait * DVD_BLOCK_LEN))
+    return 0;
+
+  ptl_mait = (ptl_mait_t *)malloc(sizeof(ptl_mait_t));
+  if(!ptl_mait)
+    return 0;
+
+  ifofile->ptl_mait = ptl_mait;
+
+  if(!(DVDReadBytes(ifofile->file, ptl_mait, PTL_MAIT_SIZE))) {
+    free(ptl_mait);
+    ifofile->ptl_mait = 0;
+    return 0;
+  }
+
+  B2N_16(ptl_mait->nr_of_countries);
+  B2N_16(ptl_mait->nr_of_vtss);
+  B2N_32(ptl_mait->last_byte);
+  
+  info_length = ptl_mait->last_byte + 1 - PTL_MAIT_SIZE;
+  
+  assert(ptl_mait->nr_of_countries != 0);
+  assert(ptl_mait->nr_of_countries < 100); // ??
+  assert(ptl_mait->nr_of_vtss != 0);
+  assert(ptl_mait->nr_of_vtss < 100); // ??  
+  assert(ptl_mait->nr_of_countries * PTL_MAIT_COUNTRY_SIZE <= info_length);
+  
+  /* Change this to read and 'translate' the tables too. 
+     I.e don't read so much here */
+  ptl_mait->countries = (ptl_mait_country_t *)malloc(info_length);
+  if(!ptl_mait->countries) {
+    free(ptl_mait);
+    ifofile->ptl_mait = 0;
+    return 0;
+  }
+  if(!(DVDReadBytes(ifofile->file, ptl_mait->countries, info_length))) {
+    fprintf(stderr, "libdvdread: Unable to read PTL_MAIT.\n");
+    ifoFree_PTL_MAIT(ifofile);
+    return 0;
+  }
+
+  for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+    B2N_16(ptl_mait->countries[i].country_code);
+    B2N_16(ptl_mait->countries[i].pf_ptl_mai_start_byte);
+  }
+  
+  for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+    CHECK_ZERO(ptl_mait->countries[i].zero_1);
+    CHECK_ZERO(ptl_mait->countries[i].zero_2);    
+    assert(ptl_mait->countries[i].pf_ptl_mai_start_byte + 
+           8 * (ptl_mait->nr_of_vtss + 1) * 2 <= ptl_mait->last_byte + 1);
+  }
+
+  return 1;
+}
+
+
+void ifoFree_PTL_MAIT(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  if(ifofile->ptl_mait) {
+    free(ifofile->ptl_mait->countries);
+    free(ifofile->ptl_mait);
+    ifofile->ptl_mait = 0;
+  }
+}
+
+int ifoRead_TITLE_C_ADT(ifo_handle_t *ifofile) {
+
+  if(!ifofile)
+    return 0;
+
+  if(!ifofile->vtsi_mat)
+    return 0;
+
+  if(ifofile->vtsi_mat->vts_c_adt == 0) /* mandatory */
+    return 0;
+
+  ifofile->vts_c_adt = (c_adt_t *)malloc(sizeof(c_adt_t));
+  if(!ifofile->vts_c_adt)
+    return 0;
+
+  if(!ifoRead_C_ADT_internal(ifofile, ifofile->vts_c_adt, 
+                             ifofile->vtsi_mat->vts_c_adt)) {
+    free(ifofile->vts_c_adt);
+    ifofile->vts_c_adt = 0;
+    return 0;
+  }
+
+  return 1;
+}
+
+int ifoRead_C_ADT(ifo_handle_t *ifofile) {
+  unsigned int sector;
+
+  if(!ifofile)
+    return 0;
+  
+  if(ifofile->vmgi_mat) {
+    if(ifofile->vmgi_mat->vmgm_c_adt == 0)
+      return 1;
+    sector = ifofile->vmgi_mat->vmgm_c_adt;
+  } else if(ifofile->vtsi_mat) {
+    if(ifofile->vtsi_mat->vtsm_c_adt == 0)
+      return 1;
+    sector = ifofile->vtsi_mat->vtsm_c_adt;
+  } else {
+    return 0;
+  }
+  
+  ifofile->menu_c_adt = (c_adt_t *)malloc(sizeof(c_adt_t));
+  if(!ifofile->menu_c_adt)
+    return 0;
+
+  if(!ifoRead_C_ADT_internal(ifofile, ifofile->menu_c_adt, sector)) {
+    free(ifofile->menu_c_adt);
+    ifofile->menu_c_adt = 0;
+    return 0;
+  }
+
+  return 1;
+}
+
+static int ifoRead_C_ADT_internal(ifo_handle_t *ifofile, 
+                                  c_adt_t *c_adt, unsigned int sector) {
+  int i, info_length;
+
+  if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN))
+    return 0;
+
+  if(!(DVDReadBytes(ifofile->file, c_adt, C_ADT_SIZE)))
+    return 0;
+
+  B2N_16(c_adt->nr_of_vobs);
+  B2N_32(c_adt->last_byte);
+  
+  info_length = c_adt->last_byte + 1 - C_ADT_SIZE;
+  
+  CHECK_ZERO(c_adt->zero_1);
+  /* assert(c_adt->nr_of_vobs > 0);  
+     Magic Knight Rayearth Daybreak is mastered very strange and has 
+     Titles with a VOBS that has no cells. */
+  assert(info_length % sizeof(cell_adr_t) == 0);
+  
+  /* assert(info_length / sizeof(cell_adr_t) >= c_adt->nr_of_vobs);
+     Enemy of the State region 2 (de) has Titles where nr_of_vobs field
+     is to high, they high ones are never referenced though. */
+  if(info_length / sizeof(cell_adr_t) < c_adt->nr_of_vobs) {
+    fprintf(stderr, "libdvdread: *C_ADT nr_of_vobs > avaiable info entries\n");
+    c_adt->nr_of_vobs = info_length / sizeof(cell_adr_t);
+  }
+  
+  c_adt->cell_adr_table = (cell_adr_t *)malloc(info_length);
+  if(!c_adt->cell_adr_table)
+    return 0;
+
+  if(info_length && 
+     !(DVDReadBytes(ifofile->file, c_adt->cell_adr_table, info_length))) {
+    free(c_adt->cell_adr_table);
+    return 0;
+  }
+
+  for(i = 0; i < info_length/sizeof(cell_adr_t); i++) {
+    B2N_16(c_adt->cell_adr_table[i].vob_id);
+    B2N_32(c_adt->cell_adr_table[i].start_sector);
+    B2N_32(c_adt->cell_adr_table[i].last_sector);
+
+    CHECK_ZERO(c_adt->cell_adr_table[i].zero_1);
+    assert(c_adt->cell_adr_table[i].vob_id > 0);
+    assert(c_adt->cell_adr_table[i].vob_id <= c_adt->nr_of_vobs);
+    assert(c_adt->cell_adr_table[i].cell_id > 0);
+    assert(c_adt->cell_adr_table[i].start_sector < 
+	   c_adt->cell_adr_table[i].last_sector);
+  }
+
+  return 1;
+}
+
+
+static void ifoFree_C_ADT_internal(c_adt_t *c_adt) {
+  if(c_adt) {
+    free(c_adt->cell_adr_table);
+    free(c_adt);
+  }
+}
+
+void ifoFree_C_ADT(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  ifoFree_C_ADT_internal(ifofile->menu_c_adt);
+  ifofile->menu_c_adt = 0;
+}
+
+void ifoFree_TITLE_C_ADT(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  ifoFree_C_ADT_internal(ifofile->vts_c_adt);
+  ifofile->vts_c_adt = 0;
+}
+
+int ifoRead_TITLE_VOBU_ADMAP(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return 0;
+
+  if(!ifofile->vtsi_mat)
+    return 0;
+  
+  if(ifofile->vtsi_mat->vts_vobu_admap == 0) /* mandatory */
+    return 0;
+  
+  ifofile->vts_vobu_admap = (vobu_admap_t *)malloc(sizeof(vobu_admap_t));
+  if(!ifofile->vts_vobu_admap)
+    return 0;
+
+  if(!ifoRead_VOBU_ADMAP_internal(ifofile, ifofile->vts_vobu_admap,
+                                  ifofile->vtsi_mat->vts_vobu_admap)) {
+    free(ifofile->vts_vobu_admap);
+    ifofile->vts_vobu_admap = 0;
+    return 0;
+  }
+
+  return 1;
+}
+
+int ifoRead_VOBU_ADMAP(ifo_handle_t *ifofile) {
+  unsigned int sector;
+
+  if(!ifofile)
+    return 0;
+     
+  if(ifofile->vmgi_mat) {
+    if(ifofile->vmgi_mat->vmgm_vobu_admap == 0)
+      return 1;
+    sector = ifofile->vmgi_mat->vmgm_vobu_admap;
+  } else if(ifofile->vtsi_mat) {
+    if(ifofile->vtsi_mat->vtsm_vobu_admap == 0)
+      return 1;
+    sector = ifofile->vtsi_mat->vtsm_vobu_admap;
+  } else {
+    return 0;
+  }
+  
+  ifofile->menu_vobu_admap = (vobu_admap_t *)malloc(sizeof(vobu_admap_t));
+  if(!ifofile->menu_vobu_admap)
+    return 0;
+  
+  if(!ifoRead_VOBU_ADMAP_internal(ifofile, ifofile->menu_vobu_admap, sector)) {
+    free(ifofile->menu_vobu_admap);
+    ifofile->menu_vobu_admap = 0;
+    return 0;
+  }
+
+  return 1;
+}
+
+static int ifoRead_VOBU_ADMAP_internal(ifo_handle_t *ifofile, 
+                                       vobu_admap_t *vobu_admap, 
+				       unsigned int sector) {
+  unsigned int i;
+  int info_length;
+
+  if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN))
+    return 0;
+
+  if(!(DVDReadBytes(ifofile->file, vobu_admap, VOBU_ADMAP_SIZE)))
+    return 0;
+
+  B2N_32(vobu_admap->last_byte);
+  
+  info_length = vobu_admap->last_byte + 1 - VOBU_ADMAP_SIZE;
+  /* assert(info_length > 0);
+     Magic Knight Rayearth Daybreak is mastered very strange and has 
+     Titles with a VOBS that has no VOBUs. */
+  assert(info_length % sizeof(uint32_t) == 0);
+  
+  vobu_admap->vobu_start_sectors = (uint32_t *)malloc(info_length); 
+  if(!vobu_admap->vobu_start_sectors) {
+    return 0;
+  }
+  if(info_length && 
+     !(DVDReadBytes(ifofile->file, 
+		    vobu_admap->vobu_start_sectors, info_length))) {
+    free(vobu_admap->vobu_start_sectors);
+    return 0;
+  }
+
+  for(i = 0; i < info_length/sizeof(uint32_t); i++)
+    B2N_32(vobu_admap->vobu_start_sectors[i]);
+
+  return 1;
+}
+
+
+static void ifoFree_VOBU_ADMAP_internal(vobu_admap_t *vobu_admap) {
+  if(vobu_admap) {
+    free(vobu_admap->vobu_start_sectors);
+    free(vobu_admap);
+  }
+}
+
+void ifoFree_VOBU_ADMAP(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  ifoFree_VOBU_ADMAP_internal(ifofile->menu_vobu_admap);
+  ifofile->menu_vobu_admap = 0;
+}
+
+void ifoFree_TITLE_VOBU_ADMAP(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  ifoFree_VOBU_ADMAP_internal(ifofile->vts_vobu_admap);
+  ifofile->vts_vobu_admap = 0;
+}
+
+int ifoRead_PGCIT(ifo_handle_t *ifofile) {
+
+  if(!ifofile)
+    return 0;
+  
+  if(!ifofile->vtsi_mat)
+    return 0;
+  
+  if(ifofile->vtsi_mat->vts_pgcit == 0) /* mandatory */
+    return 0;
+  
+  ifofile->vts_pgcit = (pgcit_t *)malloc(sizeof(pgcit_t));
+  if(!ifofile->vts_pgcit)
+    return 0;
+
+  if(!ifoRead_PGCIT_internal(ifofile, ifofile->vts_pgcit, 
+                             ifofile->vtsi_mat->vts_pgcit * DVD_BLOCK_LEN)) {
+    free(ifofile->vts_pgcit);
+    ifofile->vts_pgcit = 0;
+    return 0;
+  }
+
+  return 1;
+}
+
+static int ifoRead_PGCIT_internal(ifo_handle_t *ifofile, pgcit_t *pgcit, 
+                                  unsigned int offset) {
+  int i, info_length;
+  uint8_t *data, *ptr;
+  
+  if(!DVDFileSeek_(ifofile->file, offset))
+    return 0;
+
+  if(!(DVDReadBytes(ifofile->file, pgcit, PGCIT_SIZE)))
+    return 0;
+
+  B2N_16(pgcit->nr_of_pgci_srp);
+  B2N_32(pgcit->last_byte);
+  
+  CHECK_ZERO(pgcit->zero_1);
+  /* assert(pgcit->nr_of_pgci_srp != 0);
+     Magic Knight Rayearth Daybreak is mastered very strange and has 
+     Titles with 0 PTTs. */
+  assert(pgcit->nr_of_pgci_srp < 10000); // ?? seen max of 1338
+  
+  info_length = pgcit->nr_of_pgci_srp * PGCI_SRP_SIZE;
+  data = malloc(info_length);
+  if(!data)
+    return 0;
+
+  if(info_length && !(DVDReadBytes(ifofile->file, data, info_length))) {
+    free(data);
+    return 0;
+  }
+
+  pgcit->pgci_srp = malloc(pgcit->nr_of_pgci_srp * sizeof(pgci_srp_t));
+  if(!pgcit->pgci_srp) {
+    free(data);
+    return 0;
+  }
+  ptr = data;
+  for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+    memcpy(&pgcit->pgci_srp[i], ptr, PGCI_LU_SIZE);
+    ptr += PGCI_LU_SIZE;
+    B2N_16(pgcit->pgci_srp[i].ptl_id_mask);
+    B2N_32(pgcit->pgci_srp[i].pgc_start_byte);
+    assert(pgcit->pgci_srp[i].unknown1 == 0);
+  }
+  free(data);
+  
+  for(i = 0; i < pgcit->nr_of_pgci_srp; i++)
+    assert(pgcit->pgci_srp[i].pgc_start_byte + PGC_SIZE <= pgcit->last_byte+1);
+  
+  for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+    pgcit->pgci_srp[i].pgc = malloc(sizeof(pgc_t));
+    if(!pgcit->pgci_srp[i].pgc) {
+      int j;
+      for(j = 0; j < i; j++) {
+        ifoFree_PGC(pgcit->pgci_srp[j].pgc);
+        free(pgcit->pgci_srp[j].pgc);
+      }
+      return 0;
+    }
+    if(!ifoRead_PGC(ifofile, pgcit->pgci_srp[i].pgc, 
+                    offset + pgcit->pgci_srp[i].pgc_start_byte)) {
+      int j;
+      for(j = 0; j < i; j++) {
+        ifoFree_PGC(pgcit->pgci_srp[j].pgc);
+        free(pgcit->pgci_srp[j].pgc);
+      }
+      free(pgcit->pgci_srp);
+      return 0;
+    }
+  }
+
+  return 1;
+}
+
+static void ifoFree_PGCIT_internal(pgcit_t *pgcit) {
+  if(pgcit) {
+    int i;
+    for(i = 0; i < pgcit->nr_of_pgci_srp; i++)
+      ifoFree_PGC(pgcit->pgci_srp[i].pgc);
+    free(pgcit->pgci_srp);
+  }
+}
+
+void ifoFree_PGCIT(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  if(ifofile->vts_pgcit) {
+    ifoFree_PGCIT_internal(ifofile->vts_pgcit);
+    free(ifofile->vts_pgcit);
+    ifofile->vts_pgcit = 0;
+  }
+}
+
+
+int ifoRead_PGCI_UT(ifo_handle_t *ifofile) {
+  pgci_ut_t *pgci_ut;
+  unsigned int sector;
+  unsigned int i;  
+  int info_length;
+  uint8_t *data, *ptr;
+
+  if(!ifofile)
+    return 0;
+  
+  if(ifofile->vmgi_mat) {
+    if(ifofile->vmgi_mat->vmgm_pgci_ut == 0)
+      return 1;
+    sector = ifofile->vmgi_mat->vmgm_pgci_ut;
+  } else if(ifofile->vtsi_mat) {
+    if(ifofile->vtsi_mat->vtsm_pgci_ut == 0)
+      return 1;
+    sector = ifofile->vtsi_mat->vtsm_pgci_ut;
+  } else {
+    return 0;
+  }
+  
+  ifofile->pgci_ut = (pgci_ut_t *)malloc(sizeof(pgci_ut_t));
+  if(!ifofile->pgci_ut)
+    return 0;
+  
+  if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN)) {
+    free(ifofile->pgci_ut);
+    ifofile->pgci_ut = 0;
+    return 0;
+  }
+  
+  if(!(DVDReadBytes(ifofile->file, ifofile->pgci_ut, PGCI_UT_SIZE))) {
+    free(ifofile->pgci_ut);
+    ifofile->pgci_ut = 0;
+    return 0;
+  }
+  
+  pgci_ut = ifofile->pgci_ut;
+  
+  B2N_16(pgci_ut->nr_of_lus);
+  B2N_32(pgci_ut->last_byte);
+  
+  CHECK_ZERO(pgci_ut->zero_1);
+  assert(pgci_ut->nr_of_lus != 0);
+  assert(pgci_ut->nr_of_lus < 100); // ?? 3-4 ?
+  assert((uint32_t)pgci_ut->nr_of_lus * PGCI_LU_SIZE < pgci_ut->last_byte);
+
+  info_length = pgci_ut->nr_of_lus * PGCI_LU_SIZE;
+  data = malloc(info_length);
+  if(!data) {
+    free(pgci_ut);
+    ifofile->pgci_ut = 0;
+    return 0;
+  }
+  if(!(DVDReadBytes(ifofile->file, data, info_length))) {
+    free(data);
+    free(pgci_ut);
+    ifofile->pgci_ut = 0;
+    return 0;
+  }
+
+  pgci_ut->lu = malloc(pgci_ut->nr_of_lus * sizeof(pgci_lu_t));
+  if(!pgci_ut->lu) {
+    free(data);
+    free(pgci_ut);
+    ifofile->pgci_ut = 0;
+   return 0;
+  }
+  ptr = data;
+  for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+    memcpy(&pgci_ut->lu[i], ptr, PGCI_LU_SIZE);
+    ptr += PGCI_LU_SIZE;
+    B2N_16(pgci_ut->lu[i].lang_code); 
+    B2N_32(pgci_ut->lu[i].lang_start_byte); 
+  }
+  free(data);
+  
+  for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+    CHECK_ZERO(pgci_ut->lu[i].zero_1);
+    // Maybe this is only defined for v1.1 and later titles?
+    /* If the bits in 'lu[i].exists' are enumerated abcd efgh then:
+            VTS_x_yy.IFO        VIDEO_TS.IFO
+       a == 0x83 "Root"         0x82 "Title"
+       b == 0x84 "Subpicture"
+       c == 0x85 "Audio"
+       d == 0x86 "Angle"
+       e == 0x87 "PTT"
+    */
+    assert((pgci_ut->lu[i].exists & 0x07) == 0);
+  }
+
+  for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+    pgci_ut->lu[i].pgcit = malloc(sizeof(pgcit_t));
+    if(!pgci_ut->lu[i].pgcit) {
+      unsigned int j;
+      for(j = 0; j < i; j++) {
+        ifoFree_PGCIT_internal(pgci_ut->lu[j].pgcit);
+        free(pgci_ut->lu[j].pgcit);
+      }
+      free(pgci_ut->lu);
+      free(pgci_ut);
+      ifofile->pgci_ut = 0;
+      return 0;
+    }
+    if(!ifoRead_PGCIT_internal(ifofile, pgci_ut->lu[i].pgcit, 
+                               sector * DVD_BLOCK_LEN 
+                               + pgci_ut->lu[i].lang_start_byte)) {
+      unsigned int j;
+      for(j = 0; j < i; j++) {
+        ifoFree_PGCIT_internal(pgci_ut->lu[j].pgcit);
+        free(pgci_ut->lu[j].pgcit);
+      }
+      free(pgci_ut->lu[i].pgcit);
+      free(pgci_ut->lu);
+      free(pgci_ut);
+      ifofile->pgci_ut = 0;
+      return 0;
+    }
+    // FIXME: Iterate and verify that all menus that should exists accordingly
+    //        to pgci_ut->lu[i].exists really do?
+  }
+
+  return 1;
+}
+
+
+void ifoFree_PGCI_UT(ifo_handle_t *ifofile) {
+  unsigned int i;
+
+  if(!ifofile)
+    return;
+  
+  if(ifofile->pgci_ut) {
+    for(i = 0; i < ifofile->pgci_ut->nr_of_lus; i++) {
+      ifoFree_PGCIT_internal(ifofile->pgci_ut->lu[i].pgcit);
+      free(ifofile->pgci_ut->lu[i].pgcit);
+    }
+    free(ifofile->pgci_ut->lu);
+    free(ifofile->pgci_ut);
+    ifofile->pgci_ut = 0;
+  }
+}
+
+static int ifoRead_VTS_ATTRIBUTES(ifo_handle_t *ifofile, 
+                                  vts_attributes_t *vts_attributes, 
+                                  unsigned int offset) {
+  unsigned int i;
+
+  if(!DVDFileSeek_(ifofile->file, offset))
+    return 0;
+
+  if(!(DVDReadBytes(ifofile->file, vts_attributes, sizeof(vts_attributes_t))))
+    return 0;
+
+  B2N_32(vts_attributes->last_byte);
+  B2N_32(vts_attributes->vts_cat);
+  B2N_16(vts_attributes->vtsm_audio_attr.lang_code);
+  B2N_16(vts_attributes->vtsm_subp_attr.lang_code);
+  for(i = 0; i < 8; i++)
+    B2N_16(vts_attributes->vtstt_audio_attr[i].lang_code);
+  for(i = 0; i < 32; i++)
+    B2N_16(vts_attributes->vtstt_subp_attr[i].lang_code);
+  
+  CHECK_ZERO(vts_attributes->zero_1);
+  CHECK_ZERO(vts_attributes->zero_2);
+  CHECK_ZERO(vts_attributes->zero_3);
+  CHECK_ZERO(vts_attributes->zero_4);
+  CHECK_ZERO(vts_attributes->zero_5);
+  CHECK_ZERO(vts_attributes->zero_6);
+  CHECK_ZERO(vts_attributes->zero_7);
+  assert(vts_attributes->nr_of_vtsm_audio_streams <= 1);
+  assert(vts_attributes->nr_of_vtsm_subp_streams <= 1);
+  assert(vts_attributes->nr_of_vtstt_audio_streams <= 8);
+  for(i = vts_attributes->nr_of_vtstt_audio_streams; i < 8; i++)
+    CHECK_ZERO(vts_attributes->vtstt_audio_attr[i]);
+  assert(vts_attributes->nr_of_vtstt_subp_streams <= 32);
+  {
+    unsigned int nr_coded;
+    assert(vts_attributes->last_byte + 1 >= VTS_ATTRIBUTES_MIN_SIZE);  
+    nr_coded = (vts_attributes->last_byte + 1 - VTS_ATTRIBUTES_MIN_SIZE)/6;
+    // This is often nr_coded = 70, how do you know how many there really are?
+    if(nr_coded > 32) { // We haven't read more from disk/file anyway
+      nr_coded = 32;
+    }
+    assert(vts_attributes->nr_of_vtstt_subp_streams <= nr_coded);
+    for(i = vts_attributes->nr_of_vtstt_subp_streams; i < nr_coded; i++)
+      CHECK_ZERO(vts_attributes->vtstt_subp_attr[i]);
+  }
+
+  return 1;
+}
+
+
+
+int ifoRead_VTS_ATRT(ifo_handle_t *ifofile) {
+  vts_atrt_t *vts_atrt;
+  unsigned int i, info_length, sector;
+  uint32_t *data;
+
+  if(!ifofile)
+    return 0;
+  
+  if(!ifofile->vmgi_mat)
+    return 0;
+  
+  if(ifofile->vmgi_mat->vts_atrt == 0) /* mandatory */
+    return 0;
+  
+  sector = ifofile->vmgi_mat->vts_atrt;
+  if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN))
+    return 0;
+
+  vts_atrt = (vts_atrt_t *)malloc(sizeof(vts_atrt_t));
+  if(!vts_atrt)
+    return 0;
+
+  ifofile->vts_atrt = vts_atrt;
+  
+  if(!(DVDReadBytes(ifofile->file, vts_atrt, VTS_ATRT_SIZE))) {
+    free(vts_atrt);
+    ifofile->vts_atrt = 0;
+    return 0;
+  }
+
+  B2N_16(vts_atrt->nr_of_vtss);
+  B2N_32(vts_atrt->last_byte);
+
+  CHECK_ZERO(vts_atrt->zero_1);
+  assert(vts_atrt->nr_of_vtss != 0);
+  assert(vts_atrt->nr_of_vtss < 100); //??
+  assert((uint32_t)vts_atrt->nr_of_vtss * (4 + VTS_ATTRIBUTES_MIN_SIZE) + 
+         VTS_ATRT_SIZE < vts_atrt->last_byte + 1);
+
+  info_length = vts_atrt->nr_of_vtss * sizeof(uint32_t);
+  data = (uint32_t *)malloc(info_length);
+  if(!data) {
+    free(vts_atrt);
+    ifofile->vts_atrt = 0;
+    return 0;
+  }
+  if(!(DVDReadBytes(ifofile->file, data, info_length))) {
+    free(data);
+    free(vts_atrt);
+    ifofile->vts_atrt = 0;
+    return 0;
+  }
+  
+  for(i = 0; i < vts_atrt->nr_of_vtss; i++) {
+    B2N_32(data[i]);
+    assert(data[i] + VTS_ATTRIBUTES_MIN_SIZE < vts_atrt->last_byte + 1);
+  }
+  
+  info_length = vts_atrt->nr_of_vtss * sizeof(vts_attributes_t);
+  vts_atrt->vts = (vts_attributes_t *)malloc(info_length);
+  if(!vts_atrt->vts) {
+    free(data);
+    free(vts_atrt);
+    ifofile->vts_atrt = 0;
+    return 0;
+  }
+  for(i = 0; i < vts_atrt->nr_of_vtss; i++) {
+    unsigned int offset = data[i];
+    if(!ifoRead_VTS_ATTRIBUTES(ifofile, &(vts_atrt->vts[i]),
+                               (sector * DVD_BLOCK_LEN) + offset)) {
+      free(data);
+      free(vts_atrt);
+      ifofile->vts_atrt = 0;
+      return 0;
+    }
+
+    // This assert cant be in ifoRead_VTS_ATTRIBUTES
+    assert(offset + vts_atrt->vts[i].last_byte <= vts_atrt->last_byte + 1);
+    // Is this check correct?
+  }
+  free(data);
+
+  return 1;
+}
+
+
+void ifoFree_VTS_ATRT(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  if(ifofile->vts_atrt) {
+    free(ifofile->vts_atrt->vts);
+    free(ifofile->vts_atrt);
+    ifofile->vts_atrt = 0;
+  }
+}
+
+
+int ifoRead_TXTDT_MGI(ifo_handle_t *ifofile) {
+  txtdt_mgi_t *txtdt_mgi;
+
+  if(!ifofile)
+    return 0;
+  
+  if(!ifofile->vmgi_mat)
+    return 0;
+ 
+  /* Return successfully if there is nothing to read. */ 
+  if(ifofile->vmgi_mat->txtdt_mgi == 0)
+    return 1;
+
+  if(!DVDFileSeek_(ifofile->file, 
+		   ifofile->vmgi_mat->txtdt_mgi * DVD_BLOCK_LEN))
+    return 0;
+  
+  txtdt_mgi = (txtdt_mgi_t *)malloc(sizeof(txtdt_mgi_t));
+  if(!txtdt_mgi) {
+    return 0;
+  }
+  ifofile->txtdt_mgi = txtdt_mgi;
+
+  if(!(DVDReadBytes(ifofile->file, txtdt_mgi, TXTDT_MGI_SIZE))) {
+    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;
+  }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdvdkit2/ifo_read.h	Fri Aug 16 22:37:48 2002 +0000
@@ -0,0 +1,217 @@
+#ifndef IFO_READ_H_INCLUDED
+#define IFO_READ_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001, 2002 Björn Englund <d4bjorn@dtek.chalmers.se>,
+ *                                Håkan Hjort <d95hjort@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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdvdkit2/ifo_types.h	Fri Aug 16 22:37:48 2002 +0000
@@ -0,0 +1,747 @@
+#ifndef IFO_TYPES_H_INCLUDED
+#define IFO_TYPES_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001 Björn Englund <d4bjorn@dtek.chalmers.se>,
+ *                          Håkan Hjort <d95hjort@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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdvdkit2/nav_print.c	Fri Aug 16 22:37:48 2002 +0000
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2000, 2001, 2002 Håkan Hjort <d95hjort@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@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);
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdvdkit2/nav_print.h	Fri Aug 16 22:37:48 2002 +0000
@@ -0,0 +1,42 @@
+#ifndef NAV_PRINT_H_INCLUDED
+#define NAV_PRINT_H_INCLUDED
+
+/*
+ * Copyright (C) 2001 Billy Biggs <vektor@dumbterm.net>,
+ *                    Håkan Hjort <d95hjort@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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdvdkit2/nav_read.c	Fri Aug 16 22:37:48 2002 +0000
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2000, 2001, 2002 Håkan Hjort <d95hjort@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);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdvdkit2/nav_read.h	Fri Aug 16 22:37:48 2002 +0000
@@ -0,0 +1,41 @@
+#ifndef NAV_READ_H_INCLUDED
+#define NAV_READ_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001 Håkan Hjort <d95hjort@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 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmpdvdkit2/nav_types.h	Fri Aug 16 22:37:48 2002 +0000
@@ -0,0 +1,288 @@
+#ifndef NAV_TYPES_H_INCLUDED
+#define NAV_TYPES_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001 Håkan Hjort <d95hjort@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@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 */