changeset 15874:483e955893b8

update libdvdread to v0.9.4
author aurel
date Thu, 30 Jun 2005 22:48:26 +0000
parents 276da0bb6207
children 50960b929dd0
files libmpdvdkit2/Makefile libmpdvdkit2/README libmpdvdkit2/bswap.h libmpdvdkit2/config.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 20 files changed, 1994 insertions(+), 617 deletions(-) [+]
line wrap: on
line diff
--- a/libmpdvdkit2/Makefile	Thu Jun 30 22:41:40 2005 +0000
+++ b/libmpdvdkit2/Makefile	Thu Jun 30 22:48:26 2005 +0000
@@ -15,6 +15,7 @@
        libdvdcss.c \
        nav_print.c \
        nav_read.c \
+       md5.c \
 
 #bsdi_ioctl.c 
 
@@ -34,7 +35,8 @@
 
 # -funroll-loops  removed, triggered gcc 3.0.4 (3.x?) bug
 CFLAGS= -I. $(OPTFLAGS) $(EXTRA_INC)\
-	-DSYS_LINUX -D__USE_UNIX98 -D_REENTRANT -D_GNU_SOURCE
+	-DSYS_LINUX -D__USE_UNIX98 -D_REENTRANT -D_GNU_SOURCE \
+	-DHAVE_DVDCSS_DVDCSS_H -DSTDC_HEADERS
 
 .c.o:
 	$(CC) $(CFLAGS) -c -o $@ $<
--- a/libmpdvdkit2/README	Thu Jun 30 22:41:40 2005 +0000
+++ b/libmpdvdkit2/README	Thu Jun 30 22:48:26 2005 +0000
@@ -1,12 +1,12 @@
-MPlayer DVD-kit v2.2 :)
+MPlayer DVD-kit v2.3 :)
 made by Pontscho & A'rpi for the MPlayer project
 
 What the hell is this?
 ======================
 Nothing special, just a collection of sources and patches and fixes:
 
-- dvdread 0.9.3 + static libdvdcss (removed dlopen code)
-- libdvdcss 1.2.5
+- dvdread 0.9.4 + static libdvdcss
+- libdvdcss 1.2.8
 - optimizations enabled, asserts disabled
 everything packed together with _static_ linking to maximize performance.
 
--- a/libmpdvdkit2/bswap.h	Thu Jun 30 22:41:40 2005 +0000
+++ b/libmpdvdkit2/bswap.h	Thu Jun 30 22:48:26 2005 +0000
@@ -32,6 +32,11 @@
 
 #else 
 
+/* For __FreeBSD_version */
+#if defined(HAVE_SYS_PARAM_H)
+#include <sys/param.h>
+#endif
+
 #if defined(__linux__)
 #include <byteswap.h>
 #define B2N_16(x) x = bswap_16(x)
@@ -50,6 +55,18 @@
 #define B2N_32(x) x = swap32(x)
 #define B2N_64(x) x = swap64(x)
 
+#elif defined(__FreeBSD__) && __FreeBSD_version >= 470000
+#include <sys/endian.h>
+#define B2N_16(x) x = be16toh(x)
+#define B2N_32(x) x = be32toh(x)
+#define B2N_64(x) x = be64toh(x)
+
+#elif defined(__DragonFly__)
+#include <sys/endian.h>
+#define B2N_16(x) x = be16toh(x)
+#define B2N_32(x) x = be32toh(x)
+#define B2N_64(x) x = be64toh(x)
+
 #elif defined(ARCH_X86)
 inline static unsigned short bswap_16(unsigned short x)
 {
@@ -88,17 +105,12 @@
 }
 #define B2N_64(x) x = bswap_64(x)
 
-#elif defined(__DragonFly__)
-#include <sys/endian.h>
-#define B2N_16(x) x = be16toh(x)
-#define B2N_32(x) x = be32toh(x)
-#define B2N_64(x) x = be64toh(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 
+ * Old FreeBSD's and Solaris don't have <byteswap.h> or any other such 
  * functionality! 
  */
+
 #elif defined(__FreeBSD__) || defined(__sun) || defined(__bsdi__) || defined(__CYGWIN__)
 #define B2N_16(x) \
  x = ((((x) & 0xff00) >> 8) | \
--- a/libmpdvdkit2/config.h	Thu Jun 30 22:41:40 2005 +0000
+++ b/libmpdvdkit2/config.h	Thu Jun 30 22:48:26 2005 +0000
@@ -1,6 +1,6 @@
 
 /* Version number of package */
-#define VERSION "1.2.2"
+#define VERSION "1.2.3"
 #define HAVE_UNISTD_H 1
 
 /* Define if your processor stores words with the most significant
--- a/libmpdvdkit2/dvd_input.c	Thu Jun 30 22:41:40 2005 +0000
+++ b/libmpdvdkit2/dvd_input.c	Thu Jun 30 22:48:26 2005 +0000
@@ -21,6 +21,8 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
  */
 
+#include "config.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <fcntl.h>
@@ -29,21 +31,34 @@
 #include "dvd_reader.h"
 #include "dvd_input.h"
 
-#include "dvdcss.h"
+/* The function pointers that is the exported interface of this file. */
+dvd_input_t (*dvdinput_open)  (const char *);
+int         (*dvdinput_close) (dvd_input_t);
+int         (*dvdinput_seek)  (dvd_input_t, int);
+int         (*dvdinput_title) (dvd_input_t, int); 
+int         (*dvdinput_read)  (dvd_input_t, void *, int, int);
+char *      (*dvdinput_error) (dvd_input_t);
 
-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);
-
-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);
+#ifdef HAVE_DVDCSS_DVDCSS_H
+/* linking to libdvdcss */
+#include "dvdcss.h"
+#define DVDcss_open(a) dvdcss_open((char*)(a))
+#define DVDcss_close   dvdcss_close
+#define DVDcss_seek    dvdcss_seek
+#define DVDcss_title   dvdcss_title
+#define DVDcss_read    dvdcss_read
+#define DVDcss_error   dvdcss_error
+#else
+/* dlopening libdvdcss */
+#include <dlfcn.h>
+typedef struct dvdcss_s *dvdcss_handle;
+static dvdcss_handle (*DVDcss_open)  (const char *);
+static int           (*DVDcss_close) (dvdcss_handle);
+static int           (*DVDcss_seek)  (dvdcss_handle, int, int);
+static int           (*DVDcss_title) (dvdcss_handle, int); 
+static int           (*DVDcss_read)  (dvdcss_handle, void *, int, int);
+static char *        (*DVDcss_error) (dvdcss_handle);
+#endif
 
 /* The DVDinput handle, add stuff here for new input methods. */
 struct dvd_input_s {
@@ -61,7 +76,7 @@
 static dvd_input_t css_open(const char *target)
 {
   dvd_input_t dev;
-  
+    
   /* Allocate the handle structure */
   dev = (dvd_input_t) malloc(sizeof(struct dvd_input_s));
   if(dev == NULL) {
@@ -72,7 +87,7 @@
   /* Really open it with libdvdcss */
   dev->dvdcss = DVDcss_open(target);
   if(dev->dvdcss == 0) {
-    fprintf(stderr, "libdvdread: Could not open device with libdvdcss.\n");
+    fprintf(stderr, "libdvdread: Could not open %s with libdvdcss.\n", target);
     free(dev);
     return NULL;
   }
@@ -91,9 +106,10 @@
 /**
  * seek into the device.
  */
-static int css_seek(dvd_input_t dev, int blocks, int flags)
+static int css_seek(dvd_input_t dev, int blocks)
 {
-  return DVDcss_seek(dev->dvdcss, blocks, flags);
+  /* DVDINPUT_NOFLAGS should match the DVDCSS_NOFLAGS value. */
+  return DVDcss_seek(dev->dvdcss, blocks, DVDINPUT_NOFLAGS);
 }
 
 /**
@@ -131,34 +147,207 @@
 
 
 
+
+
+
+/**
+ * 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 (char *)"unknown error";
+}
+
+/**
+ * seek into the device.
+ */
+static int file_seek(dvd_input_t dev, int blocks)
+{
+  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)
+int dvdinput_setup(void)
 {
-    DVDcss_open = dvdcss_open;
-    DVDcss_close = dvdcss_close;
-    DVDcss_title = dvdcss_title;
-    DVDcss_seek = dvdcss_seek;
-    DVDcss_read = dvdcss_read;
-    DVDcss_error = dvdcss_error;
+  void *dvdcss_library = NULL;
+  char **dvdcss_version = NULL;
+
+#ifdef HAVE_DVDCSS_DVDCSS_H
+  /* linking to libdvdcss */
+  dvdcss_library = &dvdcss_library;  /* Give it some value != NULL */
+  /* the DVDcss_* functions have been #defined at the top */
+  dvdcss_version = &dvdcss_interface_2;
+
+#else
+  /* dlopening libdvdcss */
+  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);
+    }
+  }
+#endif /* HAVE_DVDCSS_DVDCSS_H */
+  
+  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);
+    /*
+    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;
+    /* libdvdcss wrapper 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;
+  }
 }
--- a/libmpdvdkit2/dvd_input.h	Thu Jun 30 22:41:40 2005 +0000
+++ b/libmpdvdkit2/dvd_input.h	Thu Jun 30 22:48:26 2005 +0000
@@ -5,10 +5,6 @@
  * Copyright (C) 2001, 2002 Samuel Hocevar <sam@zoy.org>,
  *                          Håkan Hjort <d95hjort@dtek.chalmers.se>
  *
- * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
- * detailed CVS changelog at http://www.mplayerhq.hu/cgi-bin/cvsweb.cgi/main/
- * $Id$
- *
  * 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
@@ -31,25 +27,21 @@
 
 #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.
+ * Pointers which will be filled either the input methods functions.
  */
-extern dvd_input_t (*DVDinput_open)  (const char *);
-extern int         (*DVDinput_close) (dvd_input_t);
-extern int         (*DVDinput_seek)  (dvd_input_t, int, int);
-extern int         (*DVDinput_title) (dvd_input_t, int); 
-extern int         (*DVDinput_read)  (dvd_input_t, void *, int, int);
-extern char *      (*DVDinput_error) (dvd_input_t);
+extern dvd_input_t (*dvdinput_open)  (const char *);
+extern int         (*dvdinput_close) (dvd_input_t);
+extern int         (*dvdinput_seek)  (dvd_input_t, int);
+extern int         (*dvdinput_title) (dvd_input_t, int); 
+extern int         (*dvdinput_read)  (dvd_input_t, void *, int, int);
+extern char *      (*dvdinput_error) (dvd_input_t);
 
 /**
  * Setup function accessed by dvd_reader.c.  Returns 1 if there is CSS support.
  */
-int DVDInputSetup(void);
+int dvdinput_setup(void);
 
 #endif /* DVD_INPUT_H_INCLUDED */
--- a/libmpdvdkit2/dvd_reader.c	Thu Jun 30 22:41:40 2005 +0000
+++ b/libmpdvdkit2/dvd_reader.c	Thu Jun 30 22:48:26 2005 +0000
@@ -1,6 +1,7 @@
 /*
- * Copyright (C) 2001, 2002 Billy Biggs <vektor@dumbterm.net>,
- *                          Håkan Hjort <d95hjort@dtek.chalmers.se>
+ * Copyright (C) 2001, 2002, 2003 Billy Biggs <vektor@dumbterm.net>,
+ *                                Håkan Hjort <d95hjort@dtek.chalmers.se>,
+ *                                Björn Englund <d4bjorn@dtek.chalmers.se>
  *
  * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
  * detailed CVS changelog at http://www.mplayerhq.hu/cgi-bin/cvsweb.cgi/main/
@@ -62,6 +63,9 @@
 #include "dvd_udf.h"
 #include "dvd_input.h"
 #include "dvd_reader.h"
+#include "md5.h"
+
+#define DEFAULT_UDF_CACHE_LEVEL 0
 
 struct dvd_reader_s {
     /* Basic information. */
@@ -70,13 +74,17 @@
     /* 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. */
+    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;
+  
+    /* Filesystem cache */
+    int udfcache_level; /* 0 - turned off, 1 - on */
+    void *udfcache;
 };
 
 struct dvd_file_s {
@@ -98,6 +106,42 @@
     ssize_t filesize;
 };
 
+/**
+ * Set the level of caching on udf
+ * level = 0 (no caching)
+ * level = 1 (caching filesystem info)
+ */
+int DVDUDFCacheLevel(dvd_reader_t *device, int level)
+{
+  struct dvd_reader_s *dev = (struct dvd_reader_s *)device;
+  
+  if(level > 0) {
+    level = 1;
+  } else if(level < 0) {
+    return dev->udfcache_level;
+  }
+
+  dev->udfcache_level = level;
+  
+  return level;
+}
+
+void *GetUDFCacheHandle(dvd_reader_t *device)
+{
+  struct dvd_reader_s *dev = (struct dvd_reader_s *)device;
+  
+  return dev->udfcache;
+}
+
+void SetUDFCacheHandle(dvd_reader_t *device, void *cache)
+{
+  struct dvd_reader_s *dev = (struct dvd_reader_s *)device;
+
+  dev->udfcache = cache;
+}
+
+
+
 /* Loop over all titles and call dvdcss_title to crack the keys. */
 static int initAllCSSKeys( dvd_reader_t *dvd )
 {
@@ -107,6 +151,10 @@
     uint32_t start, len;
     int title;
 	
+    char *nokeys_str = getenv("DVDREAD_NOKEYS");
+    if(nokeys_str != NULL)
+      return 0;
+    
     fprintf( stderr, "\n" );
     fprintf( stderr, "libdvdread: Attempting to retrieve all CSS keys\n" );
     fprintf( stderr, "libdvdread: This can take a _long_ time, "
@@ -126,7 +174,7 @@
 	    /* 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 ) {
+	    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 );
@@ -144,7 +192,7 @@
 	/* 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 ) {
+	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 );
@@ -177,18 +225,8 @@
 {
     dvd_reader_t *dvd;
     dvd_input_t dev;
-
-    /* setup cache dir is no longer needed, it's now implemented in libdvdcss.c
-    if(!dvdcss_cache_dir){
-	dvdcss_cache_dir=get_path( "" );
-	if ( dvdcss_cache_dir ) { mkdir( dvdcss_cache_dir,493 ); free( dvdcss_cache_dir ); }
-	dvdcss_cache_dir=get_path( "DVDKeys" );
-	if(dvdcss_cache_dir) mkdir( dvdcss_cache_dir,493 );
-    }
-    */
     
-    /* open it */
-    dev = DVDinput_open( location );
+    dev = dvdinput_open( location );
     if( !dev ) {
 	fprintf( stderr, "libdvdread: Can't open %s for reading\n", location );
 	return 0;
@@ -200,6 +238,9 @@
     dvd->dev = dev;
     dvd->path_root = 0;
     
+    dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
+    dvd->udfcache = NULL;
+
     if( have_css ) {
       /* Only if DVDCSS_METHOD = title, a bit if it's disc or if
        * DVDCSS_METHOD = key but region missmatch. Unfortunaly we
@@ -207,6 +248,7 @@
     
       dvd->css_state = 1; /* Need key init. */
     }
+    dvd->css_title = 0;
     
     return dvd;
 }
@@ -221,6 +263,12 @@
     dvd->dev = 0;
     dvd->path_root = strdup( path_root );
 
+    dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
+    dvd->udfcache = NULL;
+    
+    dvd->css_state = 0; /* Only used in the UDF path */
+    dvd->css_title = 0; /* Only matters in the UDF path */
+
     return dvd;
 }
 
@@ -275,7 +323,8 @@
     int ret, have_css;
     char *dev_name = 0;
 
-    if( !path ) return 0;
+    if( path == NULL )
+      return 0;
 
 #ifdef WIN32
     /* Stat doesn't work on devices under mingwin/cygwin. */
@@ -297,7 +346,7 @@
     }
 
     /* Try to open libdvdcss or fall back to standard functions */
-    have_css = DVDInputSetup();
+    have_css = dvdinput_setup();
 
     /* First check if this is a block/char device or a file*/
     if( S_ISBLK( fileinfo.st_mode ) || 
@@ -445,10 +494,10 @@
 void DVDClose( dvd_reader_t *dvd )
 {
     if( dvd ) {
-        if( dvd->dev ) DVDinput_close( dvd->dev );
+        if( dvd->dev ) dvdinput_close( dvd->dev );
         if( dvd->path_root ) free( dvd->path_root );
+	if( dvd->udfcache ) FreeUDFCache( dvd->udfcache );
         free( dvd );
-        dvd = 0;
     }
 }
 
@@ -545,7 +594,7 @@
     /* Get the full path of the file. */
     if( !findDVDFile( dvd, filename, full_path ) ) return 0;
 
-    dev = DVDinput_open( full_path );
+    dev = dvdinput_open( full_path );
     if( !dev ) return 0;
 
     dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
@@ -609,8 +658,7 @@
 //	dvd->css_state = 2;
     }
     /*    
-    if( DVDinput_seek( dvd_file->dvd->dev, 
-		       (int)start, DVDINPUT_SEEK_KEY ) < 0 ) {
+    if( dvdinput_title( dvd_file->dvd->dev, (int)start ) < 0 ) {
         fprintf( stderr, "libdvdread: Error cracking CSS key for %s\n",
 		 filename );
     }
@@ -636,7 +684,7 @@
     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;
 
@@ -650,7 +698,7 @@
             return 0;
         }
 
-        dev = DVDinput_open( full_path );
+        dev = dvdinput_open( full_path );
         if( dev == NULL ) {
             free( dvd_file );
             return 0;
@@ -663,7 +711,7 @@
         }
         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 );
+	dvdinput_title( dvd_file->title_devs[0], 0);
         dvd_file->filesize = dvd_file->title_sizes[ 0 ];
 
     } else {
@@ -680,9 +728,9 @@
             }
 
             dvd_file->title_sizes[ i ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
-            dvd_file->title_devs[ i ] = DVDinput_open( full_path );
+            dvd_file->title_devs[ i ] = dvdinput_open( full_path );
+	    dvdinput_title( dvd_file->title_devs[ i ], 0 );
             dvd_file->filesize += dvd_file->title_sizes[ i ];
-            DVDinput_seek( dvd_file->title_devs[ i ], 0, DVDINPUT_SEEK_KEY );
         }
         if( !dvd_file->title_devs[ 0 ] ) {
             free( dvd_file );
@@ -697,6 +745,10 @@
 			 dvd_read_domain_t domain )
 {
     char filename[ MAX_UDF_FILE_NAME_LEN ];
+    
+    /* Check arguments. */
+    if( dvd == NULL || titlenum < 0 )
+      return NULL;
 
     switch( domain ) {
     case DVD_READ_INFO_FILE:
@@ -730,7 +782,7 @@
         break;
     default:
         fprintf( stderr, "libdvdread: Invalid domain for file open.\n" );
-        return 0;
+        return NULL;
     }
     
     if( dvd->isImageFile ) {
@@ -750,7 +802,7 @@
 	} else {
             for( i = 0; i < 9; ++i ) {
                 if( dvd_file->title_devs[ i ] ) {
-                    DVDinput_close( dvd_file->title_devs[i] );
+                    dvdinput_close( dvd_file->title_devs[i] );
                 }
             }
         }
@@ -761,7 +813,7 @@
 }
 
 /* Internal, but used from dvd_udf.c */
-int DVDReadBlocksUDFRaw( dvd_reader_t *device, uint32_t lb_number,
+int UDFReadBlocksRaw( dvd_reader_t *device, uint32_t lb_number,
 			 size_t block_count, unsigned char *data, 
 			 int encrypted )
 {
@@ -772,13 +824,13 @@
 	return 0;
    }
 
-   ret = DVDinput_seek( device->dev, (int) lb_number, DVDINPUT_NOFLAGS );
+   ret = dvdinput_seek( device->dev, (int) lb_number );
    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, 
+   return dvdinput_read( device->dev, (char *) data, 
 			 (int) block_count, encrypted );
 }
 
@@ -792,8 +844,8 @@
 			     size_t block_count, unsigned char *data,
 			     int encrypted )
 {
-    return DVDReadBlocksUDFRaw( dvd_file->dvd, dvd_file->lb_start + offset,
-				block_count, data, encrypted );
+    return UDFReadBlocksRaw( 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'.
@@ -816,14 +868,13 @@
 
         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 );
+		off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );
                 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,
+                ret = dvdinput_read( dvd_file->title_devs[ i ], data,
 				     (int)block_count, encrypted );
                 break;
             } else {
@@ -832,31 +883,29 @@
                  * (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 );
+                off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );
                 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,
+                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. */
-
-                /* Does the next part exist? If not then return now. */
-                if( !dvd_file->title_devs[ i + 1 ] ) return ret;
+		
+		/* Does the next part exist? If not then return now. */
+		if( !dvd_file->title_devs[ i + 1 ] ) return ret;
 
                 /* Read part 2 */
-                off = DVDinput_seek( dvd_file->title_devs[ i + 1 ], 
-				     0, DVDINPUT_NOFLAGS );
+                off = dvdinput_seek( dvd_file->title_devs[ i + 1 ], 0 );
                 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 ], 
+                ret2 = dvdinput_read( dvd_file->title_devs[ i + 1 ], 
 				      data + ( part1_size
 					       * (int64_t)DVD_VIDEO_LB_LEN ),
 				      (int)(block_count - part1_size),
@@ -878,14 +927,20 @@
 {
     int ret;
     
+    /* Check arguments. */
+    if( dvd_file == NULL || offset < 0 || data == NULL )
+      return -1;
+    
     /* 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 );
-      }
+	dvdinput_title( dvd_file->dvd->dev, (int)dvd_file->lb_start );
+      } 
+      /* Here each vobu has it's own dvdcss handle, so no need to update 
+      else {
+	dvdinput_title( dvd_file->title_devs[ 0 ], (int)dvd_file->lb_start );
+      }*/
     }
     
     if( dvd_file->dvd->isImageFile ) {
@@ -901,11 +956,15 @@
 
 int DVDFileSeek( dvd_file_t *dvd_file, int offset )
 {
-   if( offset > dvd_file->filesize * DVD_VIDEO_LB_LEN ) {
+    /* Check arguments. */
+    if( dvd_file == NULL || offset < 0 )
        return -1;
-   }
-   dvd_file->seek_pos = (uint32_t) offset;
-   return 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 )
@@ -914,10 +973,16 @@
     unsigned int numsec, seek_sector, seek_byte;
     int ret;
     
+    /* Check arguments. */
+    if( dvd_file == NULL || data == NULL )
+      return -1;
+
     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;
+    numsec = ( ( seek_byte + byte_size ) / DVD_VIDEO_LB_LEN ) +
+      ( ( ( seek_byte + byte_size ) % DVD_VIDEO_LB_LEN ) ? 1 : 0 );
+    
     secbuf = (unsigned char *) malloc( numsec * DVD_VIDEO_LB_LEN );
     if( !secbuf ) {
 	fprintf( stderr, "libdvdread: Can't allocate memory " 
@@ -947,5 +1012,139 @@
 
 ssize_t DVDFileSize( dvd_file_t *dvd_file )
 {
+    /* Check arguments. */
+    if( dvd_file == NULL )
+      return -1;
+    
     return dvd_file->filesize;
 }
+
+int DVDDiscID( dvd_reader_t *dvd, unsigned char *discid )
+{
+    struct md5_ctx ctx;
+    int title;
+
+    /* Check arguments. */
+    if( dvd == NULL || discid == NULL )
+      return 0;
+    
+    /* Go through the first 10 IFO:s, in order, 
+     * and md5sum them, i.e  VIDEO_TS.IFO and VTS_0?_0.IFO */
+    md5_init_ctx( &ctx );
+    for( title = 0; title < 10; title++ ) {
+	dvd_file_t *dvd_file = DVDOpenFile( dvd, title, DVD_READ_INFO_FILE );
+	if( dvd_file != NULL ) {
+	    ssize_t bytes_read;
+	    size_t file_size = dvd_file->filesize * DVD_VIDEO_LB_LEN;
+	    char *buffer = malloc( file_size );
+	    
+	    if( buffer == NULL ) {
+		fprintf( stderr, "libdvdread: DVDDiscId, failed to "
+			 "allocate memory for file read!\n" );
+		return -1;
+	    }
+	    bytes_read = DVDReadBytes( dvd_file, buffer, file_size );
+	    if( bytes_read != file_size ) {
+		fprintf( stderr, "libdvdread: DVDDiscId read returned %d bytes"
+			 ", wanted %d\n", bytes_read, file_size );
+		DVDCloseFile( dvd_file );
+		return -1;
+	    }
+	    
+	    md5_process_bytes( buffer, file_size,  &ctx );
+	    
+	    DVDCloseFile( dvd_file );
+	    free( buffer );
+	}
+    }
+    md5_finish_ctx( &ctx, discid );
+    
+    return 0;
+}
+
+
+int DVDISOVolumeInfo( dvd_reader_t *dvd,
+		      char *volid, unsigned int volid_size,
+		      unsigned char *volsetid, unsigned int volsetid_size )
+{
+  unsigned char *buffer;
+  int ret;
+
+  /* Check arguments. */
+  if( dvd == NULL )
+    return 0;
+  
+  if( dvd->dev == NULL ) {
+    /* No block access, so no ISO... */
+    return -1;
+  }
+  
+  buffer = malloc( DVD_VIDEO_LB_LEN );
+  if( buffer == NULL ) {
+    fprintf( stderr, "libdvdread: DVDISOVolumeInfo, failed to "
+	     "allocate memory for file read!\n" );
+    return -1;
+  }
+
+  ret = UDFReadBlocksRaw( dvd, 16, 1, buffer, 0 );
+  if( ret != 1 ) {
+    fprintf( stderr, "libdvdread: DVDISOVolumeInfo, failed to "
+	     "read ISO9660 Primary Volume Descriptor!\n" );
+    return -1;
+  }
+  
+  if( (volid != NULL) && (volid_size > 0) ) {
+    unsigned int n;
+    for(n = 0; n < 32; n++) {
+      if(buffer[40+n] == 0x20) {
+	break;
+      }
+    }
+    
+    if(volid_size > n+1) {
+      volid_size = n+1;
+    }
+
+    memcpy(volid, &buffer[40], volid_size-1);
+    volid[volid_size-1] = '\0';
+  }
+  
+  if( (volsetid != NULL) && (volsetid_size > 0) ) {
+    if(volsetid_size > 128) {
+      volsetid_size = 128;
+    }
+    memcpy(volsetid, &buffer[190], volsetid_size);
+  }
+  return 0;
+}
+
+
+int DVDUDFVolumeInfo( dvd_reader_t *dvd,
+		      char *volid, unsigned int volid_size,
+		      unsigned char *volsetid, unsigned int volsetid_size )
+{
+  int ret;
+  /* Check arguments. */
+  if( dvd == NULL )
+    return -1;
+  
+  if( dvd->dev == NULL ) {
+    /* No block access, so no UDF VolumeSet Identifier */
+    return -1;
+  }
+  
+  if( (volid != NULL) && (volid_size > 0) ) {
+    ret = UDFGetVolumeIdentifier(dvd, volid, volid_size);
+    if(!ret) {
+      return -1;
+    }
+  }
+  if( (volsetid != NULL) && (volsetid_size > 0) ) {
+    ret =  UDFGetVolumeSetIdentifier(dvd, volsetid, volsetid_size);
+    if(!ret) {
+      return -1;
+    }
+  }
+    
+  return 0;  
+}
--- a/libmpdvdkit2/dvd_reader.h	Thu Jun 30 22:41:40 2005 +0000
+++ b/libmpdvdkit2/dvd_reader.h	Thu Jun 30 22:48:26 2005 +0000
@@ -3,7 +3,8 @@
 
 /*
  * Copyright (C) 2001, 2002 Billy Biggs <vektor@dumbterm.net>,
- *                          Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *                          Håkan Hjort <d95hjort@dtek.chalmers.se>,
+ *                          Björn Englund <d4bjorn@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
@@ -23,128 +24,241 @@
 #include <sys/types.h>
 
 /**
- * The length of one Logical Block of a DVD Video.
+ * The DVD access interface.
+ *
+ * This file contains the functions that form the interface to to
+ * reading files located on a DVD.
+ */
+
+/**
+ * The current version.
+ */
+#define DVDREAD_VERSION 904
+
+/**
+ * The length of one Logical Block of a DVD.
  */
 #define DVD_VIDEO_LB_LEN 2048
 
 /**
- * Maximum length of filenames for UDF.
+ * Maximum length of filenames allowed in UDF.
  */
 #define MAX_UDF_FILE_NAME_LEN 2048
 
 #ifdef __cplusplus
 extern "C" {
 #endif
-
+  
+/**
+ * Opaque type that is used as a handle for one instance of an opened DVD.
+ */
 typedef struct dvd_reader_s dvd_reader_t;
+  
+/**
+ * Opaque type for a file read handle, much like a normal fd or FILE *.
+ */
 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.
+ * name for a mounted DVD or HD copy of a DVD.
  *
  * 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:
+ * 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
+ *
+ * @param path Specifies the the device, file or directory to be used. 
+ * @return If successful a a read handle is returned. Otherwise 0 is returned.
+ *
+ * dvd = DVDOpen(path);
  */
 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.
  *
- * Closes and cleans up the DVD reader object.  You must close all open files
- * before calling this function.
+ * @param dvd A read handle that should be closed.
+ *
+ * DVDClose(dvd);
  */
 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_INFO_FILE,        /**< VIDEO_TS.IFO  or VTS_XX_0.IFO (title) */
+  DVD_READ_INFO_BACKUP_FILE, /**< VIDEO_TS.BUP  or VTS_XX_0.BUP (title) */
+  DVD_READ_MENU_VOBS,        /**< VIDEO_TS.VOB  or VTS_XX_0.VOB (title) */
+  DVD_READ_TITLE_VOBS        /**< VTS_XX_[1-9].VOB (title).  All files in 
+				  the title set are opened and read as a
+				  single file. */
 } 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.
  *
- * 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 );
+ * @param dvd  A dvd read handle.
+ * @param titlenum Which Video Title Set should be used, VIDEO_TS is 0.
+ * @param domain Which domain. 
+ * @return If successful a a file read handle is returned, otherwise 0.
+ *
+ * dvd_file = DVDOpenFile(dvd, titlenum, domain); */
+dvd_file_t *DVDOpenFile( dvd_reader_t *, int, dvd_read_domain_t );
 
 /**
- * DVDCloseFile(dvd_file);
+ * Closes a file and frees the associated structure.
  *
- * Closes a file and frees the associated structure.
+ * @param dvd_file  The file read handle to be closed.
+ *
+ * DVDCloseFile(dvd_file);
  */
 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.
+ *
+ * @param dvd_file  A file read handle.
+ * @param offset Block offset from the start of the file to start reading at.
+ * @param block_count Number of block to read.
+ * @param data Pointer to a buffer to write the data into.
+ * @return Returns number of blocks read on success, -1 on error.
+ *
+ * blocks_read = DVDReadBlocks(dvd_file, offset, block_count, data);
  */
 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.
+ *
+ * @param dvd_file  A file read handle.
+ * @param seek_offset Byte offset from the start of the file to seek to.
+ * @return The resulting position in bytes from the beginning of the file.
+ *
+ * offset_set = DVDFileSeek(dvd_file, seek_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.
+ *
+ * @param dvd_file  A file read handle.
+ * @param data Pointer to a buffer to write the data into.
+ * @param bytes Number of bytes to read.
+ * @return Returns number of bytes read on success, -1 on error.
+ *
+ * bytes_read = DVDReadBytes(dvd_file, data, bytes);
  */
 ssize_t DVDReadBytes( dvd_file_t *, void *, size_t );
 
 /**
- * blocks = DVDFileSize(dvd_file);
+ * Returns the file size in blocks.
  *
- * Returns the file size in blocks.
+ * @param dvd_file  A file read handle.
+ * @return The size of the file in blocks, -1 on error.
+ *
+ * blocks = DVDFileSize(dvd_file);
  */
 ssize_t DVDFileSize( dvd_file_t * );
 
+/**
+ * Get a unique 128 bit disc ID.
+ * This is the MD5 sum of VIDEO_TS.IFO and the VTS_0?_0.IFO files
+ * in title order (those that exist).
+ * If you need a 'text' representation of the id, print it as a
+ * hexadecimal number, using lowercase letters, discid[0] first. 
+ * I.e. the same format as the command-line 'md5sum' program uses.
+ *
+ * @param dvd A read handle to get the disc ID from
+ * @param discid The buffer to put the disc ID into. The buffer must
+ *               have room for 128 bits (16 chars).
+ * @return 0 on success, -1 on error.
+ */
+int DVDDiscID( dvd_reader_t *, unsigned char * );
+
+/**
+ * Get the UDF VolumeIdentifier and VolumeSetIdentifier
+ * from the PrimaryVolumeDescriptor.
+ *
+ * @param dvd A read handle to get the disc ID from
+ * @param volid The buffer to put the VolumeIdentifier into.
+ *              The VolumeIdentifier is latin-1 encoded (8bit unicode)
+ *              null terminated and max 32 bytes (including '\0')
+ * @param volid_size No more than volid_size bytes will be copied to volid.
+ *                   If the VolumeIdentifier is truncated because of this
+ *                   it will still be null terminated.
+ * @param volsetid The buffer to put the VolumeSetIdentifier into.
+ *                 The VolumeIdentifier is 128 bytes as
+ *                 stored in the UDF PrimaryVolumeDescriptor.
+ *                 Note that this is not a null terminated string.
+ * @param volsetid_size At most volsetid_size bytes will be copied to volsetid.
+ * @return 0 on success, -1 on error.
+ */
+int DVDUDFVolumeInfo( dvd_reader_t *, char *, unsigned int,
+		      unsigned char *, unsigned int );
+
+/**
+ * Get the ISO9660 VolumeIdentifier and VolumeSetIdentifier
+ *
+ * * Only use this function as fallback if DVDUDFVolumeInfo returns 0   *
+ * * this will happen on a disc mastered only with a iso9660 filesystem *
+ * * All video DVD discs have UDF filesystem                            *
+ *
+ * @param dvd A read handle to get the disc ID from
+ * @param volid The buffer to put the VolumeIdentifier into.
+ *              The VolumeIdentifier is coded with '0-9','A-Z','_'
+ *              null terminated and max 33 bytes (including '\0')
+ * @param volid_size No more than volid_size bytes will be copied to volid.
+ *                   If the VolumeIdentifier is truncated because of this
+ *                   it will still be null terminated.
+ * @param volsetid The buffer to put the VolumeSetIdentifier into.
+ *                 The VolumeIdentifier is 128 bytes as
+ *                 stored in the ISO9660 PrimaryVolumeDescriptor.
+ *                 Note that this is not a null terminated string.
+ * @param volsetid_size At most volsetid_size bytes will be copied to volsetid.
+ * @return 0 on success, -1 on error.
+ */
+int DVDISOVolumeInfo( dvd_reader_t *, char *, unsigned int,
+		      unsigned char *, unsigned int );
+
+/**
+ * Sets the level of caching that is done when reading from a device
+ *
+ * @param dvd A read handle to get the disc ID from
+ * @param level The level of caching wanted.
+ *             -1 - returns the current setting.
+ *              0 - UDF Cache turned off.
+ *              1 - (default level) Pointers to IFO files and some data from
+ *                  PrimaryVolumeDescriptor are cached. 
+ *
+ * @return The level of caching.
+ */
+int DVDUDFCacheLevel( dvd_reader_t *, int );
+
 #ifdef __cplusplus
 };
 #endif
--- a/libmpdvdkit2/dvd_udf.c	Thu Jun 30 22:41:40 2005 +0000
+++ b/libmpdvdkit2/dvd_udf.c	Thu Jun 30 22:48:26 2005 +0000
@@ -5,8 +5,6 @@
  * Modifications by:
  *   Billy Biggs <vektor@dumbterm.net>.
  *   Björn Englund <d4bjorn@dtek.chalmers.se>.
- *   Joey Parrish <joey@nicewarrior.org>.
- *     - updated from libdvdread 0.9.4 and removed udf caching
  *
  * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
  * detailed CVS changelog at http://www.mplayerhq.hu/cgi-bin/cvsweb.cgi/main/
@@ -34,10 +32,11 @@
  * http://www.gnu.org/copyleft/gpl.html
  */
  
+#include "config.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-//#include <assert.h>
 #ifndef __MINGW32__
 #include <sys/ioctl.h>
 #endif
@@ -50,7 +49,7 @@
 #include "dvd_udf.h"
 
 /* Private but located in/shared with dvd_reader.c */
-extern int DVDReadBlocksUDFRaw( dvd_reader_t *device, uint32_t lb_number,
+extern int UDFReadBlocksRaw( dvd_reader_t *device, uint32_t lb_number,
 				size_t block_count, unsigned char *data, 
 				int encrypted );
 
@@ -64,7 +63,7 @@
   
   while(count > 0) {
     
-    ret = DVDReadBlocksUDFRaw(device, lb_number, count, data, encrypted);
+    ret = UDFReadBlocksRaw(device, lb_number, count, data, encrypted);
         
     if(ret <= 0) {
       /* One of the reads failed or nothing more to read, too bad.
@@ -128,6 +127,201 @@
   uint8_t filetype;
 };
 
+struct udf_cache {
+  int avdp_valid;
+  struct avdp_t avdp;
+  int pvd_valid;
+  struct pvd_t pvd;
+  int partition_valid;
+  struct Partition partition;
+  int rooticb_valid;
+  struct AD rooticb;
+  int lb_num;
+  struct lbudf *lbs;
+  int map_num;
+  struct icbmap *maps;
+};
+
+typedef enum {
+  PartitionCache, RootICBCache, LBUDFCache, MapCache, AVDPCache, PVDCache
+} UDFCacheType;
+
+extern void *GetUDFCacheHandle(dvd_reader_t *device);
+extern void SetUDFCacheHandle(dvd_reader_t *device, void *cache);
+
+void FreeUDFCache(void *cache)
+{
+  struct udf_cache *c = (struct udf_cache *)cache;
+  if(c == NULL) {
+    return;
+  }
+  if(c->lbs) {
+    free(c->lbs);
+  }
+  if(c->maps) {
+    free(c->maps);
+  }
+  free(c);
+}
+
+
+static int GetUDFCache(dvd_reader_t *device, UDFCacheType type,
+		       uint32_t nr, void *data)
+{
+  int n;
+  struct udf_cache *c;
+
+  if(DVDUDFCacheLevel(device, -1) <= 0) {
+    return 0;
+  }
+  
+  c = (struct udf_cache *)GetUDFCacheHandle(device);
+  
+  if(c == NULL) {
+    return 0;
+  }
+  
+  switch(type) {
+  case AVDPCache:
+    if(c->avdp_valid) {
+      *(struct avdp_t *)data = c->avdp;
+      return 1;
+    }    
+    break;
+  case PVDCache:
+    if(c->pvd_valid) {
+      *(struct pvd_t *)data = c->pvd;
+      return 1;
+    }    
+    break;
+  case PartitionCache:
+    if(c->partition_valid) {
+      *(struct Partition *)data = c->partition;
+      return 1;
+    }
+    break;
+  case RootICBCache:
+    if(c->rooticb_valid) {
+      *(struct AD *)data = c->rooticb;
+      return 1;
+    }
+    break;
+  case LBUDFCache:
+    for(n = 0; n < c->lb_num; n++) {
+      if(c->lbs[n].lb == nr) {
+      *(uint8_t **)data = c->lbs[n].data;
+      return 1;
+      }
+    }
+    break;
+  case MapCache:
+    for(n = 0; n < c->map_num; n++) {
+      if(c->maps[n].lbn == nr) {
+       *(struct icbmap *)data = c->maps[n];
+       return 1;
+      }
+    }
+    break;
+  default:
+    break;
+  }
+  
+  return 0;
+}
+
+static int SetUDFCache(dvd_reader_t *device, UDFCacheType type,
+		       uint32_t nr, void *data)
+{
+  int n;
+  struct udf_cache *c;
+
+  if(DVDUDFCacheLevel(device, -1) <= 0) {
+    return 0;
+  }
+
+  c = (struct udf_cache *)GetUDFCacheHandle(device);
+  
+  if(c == NULL) {
+    c = calloc(1, sizeof(struct udf_cache));    
+    //    fprintf(stderr, "calloc: %d\n", sizeof(struct udf_cache));    
+    if(c == NULL) {
+      return 0;
+    }
+    SetUDFCacheHandle(device, c);
+  }
+  
+  
+  switch(type) {
+  case AVDPCache:
+    c->avdp = *(struct avdp_t *)data; 
+    c->avdp_valid = 1;
+    break;
+  case PVDCache:
+    c->pvd = *(struct pvd_t *)data; 
+    c->pvd_valid = 1;
+    break;
+  case PartitionCache:
+    c->partition = *(struct Partition *)data; 
+    c->partition_valid = 1;
+    break;
+  case RootICBCache:
+    c->rooticb = *(struct AD *)data; 
+    c->rooticb_valid = 1;
+    break;
+  case LBUDFCache:
+    for(n = 0; n < c->lb_num; n++) {
+      if(c->lbs[n].lb == nr) {
+       /* replace with new data */
+       c->lbs[n].data = *(uint8_t **)data;
+       c->lbs[n].lb = nr;
+       return 1;
+      }
+    }
+    c->lb_num++;
+    c->lbs = realloc(c->lbs, c->lb_num * sizeof(struct lbudf));
+    /*
+    fprintf(stderr, "realloc lb: %d * %d = %d\n",
+	    c->lb_num, sizeof(struct lbudf),
+	    c->lb_num * sizeof(struct lbudf));
+    */
+    if(c->lbs == NULL) {
+      c->lb_num = 0;
+      return 0;
+    }
+    c->lbs[n].data = *(uint8_t **)data;
+    c->lbs[n].lb = nr;
+    break;
+  case MapCache:
+    for(n = 0; n < c->map_num; n++) {
+      if(c->maps[n].lbn == nr) {
+       /* replace with new data */
+       c->maps[n] = *(struct icbmap *)data;
+       c->maps[n].lbn = nr;
+       return 1;
+      }
+    }
+    c->map_num++;
+    c->maps = realloc(c->maps, c->map_num * sizeof(struct icbmap));
+    /*
+    fprintf(stderr, "realloc maps: %d * %d = %d\n",
+	    c->map_num, sizeof(struct icbmap),
+	    c->map_num * sizeof(struct icbmap));
+    */
+    if(c->maps == NULL) {
+      c->map_num = 0;
+      return 0;
+    }
+    c->maps[n] = *(struct icbmap *)data;
+    c->maps[n].lbn = nr;
+    break;
+  default:
+    return 0;
+  }
+    
+  return 1;
+}
+
+
 /* 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))
@@ -302,8 +496,16 @@
     uint8_t LogBlock[DVD_VIDEO_LB_LEN];
     uint32_t lbnum;
     uint16_t TagID;
+    struct icbmap tmpmap;
 
     lbnum = partition->Start + ICB.Location;
+    tmpmap.lbn = lbnum;
+    if(GetUDFCache(device, MapCache, lbnum, &tmpmap)) {
+      *FileType = tmpmap.filetype;
+      *File = tmpmap.file;
+      return 1;
+    }
+
     do {
         if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) {
             TagID = 0;
@@ -313,6 +515,9 @@
 
         if( TagID == 261 ) {
             UDFFileEntry( LogBlock, FileType, partition, File );
+           tmpmap.file = *File;
+           tmpmap.filetype = *FileType;
+           SetUDFCache(device, MapCache, tmpmap.lbn, &tmpmap);
             return 1;
         };
     } while( ( lbnum <= partition->Start + ICB.Location + ( ICB.Length - 1 )
@@ -328,7 +533,8 @@
  * 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 ) 
+                       struct Partition *partition, struct AD *FileICB,
+		       int cache_file_info) 
 {
     char filename[ MAX_UDF_FILE_NAME_LEN ];
     uint8_t directory[ 2 * DVD_VIDEO_LB_LEN ];
@@ -336,9 +542,78 @@
     uint16_t TagID;
     uint8_t filechar;
     unsigned int p;
+    uint8_t *cached_dir = NULL;
+    uint32_t dir_lba;
+    struct AD tmpICB;
+    int found = 0;
+    int in_cache = 0;
 
     /* Scan dir for ICB of file */
     lbnum = partition->Start + Dir.Location;
+    
+    if(DVDUDFCacheLevel(device, -1) > 0) {
+      /* caching */
+      
+      if(!GetUDFCache(device, LBUDFCache, lbnum, &cached_dir)) {
+	dir_lba = (Dir.Length + DVD_VIDEO_LB_LEN) / DVD_VIDEO_LB_LEN;
+	if((cached_dir = malloc(dir_lba * DVD_VIDEO_LB_LEN)) == NULL) {
+	  return 0;
+	}
+	if( DVDReadLBUDF( device, lbnum, dir_lba, cached_dir, 0) <= 0 ) {
+	  free(cached_dir);
+	  cached_dir = NULL;
+	}
+	/*
+	if(cached_dir) {
+	  fprintf(stderr, "malloc dir: %d\n",
+		  dir_lba * DVD_VIDEO_LB_LEN);
+	}
+	*/
+	SetUDFCache(device, LBUDFCache, lbnum, &cached_dir);
+      } else {
+	in_cache = 1;
+      }
+      
+      if(cached_dir == NULL) {
+	return 0;
+      }
+      
+      p = 0;
+      
+      while( p < Dir.Length ) {
+        UDFDescriptor( &cached_dir[ p ], &TagID );
+        if( TagID == 257 ) {
+	  p += UDFFileIdentifier( &cached_dir[ p ], &filechar,
+				  filename, &tmpICB );
+	  if(cache_file_info && !in_cache) {
+	    uint8_t tmpFiletype;
+	    struct AD tmpFile;
+	    
+	    if( !strcasecmp( FileName, filename ) ) {
+	      *FileICB = tmpICB;
+	      found = 1;
+	      
+	    }
+	    UDFMapICB(device, tmpICB, &tmpFiletype,
+		      partition, &tmpFile);
+	  } else {
+	    if( !strcasecmp( FileName, filename ) ) {
+	      *FileICB = tmpICB;
+	      return 1;
+	    }
+	  }
+        } else {
+	  if(cache_file_info && (!in_cache) && found) {
+	    return 1;
+	  }
+	  return 0;
+        }
+      }
+      if(cache_file_info && (!in_cache) && found) {
+	return 1;
+      }
+      return 0;
+    }
 
     if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) {
         return 0;
@@ -380,6 +655,10 @@
   int terminate;
   struct avdp_t; 
   
+  if(GetUDFCache(device, AVDPCache, 0, avdp)) {
+    return 1;
+  }
+
   /* Find Anchor */
   lastsector = 0;
   lbnum = 256;   /* Try #1, prime anchor */
@@ -427,6 +706,8 @@
   avdp->rvds.location = MVDS_location;
   avdp->rvds.length = MVDS_length;
   
+  SetUDFCache(device, AVDPCache, 0, avdp);
+  
   return 1;
 }
 
@@ -514,8 +795,11 @@
     strcat( tokenline, filename );
 
     
+    if(!(GetUDFCache(device, PartitionCache, 0, &partition) &&
+        GetUDFCache(device, RootICBCache, 0, &RootICB))) {
       /* Find partition, 0 is the standard location for DVD Video.*/
       if( !UDFFindPartition( device, 0, &partition ) ) return 0;
+      SetUDFCache(device, PartitionCache, 0, &partition);
       
       /* Find root dir ICB */
       lbnum = partition.Start;
@@ -536,23 +820,30 @@
     /* Sanity checks. */
     if( TagID != 256 ) return 0;
     if( RootICB.Partition != 0 ) return 0;
+    SetUDFCache(device, RootICBCache, 0, &RootICB);
+    }
 
     /* Find root dir */
     if( !UDFMapICB( device, RootICB, &filetype, &partition, &File ) ) return 0;
     if( filetype != 4 ) return 0;  /* Root dir should be dir */
 
     {
+      int cache_file_info = 0;
       /* Tokenize filepath */
       token = strtok(tokenline, "/");
       
       while( token != NULL ) {
        
-        if( !UDFScanDir( device, File, token, &partition, &ICB)) {
+        if( !UDFScanDir( device, File, token, &partition, &ICB,
+                        cache_file_info)) {
          return 0;
        }
         if( !UDFMapICB( device, ICB, &filetype, &partition, &File ) ) {
          return 0;
        }
+       if(!strcmp(token, "VIDEO_TS")) {
+         cache_file_info = 1;
+       }
         token = strtok( NULL, "/" );
       }
     } 
@@ -636,12 +927,76 @@
 {
   uint8_t pvd_buf[DVD_VIDEO_LB_LEN];
   
+  if(GetUDFCache(device, PVDCache, 0, pvd)) {
+    return 1;
+  }
+
   if(!UDFGetDescriptor( device, 1, pvd_buf, sizeof(pvd_buf))) {
     return 0;
   }
   
   memcpy(pvd->VolumeIdentifier, &pvd_buf[24], 32);
   memcpy(pvd->VolumeSetIdentifier, &pvd_buf[72], 128);
+  SetUDFCache(device, PVDCache, 0, pvd);
   
   return 1;
 }
+
+/**
+ * Gets the Volume Identifier string, in 8bit unicode (latin-1)
+ * volid, place to put the string
+ * volid_size, size of the buffer volid points to
+ * returns the size of buffer needed for all data
+ */
+int UDFGetVolumeIdentifier(dvd_reader_t *device, char *volid,
+			   unsigned int volid_size)
+{
+  struct pvd_t pvd;
+  unsigned int volid_len;
+
+  /* get primary volume descriptor */
+  if(!UDFGetPVD(device, &pvd)) {
+    return 0;
+  }
+
+  volid_len = pvd.VolumeIdentifier[31];
+  if(volid_len > 31) {
+    /* this field is only 32 bytes something is wrong */
+    volid_len = 31;
+  }
+  if(volid_size > volid_len) {
+    volid_size = volid_len;
+  }
+  Unicodedecode(pvd.VolumeIdentifier, volid_size, volid);
+  
+  return volid_len;
+}
+
+/**
+ * Gets the Volume Set Identifier, as a 128-byte dstring (not decoded)
+ * WARNING This is not a null terminated string
+ * volsetid, place to put the data
+ * volsetid_size, size of the buffer volsetid points to 
+ * the buffer should be >=128 bytes to store the whole volumesetidentifier
+ * returns the size of the available volsetid information (128)
+ * or 0 on error
+ */
+int UDFGetVolumeSetIdentifier(dvd_reader_t *device, uint8_t *volsetid,
+			      unsigned int volsetid_size)
+{
+  struct pvd_t pvd;
+
+  /* get primary volume descriptor */
+  if(!UDFGetPVD(device, &pvd)) {
+    return 0;
+  }
+
+
+  if(volsetid_size > 128) {
+    volsetid_size = 128;
+  }
+  
+  memcpy(volsetid, pvd.VolumeSetIdentifier, volsetid_size);
+  
+  return 128;
+}
--- a/libmpdvdkit2/dvd_udf.h	Thu Jun 30 22:41:40 2005 +0000
+++ b/libmpdvdkit2/dvd_udf.h	Thu Jun 30 22:48:26 2005 +0000
@@ -7,7 +7,8 @@
  *
  * Modifications by:
  *   Billy Biggs <vektor@dumbterm.net>.
- *
+ *   Björn Englund <d4bjorn@dtek.chalmers.se>.
+ * 
  * 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
@@ -47,6 +48,11 @@
  */
 uint32_t UDFFindFile( dvd_reader_t *device, char *filename, uint32_t *size );
 
+void FreeUDFCache(void *cache);
+int UDFGetVolumeIdentifier(dvd_reader_t *device,
+			   char *volid, unsigned int volid_size);
+int UDFGetVolumeSetIdentifier(dvd_reader_t *device,
+			      uint8_t *volsetid, unsigned int volsetid_size);
 #ifdef __cplusplus
 };
 #endif
--- a/libmpdvdkit2/ifo_print.c	Thu Jun 30 22:41:40 2005 +0000
+++ b/libmpdvdkit2/ifo_print.c	Thu Jun 30 22:48:26 2005 +0000
@@ -1,10 +1,7 @@
 /* 
- * Copyright (C) 2000, 2001, 2002 Björn Englund <d4bjorn@dtek.chalmers.se>, 
- *                                Håkan Hjort <d95hjort@dtek.chalmers.se>
- *
- * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
- * detailed CVS changelog at http://www.mplayerhq.hu/cgi-bin/cvsweb.cgi/main/
- * $Id$
+ * Copyright (C) 2000, 2001, 2002, 2003
+ *               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
@@ -21,26 +18,27 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#include "config.h"
+
 #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"
+#include "dvdread_internal.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) {
+static void ifoPrint_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);
+  CHECK_VALUE((dtime->hour>>4) < 0xa && (dtime->hour&0xf) < 0xa);
+  CHECK_VALUE((dtime->minute>>4) < 0x7 && (dtime->minute&0xf) < 0xa);
+  CHECK_VALUE((dtime->second>>4) < 0x7 && (dtime->second&0xf) < 0xa);
+  CHECK_VALUE((dtime->frame_u&0xf) < 0xa);
   
   printf("%02x:%02x:%02x.%02x", 
 	 dtime->hour,
@@ -79,7 +77,7 @@
   printf("\n");
 }
 
-static void ifoPrint_video_attributes(int level, video_attr_t *attr) {
+static void ifoPrint_video_attributes(video_attr_t *attr) {
   
   /* The following test is shorter but not correct ISO C,
      memcmp(attr,my_friendly_zeros, sizeof(video_attr_t)) */
@@ -90,6 +88,7 @@
      && attr->unknown1 == 0 
      && attr->line21_cc_1 == 0 
      && attr->line21_cc_2 == 0 
+     && attr->bit_rate == 0 
      && attr->video_format == 0 
      && attr->letterboxed == 0 
      && attr->film_mode == 0) {
@@ -149,7 +148,7 @@
   }
   
   printf("U%x ", attr->unknown1);
-  assert(!attr->unknown1);
+  CHECK_VALUE(!attr->unknown1);
   
   if(attr->line21_cc_1 || attr->line21_cc_2) {
     printf("NTSC CC ");
@@ -158,6 +157,17 @@
     if(attr->line21_cc_2)
       printf("2 ");
   }
+
+  switch(attr->bit_rate) {
+    case 0:
+      printf("Variable Bit Rate ");
+      break;
+    case 1:
+      printf("Constant Bit Rate ");
+      break;
+    default:
+      printf("(please send a bug report)");
+  }
   
   {
     int height = 480;
@@ -192,7 +202,7 @@
   }
 }
 
-static void ifoPrint_audio_attributes(int level, audio_attr_t *attr) {
+static void ifoPrint_audio_attributes(audio_attr_t *attr) {
   
   if(attr->audio_format == 0
      && attr->multichannel_extension == 0
@@ -201,8 +211,10 @@
      && attr->quantization == 0
      && attr->sample_frequency == 0
      && attr->channels == 0
+     && attr->lang_code == 0
      && attr->lang_extension == 0
-     && attr->unknown1 == 0
+     && attr->code_extension == 0
+     && attr->unknown3 == 0
      && attr->unknown1 == 0) {
     printf("-- Unspecified --");
     return;
@@ -240,10 +252,11 @@
   switch(attr->lang_type) {
   case 0:
     // not specified
-    assert(attr->lang_code == 0 || attr->lang_code == 0xffff);
+    CHECK_VALUE(attr->lang_code == 0 || attr->lang_code == 0xffff);
     break;
   case 1:
-    printf("%c%c ", attr->lang_code>>8, attr->lang_code & 0xff);
+    printf("%c%c (%c) ", attr->lang_code>>8, attr->lang_code & 0xff,
+           attr->lang_extension ? attr->lang_extension : ' ');
     break;
   default:
     printf("(please send a bug report) ");
@@ -294,7 +307,7 @@
   
   printf("%dCh ", attr->channels + 1);
   
-  switch(attr->lang_extension) {
+  switch(attr->code_extension) {
   case 0:
     printf("Not specified ");
     break;
@@ -315,34 +328,64 @@
     printf("(please send a bug report) ");
   }
     
-  printf("%d ", attr->unknown1);
-  printf("%d ", attr->unknown2);
+  printf("%d ", attr->unknown3);
+  if(attr->application_mode == 1) {
+    printf("ca=%d ", attr->app_info.karaoke.channel_assignment);
+    printf("%d ", attr->app_info.karaoke.version);
+    if(attr->app_info.karaoke.mc_intro) 
+      printf("mc intro ");
+    printf("%s ", attr->app_info.karaoke.mode ? "duet" : "solo");
+    printf("%d ", attr->app_info.karaoke.unknown4);
+  }
+  if(attr->application_mode == 2) {
+    if(attr->app_info.surround.dolby_encoded) {
+      printf("dolby surround ");
+    }
+    printf("%d ", attr->app_info.surround.unknown5);
+    printf("%d ", attr->app_info.surround.unknown6);
+  }    
 }
 
-static void ifoPrint_subp_attributes(int level, subp_attr_t *attr) {
+static void ifoPrint_subp_attributes(subp_attr_t *attr) {
   
   if(attr->type == 0
+     && attr->code_mode == 0
      && attr->lang_code == 0
+     && attr->lang_extension == 0
      && attr->zero1 == 0
      && attr->zero2 == 0
-     && attr->lang_extension== 0) {
+     && attr->code_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);
+  switch(attr->code_mode) {
+  case 0:
+    printf("Coding Mode RLE ");
+    break;
+  case 1:
+    printf("Coding Mode Extended ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }    
+ 
+  if(attr->type == 1) {
+    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 ", 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("lang not specified ");
   }
   
   printf("%d ", attr->zero1);
   printf("%d ", attr->zero2);
-
+  printf("%d ", attr->code_extension);
+  
+  /* Is this correct?  should it not be subp_code_ext here instead? */
   switch(attr->lang_extension) {
   case 0:
     printf("Not specified ");
@@ -498,20 +541,20 @@
   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);
+  ifoPrint_video_attributes(&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);
+    ifoPrint_audio_attributes(&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);
+    ifoPrint_subp_attributes(&vmgi_mat->vmgm_subp_attr);
     printf("\n");
   }
 }
@@ -540,14 +583,14 @@
   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);
+  ifoPrint_video_attributes(&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);
+    ifoPrint_audio_attributes(&vtsi_mat->vtsm_audio_attr);
     printf("\n");
   }
   
@@ -555,19 +598,19 @@
 	 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);
+    ifoPrint_subp_attributes(&vtsi_mat->vtsm_subp_attr);
     printf("\n");
   }
   
   printf("Video attributes of VTS_VOBS: ");
-  ifoPrint_video_attributes(5, &vtsi_mat->vts_video_attr);
+  ifoPrint_video_attributes(&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]);
+    ifoPrint_audio_attributes(&vtsi_mat->vts_audio_attr[i]);
     printf("\n");
   }
   
@@ -575,9 +618,11 @@
 	 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]);
+    ifoPrint_subp_attributes(&vtsi_mat->vts_subp_attr[i]);
     printf("\n");
   }
+  
+  /* FIXME:  Add printing of MultiChannel Extension */
 }
 
 
@@ -631,7 +676,7 @@
   for(i=0;i<nr;i++) {
     printf("Cell: %3i ", i + 1);
 
-    ifoPrint_time(5, &cell_playback[i].playback_time);
+    ifoPrint_time(&cell_playback[i].playback_time);
     printf("\t");
 
     if(cell_playback[i].block_mode || cell_playback[i].block_type) {
@@ -709,7 +754,7 @@
   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");
+  ifoPrint_time(&pgc->playback_time); printf("\n");
 
   /* If no programs/no time then does this mean anything? */
   printf("Prohibited user operations: ");
@@ -734,7 +779,12 @@
   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->pg_playback_mode == 0)
+      printf("PG Playback mode: Sequential\n");
+    else if(!(pgc->pg_playback_mode & 0x80))
+      printf("PG Playback mode: Random %i\n", pgc->pg_playback_mode);
+    else
+      printf("PG Playback mode: Shuffle %i\n", pgc->pg_playback_mode & 0x7f );
   }
   
   if(pgc->nr_of_programs != 0) {
@@ -764,8 +814,23 @@
     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("\tTitle playback type: %s%s%s%s%s%s%s\n",
+	   tt_srpt->title[i].pb_ty.multi_or_random_pgc_title ? 
+	   " One Random PGC Title or Multi PGC Title" : 
+	   " One Sequential PGC Title",
+	   tt_srpt->title[i].pb_ty.jlc_exists_in_cell_cmd ?
+	   "" : ", No Link/Jump/Call exists in Cell command",
+	   tt_srpt->title[i].pb_ty.jlc_exists_in_prepost_cmd ?
+	   "" : ", No Link/Jump/Call exists in Pre- and/or Post-command",
+	   tt_srpt->title[i].pb_ty.jlc_exists_in_button_cmd ?
+	   "" : ", No Link/Jump/Call exists in Button command",
+	   tt_srpt->title[i].pb_ty.jlc_exists_in_tt_dom ?
+	   "" : ", No Link/Jump/Call exists in TT_DOM",
+	   tt_srpt->title[i].pb_ty.chapter_search_or_play ?
+	   ", UOP1 (TT_Play and PTT_Search) prohibited" : "",
+	   tt_srpt->title[i].pb_ty.title_or_time_play ?
+	   ", UOP0 (Time_Play and Time_Search) prohibited" : ""
+	   );    
     printf("\tParental ID field: %04x\n",
 	   tt_srpt->title[i].parental_id);
     printf("\tTitle set starting sector %08x\n", 
@@ -780,6 +845,8 @@
 	 vts_ptt_srpt->nr_of_srpts, 
 	 vts_ptt_srpt->last_byte);
   for(i=0;i<vts_ptt_srpt->nr_of_srpts;i++) {
+    printf("\nVTS_PTT number %d has a offset %d relative to VTS_PTT_SRPT\n", 
+				i + 1, vts_ptt_srpt->ttu_offset[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, 
@@ -790,40 +857,57 @@
 }
 
 
-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;
+  int i, level, vts;
   
   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);
+  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", 
+    
+    printf("Start byte: %i\n", ptl_mait->countries[i].pf_ptl_mai_start_byte);
+    printf("Parental Masks for country: %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);
+    
+    for(vts = 0; vts <= ptl_mait->nr_of_vtss; vts++) {
+      if( vts == 0 ) {
+	printf("VMG    "); 
+      } else {
+	printf("VTS %2d ", vts);
+      }
+      for(level = 0; level < 8; level++) {
+	printf("%d: %04x  ", level,
+	       ptl_mait->countries[i].pf_ptl_mai[vts][level] );
+      }
       printf("\n");
     }
   }
 }
 
+void ifoPrint_VTS_TMAPT(vts_tmapt_t *vts_tmapt) {
+  unsigned int timeunit;
+  int i, j;
+  
+  printf("Number of VTS_TMAPS: %i\n", vts_tmapt->nr_of_tmaps);
+  printf("Last byte: %i\n", vts_tmapt->last_byte);
+
+  for(i = 0; i < vts_tmapt->nr_of_tmaps; i++) {
+    printf("TMAP %i\n", i + 1);
+    printf("  offset %d relative to VTS_TMAPTI\n", vts_tmapt->tmap_offset[i]);
+    printf("  Time unit (seconds): %i\n", vts_tmapt->tmap[i].tmu);
+    printf("  Number of entries: %i\n", vts_tmapt->tmap[i].nr_of_entries);
+    timeunit = vts_tmapt->tmap[i].tmu;
+    for(j = 0; j < vts_tmapt->tmap[i].nr_of_entries; j++) {
+      unsigned int ac_time = timeunit * (j + 1);
+      printf("Time: %2i:%02i:%02i  VOBU Sector: 0x%08x %s\n", 
+	     ac_time / (60 * 60), (ac_time / 60) % 60, ac_time % 60,
+	     vts_tmapt->tmap[i].map_ent[j] & 0x7fffffff,
+	     (vts_tmapt->tmap[i].map_ent[j] >> 31) ? "discontinuity" : "");
+    }
+  }
+}
 
 void ifoPrint_C_ADT(c_adt_t *c_adt) {
   int i, entries;
@@ -870,9 +954,10 @@
   
   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",
+    printf("\nMenu Language Code: %c%c (%c)\n",
 	   pgci_ut->lu[i].lang_code >> 8,
-	   pgci_ut->lu[i].lang_code & 0xff);
+	   pgci_ut->lu[i].lang_code & 0xff,
+	   pgci_ut->lu[i].lang_extension ? pgci_ut->lu[i].lang_extension :' ');
     printf("Menu Existence: %02x\n", pgci_ut->lu[i].exists);
     ifoPrint_PGCIT(pgci_ut->lu[i].pgcit);
   }
@@ -885,31 +970,31 @@
   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);
+  ifoPrint_video_attributes(&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);
+    ifoPrint_audio_attributes(&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);
+    ifoPrint_subp_attributes(&vts_attributes->vtsm_subp_attr);
     printf("\n");
   }
    
   printf("Video attributes of VTSTT_VOBS: ");
-  ifoPrint_video_attributes(5, &vts_attributes->vtstt_vobs_video_attr);
+  ifoPrint_video_attributes(&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]);
+    ifoPrint_audio_attributes(&vts_attributes->vtstt_audio_attr[i]);
     printf("\n");
   }
   
@@ -917,7 +1002,7 @@
 	 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]);
+    ifoPrint_subp_attributes(&vts_attributes->vtstt_subp_attr[i]);
     printf("\n");
   }
 }
@@ -929,6 +1014,8 @@
   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);
+    printf("  offset %d relative to VMG_VTS_ATRT\n", 
+	   vts_atrt->vts_atrt_offsets[i]);
     ifoPrint_VTS_ATTRIBUTES(&vts_atrt->vts[i]);
   }
 }
@@ -1022,6 +1109,14 @@
     } else {
       printf("No Menu PGCI Unit table present\n");
     }
+    
+    printf("\nTime Search table\n");
+    printf(  "-----------------\n");
+    if(ifohandle->vts_tmapt) {
+      ifoPrint_VTS_TMAPT(ifohandle->vts_tmapt);
+    } else {
+      printf("No Time Search table present\n");
+    }
 
     printf("\nMenu Cell Adress table\n");
     printf(  "-----------------\n");
--- a/libmpdvdkit2/ifo_print.h	Thu Jun 30 22:41:40 2005 +0000
+++ b/libmpdvdkit2/ifo_print.h	Thu Jun 30 22:48:26 2005 +0000
@@ -53,6 +53,7 @@
 void ifoPrint_PGC(pgc_t *);
 void ifoPrint_PGCIT(pgcit_t *);
 void ifoPrint_PGCI_UT(pgci_ut_t *);
+void ifoPrint_VTS_TMAPT(vts_tmapt_t *);
 void ifoPrint_C_ADT(c_adt_t *);
 void ifoPrint_VOBU_ADMAP(vobu_admap_t *);
 
--- a/libmpdvdkit2/ifo_read.c	Thu Jun 30 22:41:40 2005 +0000
+++ b/libmpdvdkit2/ifo_read.c	Thu Jun 30 22:48:26 2005 +0000
@@ -1,10 +1,7 @@
 /*
- * Copyright (C) 2000, 2001, 2002  Björn Englund <d4bjorn@dtek.chalmers.se>, 
- *                                 Håkan Hjort <d95hjort@dtek.chalmers.se>
- *
- * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
- * detailed CVS changelog at http://www.mplayerhq.hu/cgi-bin/cvsweb.cgi/main/
- * $Id$
+ * Copyright (C) 2000, 2001, 2002, 2003
+ *               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
@@ -21,25 +18,29 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#include "config.h"
+
 #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"
+#include "dvd_reader.h"
+#include "dvdread_internal.h"
 
 #ifndef DVD_BLOCK_LEN
 #define DVD_BLOCK_LEN 2048
 #endif
 
 #ifndef NDEBUG
+#define CHECK_ZERO0(arg) \
+  if(arg != 0) { \
+    fprintf(stderr, "*** Zero check failed in %s:%i\n    for %s = 0x%x\n", \
+            __FILE__, __LINE__, # arg, arg); \
+  }
 #define CHECK_ZERO(arg) \
   if(memcmp(my_friendly_zeros, &arg, sizeof(arg))) { \
     unsigned int i_CZ; \
@@ -51,6 +52,7 @@
   }
 static const uint8_t my_friendly_zeros[2048];
 #else
+#define CHECK_ZERO0(arg) (void)(arg)
 #define CHECK_ZERO(arg) (void)(arg)
 #endif
 
@@ -87,7 +89,7 @@
 static void ifoFree_PGCIT_internal(pgcit_t *pgcit);
 
 
-static int DVDFileSeek_( dvd_file_t *dvd_file, uint32_t offset ) {
+static inline int DVDFileSeek_( dvd_file_t *dvd_file, uint32_t offset ) {
   return (DVDFileSeek(dvd_file, (int)offset) == (int)offset);
 }
 
@@ -102,6 +104,8 @@
   memset(ifofile, 0, sizeof(ifo_handle_t));
 
   ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_FILE);
+  if(!ifofile->file) /* Should really catch any error and try to fallback */
+    ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_BACKUP_FILE);
   if(!ifofile->file) {
     if(title) {
       fprintf(stderr, "libdvdread: Can't open file VTS_%02d_0.IFO.\n", title);
@@ -150,6 +154,7 @@
 
 
     ifoRead_PGCI_UT(ifofile);
+    ifoRead_VTS_TMAPT(ifofile);
     ifoRead_C_ADT(ifofile);
     ifoRead_VOBU_ADMAP(ifofile);
 
@@ -184,6 +189,8 @@
   memset(ifofile, 0, sizeof(ifo_handle_t));
 
   ifofile->file = DVDOpenFile(dvd, 0, DVD_READ_INFO_FILE);
+  if(!ifofile->file) /* Should really catch any error and try to fallback */
+    ifofile->file = DVDOpenFile(dvd, 0, DVD_READ_INFO_BACKUP_FILE);
   if(!ifofile->file) {
     fprintf(stderr, "libdvdread: Can't open file VIDEO_TS.IFO.\n");
     free(ifofile);
@@ -215,6 +222,8 @@
   }
     
   ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_FILE);
+  if(!ifofile->file) /* Should really catch any error and try to fallback */
+    ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_BACKUP_FILE);
   if(!ifofile->file) {
     fprintf(stderr, "libdvdread: Can't open file VTS_%02d_0.IFO.\n", title);
     free(ifofile);
@@ -320,33 +329,33 @@
   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 <= 
+  CHECK_VALUE(vmgi_mat->vmg_last_sector != 0);
+  CHECK_VALUE(vmgi_mat->vmgi_last_sector != 0);
+  CHECK_VALUE(vmgi_mat->vmgi_last_sector * 2 <= vmgi_mat->vmg_last_sector);
+  CHECK_VALUE(vmgi_mat->vmgi_last_sector * 2 <= vmgi_mat->vmg_last_sector);
+  CHECK_VALUE(vmgi_mat->vmg_nr_of_volumes != 0);
+  CHECK_VALUE(vmgi_mat->vmg_this_volume_nr != 0);
+  CHECK_VALUE(vmgi_mat->vmg_this_volume_nr <= vmgi_mat->vmg_nr_of_volumes);
+  CHECK_VALUE(vmgi_mat->disc_side == 1 || vmgi_mat->disc_side == 2);
+  CHECK_VALUE(vmgi_mat->vmg_nr_of_title_sets != 0);
+  CHECK_VALUE(vmgi_mat->vmgi_last_byte >= 341);
+  CHECK_VALUE(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 || 
+  /* It seems that first_play_pgc is optional. */
+  CHECK_VALUE(vmgi_mat->first_play_pgc < vmgi_mat->vmgi_last_byte);
+  CHECK_VALUE(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);
+  CHECK_VALUE(vmgi_mat->tt_srpt <= vmgi_mat->vmgi_last_sector);
+  CHECK_VALUE(vmgi_mat->vmgm_pgci_ut <= vmgi_mat->vmgi_last_sector);
+  CHECK_VALUE(vmgi_mat->ptl_mait <= vmgi_mat->vmgi_last_sector);
+  CHECK_VALUE(vmgi_mat->vts_atrt <= vmgi_mat->vmgi_last_sector);
+  CHECK_VALUE(vmgi_mat->txtdt_mgi <= vmgi_mat->vmgi_last_sector);
+  CHECK_VALUE(vmgi_mat->vmgm_c_adt <= vmgi_mat->vmgi_last_sector);
+  CHECK_VALUE(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);
+  CHECK_VALUE(vmgi_mat->nr_of_vmgm_audio_streams <= 1);
+  CHECK_VALUE(vmgi_mat->nr_of_vmgm_subp_streams <= 1);
 
   return 1;
 }
@@ -422,34 +431,44 @@
   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 &&
+  CHECK_ZERO(vtsi_mat->zero_21);
+  CHECK_VALUE(vtsi_mat->vtsi_last_sector*2 <= vtsi_mat->vts_last_sector);
+  CHECK_VALUE(vtsi_mat->vtsi_last_byte/DVD_BLOCK_LEN <= vtsi_mat->vtsi_last_sector);
+  CHECK_VALUE(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 || 
+  CHECK_VALUE(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);
+  CHECK_VALUE(vtsi_mat->vts_ptt_srpt <= vtsi_mat->vtsi_last_sector);
+  CHECK_VALUE(vtsi_mat->vts_pgcit <= vtsi_mat->vtsi_last_sector);
+  CHECK_VALUE(vtsi_mat->vtsm_pgci_ut <= vtsi_mat->vtsi_last_sector);
+  CHECK_VALUE(vtsi_mat->vts_tmapt <= vtsi_mat->vtsi_last_sector);
+  CHECK_VALUE(vtsi_mat->vtsm_c_adt <= vtsi_mat->vtsi_last_sector);
+  CHECK_VALUE(vtsi_mat->vtsm_vobu_admap <= vtsi_mat->vtsi_last_sector);
+  CHECK_VALUE(vtsi_mat->vts_c_adt <= vtsi_mat->vtsi_last_sector);
+  CHECK_VALUE(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);
+  CHECK_VALUE(vtsi_mat->nr_of_vtsm_audio_streams <= 1);
+  CHECK_VALUE(vtsi_mat->nr_of_vtsm_subp_streams <= 1);
 
-  assert(vtsi_mat->nr_of_vts_audio_streams <= 8);
+  CHECK_VALUE(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);
+  CHECK_VALUE(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]);      
-
+  
+  for(i = 0; i < 8; i++) {
+    CHECK_ZERO0(vtsi_mat->vts_mu_audio_attr[i].zero1);
+    CHECK_ZERO0(vtsi_mat->vts_mu_audio_attr[i].zero2);
+    CHECK_ZERO0(vtsi_mat->vts_mu_audio_attr[i].zero3);
+    CHECK_ZERO0(vtsi_mat->vts_mu_audio_attr[i].zero4);
+    CHECK_ZERO0(vtsi_mat->vts_mu_audio_attr[i].zero5);
+    CHECK_ZERO(vtsi_mat->vts_mu_audio_attr[i].zero6);
+  }
+  
   return 1;
 }
 
@@ -470,7 +489,7 @@
   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);
+  CHECK_VALUE(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;
@@ -572,9 +591,9 @@
     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 <= 
+    CHECK_VALUE(cell_playback[i].last_vobu_start_sector <= 
            cell_playback[i].last_sector);
-    assert(cell_playback[i].first_sector <= 
+    CHECK_VALUE(cell_playback[i].first_sector <= 
            cell_playback[i].last_vobu_start_sector);
   }
 
@@ -627,7 +646,7 @@
     B2N_32(pgc->palette[i]);
   
   CHECK_ZERO(pgc->zero_1);
-  assert(pgc->nr_of_programs <= pgc->nr_of_cells);
+  CHECK_VALUE(pgc->nr_of_programs <= pgc->nr_of_cells);
 
   /* verify time (look at print_time) */
   for(i = 0; i < 8; i++)
@@ -641,13 +660,13 @@
   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);
+    CHECK_VALUE(pgc->program_map_offset == 0);
+    CHECK_VALUE(pgc->cell_playback_offset == 0);
+    CHECK_VALUE(pgc->cell_position_offset == 0);
   } else {
-    assert(pgc->program_map_offset != 0);
-    assert(pgc->cell_playback_offset != 0);
-    assert(pgc->cell_position_offset != 0);
+    CHECK_VALUE(pgc->program_map_offset != 0);
+    CHECK_VALUE(pgc->cell_playback_offset != 0);
+    CHECK_VALUE(pgc->cell_position_offset != 0);
   }
   
   if(pgc->command_tbl_offset != 0) {
@@ -728,11 +747,10 @@
   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. */
-  }
+  /* It seems that first_play_pgc is optional after all. */
+  ifofile->first_play_pgc = 0;
+  if(ifofile->vmgi_mat->first_play_pgc == 0)
+    return 1;
   
   ifofile->first_play_pgc = (pgc_t *)malloc(sizeof(pgc_t));
   if(!ifofile->first_play_pgc)
@@ -825,22 +843,22 @@
   
 
   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);
+  CHECK_VALUE(tt_srpt->nr_of_srpts != 0);
+  CHECK_VALUE(tt_srpt->nr_of_srpts < 100); // ??
+  CHECK_VALUE((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);
+    CHECK_VALUE(tt_srpt->title[i].pb_ty.zero_1 == 0);
+    CHECK_VALUE(tt_srpt->title[i].nr_of_angles != 0);
+    CHECK_VALUE(tt_srpt->title[i].nr_of_angles < 10);
+    //CHECK_VALUE(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);
+    CHECK_VALUE(tt_srpt->title[i].nr_of_ptts < 1000); // ??
+    CHECK_VALUE(tt_srpt->title[i].title_set_nr != 0);
+    CHECK_VALUE(tt_srpt->title[i].title_set_nr < 100); // ??
+    CHECK_VALUE(tt_srpt->title[i].vts_ttn != 0);
+    CHECK_VALUE(tt_srpt->title[i].vts_ttn < 100); // ??
+    //CHECK_VALUE(tt_srpt->title[i].title_set_sector != 0);
   }
   
   // Make this a function
@@ -906,8 +924,8 @@
   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); // ??
+  CHECK_VALUE(vts_ptt_srpt->nr_of_srpts != 0);
+  CHECK_VALUE(vts_ptt_srpt->nr_of_srpts < 100); // ??
   
   info_length = vts_ptt_srpt->last_byte + 1 - VTS_PTT_SRPT_SIZE;
   
@@ -931,8 +949,10 @@
        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);
+    CHECK_VALUE(data[i] + sizeof(ptt_info_t) <= vts_ptt_srpt->last_byte + 1 + 4);
   }
+ 
+  vts_ptt_srpt->ttu_offset = data;
   
   vts_ptt_srpt->title = malloc(vts_ptt_srpt->nr_of_srpts * sizeof(ttu_t));
   if(!vts_ptt_srpt->title) {
@@ -951,7 +971,7 @@
        Magic Knight Rayearth Daybreak is mastered very strange and has 
        Titles with 0 PTTs. */
     if(n < 0) n = 0;
-    assert(n % 4 == 0);
+    CHECK_VALUE(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));
@@ -965,14 +985,13 @@
     }
     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);
+      CHECK_VALUE(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++) {
@@ -982,12 +1001,12 @@
   }
   
   for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
-    assert(vts_ptt_srpt->title[i].nr_of_ptts < 1000); // ??
+    CHECK_VALUE(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); // ??
+      CHECK_VALUE(vts_ptt_srpt->title[i].ptt[j].pgcn != 0 );
+      CHECK_VALUE(vts_ptt_srpt->title[i].ptt[j].pgcn < 1000); // ??
+      CHECK_VALUE(vts_ptt_srpt->title[i].ptt[j].pgn != 0);
+      CHECK_VALUE(vts_ptt_srpt->title[i].ptt[j].pgn < 100); // ??
     }
   }
 
@@ -1003,6 +1022,7 @@
     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->ttu_offset);
     free(ifofile->vts_ptt_srpt->title);
     free(ifofile->vts_ptt_srpt);
     ifofile->vts_ptt_srpt = 0;
@@ -1013,7 +1033,7 @@
 int ifoRead_PTL_MAIT(ifo_handle_t *ifofile) {
   ptl_mait_t *ptl_mait;
   int info_length;
-  unsigned int i;
+  unsigned int i, j;
 
   if(!ifofile)
     return 0;
@@ -1024,8 +1044,7 @@
   if(ifofile->vmgi_mat->ptl_mait == 0)
     return 1;
 
-  if(!DVDFileSeek_(ifofile->file, 
-		  ifofile->vmgi_mat->ptl_mait * DVD_BLOCK_LEN))
+  if(!DVDFileSeek_(ifofile->file, ifofile->vmgi_mat->ptl_mait * DVD_BLOCK_LEN))
     return 0;
 
   ptl_mait = (ptl_mait_t *)malloc(sizeof(ptl_mait_t));
@@ -1044,26 +1063,29 @@
   B2N_16(ptl_mait->nr_of_vtss);
   B2N_32(ptl_mait->last_byte);
   
-  info_length = ptl_mait->last_byte + 1 - PTL_MAIT_SIZE;
+  CHECK_VALUE(ptl_mait->nr_of_countries != 0);
+  CHECK_VALUE(ptl_mait->nr_of_countries < 100); // ??
+  CHECK_VALUE(ptl_mait->nr_of_vtss != 0);
+  CHECK_VALUE(ptl_mait->nr_of_vtss < 100); // ??  
+  CHECK_VALUE(ptl_mait->nr_of_countries * PTL_MAIT_COUNTRY_SIZE 
+	      <= 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 */
+  info_length = ptl_mait->nr_of_countries * sizeof(ptl_mait_country_t);
   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++) {
+    if(!(DVDReadBytes(ifofile->file, &ptl_mait->countries[i], PTL_MAIT_COUNTRY_SIZE))) {
+      fprintf(stderr, "libdvdread: Unable to read PTL_MAIT.\n");
+      free(ptl_mait->countries);
+      free(ptl_mait);
+      ifofile->ptl_mait = 0;
+      return 0;
+    }
   }
 
   for(i = 0; i < ptl_mait->nr_of_countries; i++) {
@@ -1074,25 +1096,221 @@
   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);
+    CHECK_VALUE(ptl_mait->countries[i].pf_ptl_mai_start_byte
+		+ 8*2 * (ptl_mait->nr_of_vtss + 1) <= ptl_mait->last_byte + 1);
   }
 
+  for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+    uint16_t *pf_temp;
+    
+    if(!DVDFileSeek_(ifofile->file, 
+		     ifofile->vmgi_mat->ptl_mait * DVD_BLOCK_LEN
+                     + ptl_mait->countries[i].pf_ptl_mai_start_byte)) {
+      fprintf(stderr, "libdvdread: Unable to seak PTL_MAIT table.\n");
+      free(ptl_mait->countries);
+      free(ptl_mait);
+      return 0;
+    }
+    info_length = (ptl_mait->nr_of_vtss + 1) * sizeof(pf_level_t);
+    pf_temp = (uint16_t *)malloc(info_length);
+    if(!pf_temp) {
+      for(j = 0; j < i ; j++) {
+         free(ptl_mait->countries[j].pf_ptl_mai);
+      }
+      free(ptl_mait->countries);
+      free(ptl_mait);
+      return 0;
+    }
+    if(!(DVDReadBytes(ifofile->file, pf_temp, info_length))) {
+       fprintf(stderr, "libdvdread: Unable to read PTL_MAIT table.\n");
+       free(pf_temp);
+       for(j = 0; j < i ; j++) {
+	  free(ptl_mait->countries[j].pf_ptl_mai);
+       }
+       free(ptl_mait->countries);
+       free(ptl_mait);
+       return 0;
+    }
+    for (j = 0; j < ((ptl_mait->nr_of_vtss + 1) * 8); j++) {
+        B2N_16(pf_temp[j]);
+    }
+    ptl_mait->countries[i].pf_ptl_mai = (pf_level_t *)malloc(info_length);
+    if(!ptl_mait->countries[i].pf_ptl_mai) {
+      free(pf_temp);
+      for(j = 0; j < i ; j++) {
+	free(ptl_mait->countries[j].pf_ptl_mai);
+      }
+      free(ptl_mait->countries);
+      free(ptl_mait);
+      return 0;
+    }
+    { /* Transpose the array so we can use C indexing. */
+      int level, vts;
+      for(level = 0; level < 8; level++) {
+	for(vts = 0; vts <= ptl_mait->nr_of_vtss; vts++) {
+	  ptl_mait->countries[i].pf_ptl_mai[vts][level] =
+	    pf_temp[(7-level)*(ptl_mait->nr_of_vtss+1) + vts];
+	}
+      }
+      free(pf_temp);
+    }
+  }
   return 1;
 }
 
-
 void ifoFree_PTL_MAIT(ifo_handle_t *ifofile) {
+  unsigned int i;
+  
   if(!ifofile)
     return;
   
   if(ifofile->ptl_mait) {
+    for(i = 0; i < ifofile->ptl_mait->nr_of_countries; i++) {
+       free(ifofile->ptl_mait->countries[i].pf_ptl_mai);
+    }
     free(ifofile->ptl_mait->countries);
     free(ifofile->ptl_mait);
     ifofile->ptl_mait = 0;
   }
 }
 
+int ifoRead_VTS_TMAPT(ifo_handle_t *ifofile) {
+  vts_tmapt_t *vts_tmapt;
+  uint32_t *vts_tmap_srp;
+  unsigned int offset;
+  int info_length;
+  unsigned int i, j;
+  
+  if(!ifofile)
+    return 0;
+
+  if(!ifofile->vtsi_mat)
+    return 0;
+
+  if(ifofile->vtsi_mat->vts_tmapt == 0) { /* optional(?) */
+    ifofile->vts_tmapt = NULL;
+    fprintf(stderr,"Please send bug report - no VTS_TMAPT ?? \n");
+    return 1;
+  }
+  
+  offset = ifofile->vtsi_mat->vts_tmapt * DVD_BLOCK_LEN;
+  
+  if(!DVDFileSeek_(ifofile->file, offset)) 
+    return 0;
+  
+  vts_tmapt = (vts_tmapt_t *)malloc(sizeof(vts_tmapt_t));
+  if(!vts_tmapt)
+    return 0;
+  
+  ifofile->vts_tmapt = vts_tmapt;
+  
+  if(!(DVDReadBytes(ifofile->file, vts_tmapt, VTS_TMAPT_SIZE))) {
+    fprintf(stderr, "libdvdread: Unable to read VTS_TMAPT.\n");
+    free(vts_tmapt);
+    ifofile->vts_tmapt = NULL;
+    return 0;
+  }
+
+  B2N_16(vts_tmapt->nr_of_tmaps);
+  B2N_32(vts_tmapt->last_byte);
+  
+  CHECK_ZERO(vts_tmapt->zero_1);
+  
+  info_length = vts_tmapt->nr_of_tmaps * 4;
+  
+  vts_tmap_srp = (uint32_t *)malloc(info_length);
+  if(!vts_tmap_srp) {
+    free(vts_tmapt);
+    ifofile->vts_tmapt = NULL;
+    return 0;
+  }
+
+  vts_tmapt->tmap_offset = vts_tmap_srp;
+  
+  if(!(DVDReadBytes(ifofile->file, vts_tmap_srp, info_length))) {
+    fprintf(stderr, "libdvdread: Unable to read VTS_TMAPT.\n");
+    free(vts_tmap_srp);
+    free(vts_tmapt);
+    ifofile->vts_tmapt = NULL;
+    return 0;
+  }
+
+  for (i = 0; i < vts_tmapt->nr_of_tmaps; i++) {
+     B2N_32(vts_tmap_srp[i]); 
+  }
+
+  
+  info_length = vts_tmapt->nr_of_tmaps * sizeof(vts_tmap_t);
+  
+  vts_tmapt->tmap = (vts_tmap_t *)malloc(info_length);
+  if(!vts_tmapt->tmap) {
+    free(vts_tmap_srp);
+    free(vts_tmapt);
+    ifofile->vts_tmapt = NULL;
+    return 0;
+  }
+
+  memset(vts_tmapt->tmap, 0, info_length); /* So ifoFree_VTS_TMAPT works. */
+  
+  for(i = 0; i < vts_tmapt->nr_of_tmaps; i++) {
+    if(!DVDFileSeek_(ifofile->file, offset + vts_tmap_srp[i])) {
+      ifoFree_VTS_TMAPT(ifofile);
+      return 0;
+    }
+
+    if(!(DVDReadBytes(ifofile->file, &vts_tmapt->tmap[i], VTS_TMAP_SIZE))) {
+      fprintf(stderr, "libdvdread: Unable to read VTS_TMAP.\n");
+      ifoFree_VTS_TMAPT(ifofile);
+      return 0;
+    }
+    
+    B2N_16(vts_tmapt->tmap[i].nr_of_entries);
+    CHECK_ZERO(vts_tmapt->tmap[i].zero_1);
+    
+    if(vts_tmapt->tmap[i].nr_of_entries == 0) { /* Early out if zero entries */
+      vts_tmapt->tmap[i].map_ent = NULL;
+      continue;
+    }
+    
+    info_length = vts_tmapt->tmap[i].nr_of_entries * sizeof(map_ent_t);
+    
+    vts_tmapt->tmap[i].map_ent = (map_ent_t *)malloc(info_length);
+    if(!vts_tmapt->tmap[i].map_ent) {
+      ifoFree_VTS_TMAPT(ifofile);
+      return 0;
+    }
+
+    if(!(DVDReadBytes(ifofile->file, vts_tmapt->tmap[i].map_ent, info_length))) {
+      fprintf(stderr, "libdvdread: Unable to read VTS_TMAP_ENT.\n");
+      ifoFree_VTS_TMAPT(ifofile);
+      return 0;
+    }
+    
+    for(j = 0; j < vts_tmapt->tmap[i].nr_of_entries; j++)
+      B2N_32(vts_tmapt->tmap[i].map_ent[j]);
+  }    
+  
+  return 1;
+}
+
+void ifoFree_VTS_TMAPT(ifo_handle_t *ifofile) {
+  unsigned int i;
+  
+  if(!ifofile)
+    return;
+  
+  if(ifofile->vts_tmapt) {  
+    for(i = 0; i < ifofile->vts_tmapt->nr_of_tmaps; i++)
+      if(ifofile->vts_tmapt->tmap[i].map_ent)
+	free(ifofile->vts_tmapt->tmap[i].map_ent);
+    free(ifofile->vts_tmapt->tmap);
+    free(ifofile->vts_tmapt->tmap_offset);
+    free(ifofile->vts_tmapt);
+    ifofile->vts_tmapt = NULL;
+  }
+}
+
+
 int ifoRead_TITLE_C_ADT(ifo_handle_t *ifofile) {
 
   if(!ifofile)
@@ -1168,7 +1386,7 @@
   /* 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);
+  CHECK_VALUE(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
@@ -1194,10 +1412,10 @@
     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 < 
+    CHECK_VALUE(c_adt->cell_adr_table[i].vob_id > 0);
+    CHECK_VALUE(c_adt->cell_adr_table[i].vob_id <= c_adt->nr_of_vobs);
+    CHECK_VALUE(c_adt->cell_adr_table[i].cell_id > 0);
+    CHECK_VALUE(c_adt->cell_adr_table[i].start_sector < 
 	   c_adt->cell_adr_table[i].last_sector);
   }
 
@@ -1301,7 +1519,7 @@
   /* 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);
+  CHECK_VALUE(info_length % sizeof(uint32_t) == 0);
   
   vobu_admap->vobu_start_sectors = (uint32_t *)malloc(info_length); 
   if(!vobu_admap->vobu_start_sectors) {
@@ -1387,7 +1605,7 @@
   /* 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
+  CHECK_VALUE(pgcit->nr_of_pgci_srp < 10000); // ?? seen max of 1338
   
   info_length = pgcit->nr_of_pgci_srp * PGCI_SRP_SIZE;
   data = malloc(info_length);
@@ -1410,12 +1628,12 @@
     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);
+    CHECK_VALUE(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);
+    CHECK_VALUE(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));
@@ -1507,9 +1725,9 @@
   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);
+  CHECK_VALUE(pgci_ut->nr_of_lus != 0);
+  CHECK_VALUE(pgci_ut->nr_of_lus < 100); // ?? 3-4 ?
+  CHECK_VALUE((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);
@@ -1542,7 +1760,6 @@
   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
@@ -1552,7 +1769,7 @@
        d == 0x86 "Angle"
        e == 0x87 "PTT"
     */
-    assert((pgci_ut->lu[i].exists & 0x07) == 0);
+    CHECK_VALUE((pgci_ut->lu[i].exists & 0x07) == 0);
   }
 
   for(i = 0; i < pgci_ut->nr_of_lus; i++) {
@@ -1634,21 +1851,21 @@
   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);
+  CHECK_VALUE(vts_attributes->nr_of_vtsm_audio_streams <= 1);
+  CHECK_VALUE(vts_attributes->nr_of_vtsm_subp_streams <= 1);
+  CHECK_VALUE(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);
+  CHECK_VALUE(vts_attributes->nr_of_vtstt_subp_streams <= 32);
   {
     unsigned int nr_coded;
-    assert(vts_attributes->last_byte + 1 >= VTS_ATTRIBUTES_MIN_SIZE);  
+    CHECK_VALUE(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);
+    CHECK_VALUE(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]);
   }
@@ -1692,9 +1909,9 @@
   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) + 
+  CHECK_VALUE(vts_atrt->nr_of_vtss != 0);
+  CHECK_VALUE(vts_atrt->nr_of_vtss < 100); //??
+  CHECK_VALUE((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);
@@ -1704,6 +1921,9 @@
     ifofile->vts_atrt = 0;
     return 0;
   }
+
+  vts_atrt->vts_atrt_offsets = data;   
+
   if(!(DVDReadBytes(ifofile->file, data, info_length))) {
     free(data);
     free(vts_atrt);
@@ -1713,7 +1933,7 @@
   
   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);
+    CHECK_VALUE(data[i] + VTS_ATTRIBUTES_MIN_SIZE < vts_atrt->last_byte + 1);
   }
   
   info_length = vts_atrt->nr_of_vtss * sizeof(vts_attributes_t);
@@ -1735,10 +1955,9 @@
     }
 
     // This assert cant be in ifoRead_VTS_ATTRIBUTES
-    assert(offset + vts_atrt->vts[i].last_byte <= vts_atrt->last_byte + 1);
+    CHECK_VALUE(offset + vts_atrt->vts[i].last_byte <= vts_atrt->last_byte + 1);
     // Is this check correct?
   }
-  free(data);
 
   return 1;
 }
@@ -1750,6 +1969,7 @@
   
   if(ifofile->vts_atrt) {
     free(ifofile->vts_atrt->vts);
+    free(ifofile->vts_atrt->vts_atrt_offsets);
     free(ifofile->vts_atrt);
     ifofile->vts_atrt = 0;
   }
--- a/libmpdvdkit2/ifo_read.h	Thu Jun 30 22:41:40 2005 +0000
+++ b/libmpdvdkit2/ifo_read.h	Thu Jun 30 22:48:26 2005 +0000
@@ -114,8 +114,7 @@
  *
  * 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.**
+ * manager information file (VMGI).  This structure is optional.
  */
 int ifoRead_FP_PGC(ifo_handle_t *);
 
@@ -141,7 +140,17 @@
  * VTSI files, this fills the ifofile->vtsm_pgci_ut structure.
  */
 int ifoRead_PGCI_UT(ifo_handle_t *);
-
+  
+/**
+ * okay = ifoRead_VTS_TMAPT(ifofile);
+ *
+ * Reads in the VTS Time Map Table, this data is only located in the video
+ * title set information file.  This fills the ifofile->vts_tmapt structure
+ * and all its substructures.  When pressent enables VOBU level time-based
+ * seeking for One_Sequential_PGC_Titles.
+ */
+int ifoRead_VTS_TMAPT(ifo_handle_t *);
+  
 /**
  * okay = ifoRead_C_ADT(ifofile);
  *
@@ -209,6 +218,7 @@
 void ifoFree_FP_PGC(ifo_handle_t *);
 void ifoFree_PGCIT(ifo_handle_t *);
 void ifoFree_PGCI_UT(ifo_handle_t *);
+void ifoFree_VTS_TMAPT(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 *);
--- a/libmpdvdkit2/ifo_types.h	Thu Jun 30 22:41:40 2005 +0000
+++ b/libmpdvdkit2/ifo_types.h	Thu Jun 30 22:48:26 2005 +0000
@@ -63,7 +63,7 @@
   uint8_t hour;
   uint8_t minute;
   uint8_t second;
-  uint8_t frame_u; // The two high bits are the frame rate.
+  uint8_t frame_u; /* The two high bits are the frame rate. */
 } ATTRIBUTE_PACKED dvd_time_t;
 
 /**
@@ -87,7 +87,8 @@
   
   unsigned int line21_cc_1          : 1;
   unsigned int line21_cc_2          : 1;
-  unsigned int unknown1             : 2;
+  unsigned int unknown1             : 1;
+  unsigned int bit_rate             : 1;
   
   unsigned int picture_size         : 2;
   unsigned int letterboxed          : 1;
@@ -102,14 +103,15 @@
   unsigned int letterboxed          : 1;
   unsigned int picture_size         : 2;
   
-  unsigned int unknown1             : 2;
+  unsigned int bit_rate             : 1;
+  unsigned int unknown1             : 1;
   unsigned int line21_cc_2          : 1;
   unsigned int line21_cc_1          : 1;
 #endif
 } ATTRIBUTE_PACKED video_attr_t;
 
 /**
- * Audio Attributes. (Incomplete/Wrong?)
+ * Audio Attributes.
  */
 typedef struct {
 #ifdef WORDS_BIGENDIAN
@@ -134,13 +136,99 @@
   unsigned int quantization           : 2;
 #endif
   uint16_t lang_code;
-  uint8_t  lang_code2; // ??
   uint8_t  lang_extension;
-  uint16_t unknown2;
+  uint8_t  code_extension;
+  uint8_t unknown3;
+  union {
+    struct ATTRIBUTE_PACKED {
+#ifdef WORDS_BIGENDIAN
+      unsigned int unknown4           : 1;
+      unsigned int channel_assignment : 3;
+      unsigned int version            : 2;
+      unsigned int mc_intro           : 1; /* probably 0: true, 1:false */
+      unsigned int mode               : 1; /* Karaoke mode 0: solo 1: duet */
+#else
+      unsigned int mode               : 1;
+      unsigned int mc_intro           : 1;
+      unsigned int version            : 2;
+      unsigned int channel_assignment : 3;
+      unsigned int unknown4           : 1;
+#endif
+    } karaoke;
+    struct ATTRIBUTE_PACKED {
+#ifdef WORDS_BIGENDIAN
+      unsigned int unknown5           : 4;
+      unsigned int dolby_encoded      : 1; /* suitable for surround decoding */
+      unsigned int unknown6           : 3;
+#else
+      unsigned int unknown6           : 3;
+      unsigned int dolby_encoded      : 1;
+      unsigned int unknown5           : 4;
+#endif
+    } surround;
+  } app_info;
 } ATTRIBUTE_PACKED audio_attr_t;
 
+
 /**
- * Subpicture Attributes.(Incomplete/Wrong)
+ * MultiChannel Extension
+ */
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+  unsigned int zero1      : 7;
+  unsigned int ach0_gme   : 1;
+
+  unsigned int zero2      : 7;
+  unsigned int ach1_gme   : 1;
+
+  unsigned int zero3      : 4;
+  unsigned int ach2_gv1e  : 1;
+  unsigned int ach2_gv2e  : 1;
+  unsigned int ach2_gm1e  : 1;
+  unsigned int ach2_gm2e  : 1;
+
+  unsigned int zero4      : 4;
+  unsigned int ach3_gv1e  : 1;
+  unsigned int ach3_gv2e  : 1;
+  unsigned int ach3_gmAe  : 1;
+  unsigned int ach3_se2e  : 1;
+
+  unsigned int zero5      : 4;
+  unsigned int ach4_gv1e  : 1;
+  unsigned int ach4_gv2e  : 1;
+  unsigned int ach4_gmBe  : 1;
+  unsigned int ach4_seBe  : 1;
+#else
+  unsigned int ach0_gme   : 1;
+  unsigned int zero1      : 7;
+
+  unsigned int ach1_gme   : 1;
+  unsigned int zero2      : 7;
+
+  unsigned int ach2_gm2e  : 1;
+  unsigned int ach2_gm1e  : 1;
+  unsigned int ach2_gv2e  : 1;
+  unsigned int ach2_gv1e  : 1;
+  unsigned int zero3      : 4;
+
+  unsigned int ach3_se2e  : 1;
+  unsigned int ach3_gmAe  : 1;
+  unsigned int ach3_gv2e  : 1;
+  unsigned int ach3_gv1e  : 1;
+  unsigned int zero4      : 4;
+
+  unsigned int ach4_seBe  : 1;
+  unsigned int ach4_gmBe  : 1;
+  unsigned int ach4_gv2e  : 1;
+  unsigned int ach4_gv1e  : 1;
+  unsigned int zero5      : 4;
+#endif
+  uint8_t zero6[19];
+} ATTRIBUTE_PACKED multichannel_ext_t;
+
+
+/**
+ * Subpicture Attributes.
  */
 typedef struct {
   /*
@@ -153,11 +241,19 @@
    * language: indicates language if type == 1
    * lang extension: if type == 1 contains the lang extension
    */
-  uint8_t type;
-  uint8_t zero1;
+#ifdef WORDS_BIGENDIAN
+  unsigned int code_mode : 3;
+  unsigned int zero1     : 3;
+  unsigned int type      : 2;
+#else
+  unsigned int type      : 2;
+  unsigned int zero1     : 3;
+  unsigned int code_mode : 3;
+#endif
+  uint8_t  zero2;
   uint16_t lang_code;
-  uint8_t lang_extension;
-  uint8_t zero2;
+  uint8_t  lang_extension;
+  uint8_t  code_extension;
 } ATTRIBUTE_PACKED subp_attr_t;
 
 
@@ -193,8 +289,8 @@
   unsigned int stc_discontinuity: 1;
   unsigned int seamless_angle   : 1;
   
-  unsigned int unknown1         : 1;
-  unsigned int restricted       : 1;
+  unsigned int playback_mode    : 1;  /**< When set, enter StillMode after each VOBU */
+  unsigned int restricted       : 1;  /**< ?? drop out of fastforward? */
   unsigned int unknown2         : 6;
 #else
   unsigned int seamless_angle   : 1;
@@ -206,7 +302,7 @@
   
   unsigned int unknown2         : 6;
   unsigned int restricted       : 1;
-  unsigned int unknown1         : 1;
+  unsigned int playback_mode    : 1;
 #endif
   uint8_t still_time;
   uint8_t cell_cmd_nr;
@@ -239,65 +335,65 @@
  */
 typedef struct {
 #ifdef WORDS_BIGENDIAN
-  unsigned int zero                           : 7; // 25-31
-  unsigned int video_pres_mode_change         : 1; // 24
+  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 karaoke_audio_pres_mode_change : 1; /* 23 */
+  unsigned int angle_change                   : 1;
+  unsigned int subpic_stream_change           : 1;
+  unsigned int audio_stream_change            : 1;
+  unsigned int pause_on                       : 1;
+  unsigned int still_off                      : 1;
+  unsigned int button_select_or_activate      : 1;
+  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 chapter_menu_call              : 1; /* 15 */
+  unsigned int angle_menu_call                : 1;
+  unsigned int audio_menu_call                : 1;
+  unsigned int subpic_menu_call               : 1;
+  unsigned int root_menu_call                 : 1;
+  unsigned int title_menu_call                : 1;
+  unsigned int backward_scan                  : 1;
+  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
+  unsigned int next_pg_search                 : 1; /* 7 */
+  unsigned int prev_or_top_pg_search          : 1;
+  unsigned int time_or_chapter_search         : 1;
+  unsigned int go_up                          : 1;
+  unsigned int stop                           : 1;
+  unsigned int title_play                     : 1;
+  unsigned int chapter_search_or_play         : 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 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 resume                         : 1; /* 16 */
+  unsigned int button_select_or_activate      : 1;
+  unsigned int still_off                      : 1;
+  unsigned int pause_on                       : 1;
+  unsigned int audio_stream_change            : 1;
+  unsigned int subpic_stream_change           : 1;
+  unsigned int angle_change                   : 1;
+  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 forward_scan                   : 1; /* 8 */
+  unsigned int backward_scan                  : 1;
+  unsigned int title_menu_call                : 1;
+  unsigned int root_menu_call                 : 1;
+  unsigned int subpic_menu_call               : 1;
+  unsigned int audio_menu_call                : 1;
+  unsigned int angle_menu_call                : 1;
+  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
+  unsigned int title_or_time_play             : 1; /* 0 */
+  unsigned int chapter_search_or_play         : 1;
+  unsigned int title_play                     : 1;
+  unsigned int stop                           : 1;
+  unsigned int go_up                          : 1;
+  unsigned int time_or_chapter_search         : 1;
+  unsigned int prev_or_top_pg_search          : 1;
+  unsigned int next_pg_search                 : 1; /* 7 */
 #endif
 } ATTRIBUTE_PACKED user_ops_t;
 
@@ -365,7 +461,7 @@
  */
 typedef struct {
   uint16_t lang_code;
-  uint8_t  zero_1;
+  uint8_t  lang_extension;
   uint8_t  exists;
   uint32_t lang_start_byte;
   pgcit_t *pgcit;
@@ -401,7 +497,7 @@
   uint16_t nr_of_vobs; /* VOBs */
   uint16_t zero_1;
   uint32_t last_byte;
-  cell_adr_t *cell_adr_table;
+  cell_adr_t *cell_adr_table;  /* No explicit size given. */
 } ATTRIBUTE_PACKED c_adt_t;
 #define C_ADT_SIZE 8
 
@@ -457,11 +553,11 @@
   
   video_attr_t vmgm_video_attr;
   uint8_t  zero_7;
-  uint8_t  nr_of_vmgm_audio_streams; // should be 0 or 1
+  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
+  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;
@@ -469,21 +565,21 @@
 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 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
+  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 title_or_time_play        : 1;
+  unsigned int chapter_search_or_play    : 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 multi_or_random_pgc_title : 1;
   unsigned int zero_1                    : 1;
 #endif
 } ATTRIBUTE_PACKED playback_type_t;
@@ -512,6 +608,13 @@
 } ATTRIBUTE_PACKED tt_srpt_t;
 #define TT_SRPT_SIZE 8
 
+
+/**
+ * Parental Management Information Unit Table.
+ * Level 1 (US: G), ..., 7 (US: NC-17), 8
+ */
+typedef uint16_t pf_level_t[8];
+
 /**
  * Parental Management Information Unit Table.
  */
@@ -520,7 +623,7 @@
   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 */
+  pf_level_t *pf_ptl_mai; /* table of (nr_of_vtss + 1), video_ts is first */
 } ATTRIBUTE_PACKED ptl_mait_country_t;
 #define PTL_MAIT_COUNTRY_SIZE 8
 
@@ -544,12 +647,12 @@
   
   video_attr_t vtsm_vobs_attr;
   uint8_t  zero_1;
-  uint8_t  nr_of_vtsm_audio_streams; // should be 0 or 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
+  uint8_t  nr_of_vtsm_subp_streams; /* should be 0 or 1 */
   subp_attr_t vtsm_subp_attr;
   subp_attr_t zero_5[27];
   
@@ -575,6 +678,7 @@
   uint16_t zero_1;
   uint32_t last_byte;
   vts_attributes_t *vts;
+  uint32_t *vts_atrt_offsets; /* offsets table for each vts_attributes */
 } ATTRIBUTE_PACKED vts_atrt_t;
 #define VTS_ATRT_SIZE 8
 
@@ -585,18 +689,18 @@
   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 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 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 unknown4; /* ?? allways 0x30 language?, text format? */
   uint8_t unknown5;
-  uint16_t offset; // from first 
+  uint16_t offset; /* from first */
   
-  char text[12]; // ended by 0x09
+  char text[12]; /* ended by 0x09 */
 #endif
 } ATTRIBUTE_PACKED txtdt_t;
 
@@ -656,7 +760,7 @@
   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 vts_tmapt;       /* sector */
   uint32_t vtsm_c_adt;      /* sector */
   uint32_t vtsm_vobu_admap; /* sector */
   uint32_t vts_c_adt;       /* sector */
@@ -665,11 +769,11 @@
   
   video_attr_t vtsm_video_attr;
   uint8_t  zero_14;
-  uint8_t  nr_of_vtsm_audio_streams; // should be 0 or 1
+  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
+  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];
@@ -681,6 +785,8 @@
   uint8_t  zero_20[17];
   uint8_t  nr_of_vts_subp_streams;
   subp_attr_t vts_subp_attr[32];
+  uint16_t zero_21;
+  multichannel_ext_t vts_mu_audio_attr[8];
   /* XXX: how much 'padding' here, if any? */
 } ATTRIBUTE_PACKED vtsi_mat_t;
 
@@ -708,10 +814,41 @@
   uint16_t zero_1;
   uint32_t last_byte;
   ttu_t  *title;
+  uint32_t *ttu_offset; /* offset table for each ttu */
 } ATTRIBUTE_PACKED vts_ptt_srpt_t;
 #define VTS_PTT_SRPT_SIZE 8
 
 
+/**
+ * Time Map Entry.
+ */
+/* Should this be bit field at all or just the uint32_t? */
+typedef uint32_t map_ent_t;
+
+/**
+ * Time Map.
+ */
+typedef struct {
+  uint8_t  tmu;   /* Time unit, in seconds */
+  uint8_t  zero_1;
+  uint16_t nr_of_entries;
+  map_ent_t *map_ent;
+} ATTRIBUTE_PACKED vts_tmap_t;
+#define VTS_TMAP_SIZE 4
+
+/**
+ * Time Map Table.
+ */
+typedef struct {
+  uint16_t nr_of_tmaps;
+  uint16_t zero_1;
+  uint32_t last_byte;
+  vts_tmap_t *tmap;
+  uint32_t *tmap_offset; /* offset table for each tmap */
+} ATTRIBUTE_PACKED vts_tmapt_t;
+#define VTS_TMAPT_SIZE 8
+
+
 #if PRAGMA_PACK
 #pragma pack()
 #endif
@@ -743,7 +880,7 @@
   vtsi_mat_t     *vtsi_mat;
   vts_ptt_srpt_t *vts_ptt_srpt;
   pgcit_t        *vts_pgcit;
-  int            *vts_tmapt; // FIXME add/correct the type
+  vts_tmapt_t    *vts_tmapt;
   c_adt_t        *vts_c_adt;
   vobu_admap_t   *vts_vobu_admap;
 } ifo_handle_t;
--- a/libmpdvdkit2/nav_print.c	Thu Jun 30 22:41:40 2005 +0000
+++ b/libmpdvdkit2/nav_print.c	Thu Jun 30 22:48:26 2005 +0000
@@ -1,9 +1,5 @@
 /*
- * Copyright (C) 2000, 2001, 2002 Håkan Hjort <d95hjort@dtek.chalmers.se>
- *
- * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
- * detailed CVS changelog at http://www.mplayerhq.hu/cgi-bin/cvsweb.cgi/main/
- * $Id$
+ * Copyright (C) 2000, 2001, 2002, 2003 Håkan Hjort <d95hjort@dtek.chalmers.se>
  *
  * Much of the contents in this file is based on VOBDUMP.
  *
@@ -27,21 +23,21 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#include "config.h"
+
 #include <stdio.h>
 #include <inttypes.h>
-//#include <assert.h>
 
-#include "config.h" // Needed for WORDS_BIGENDIAN
 #include "nav_types.h"
 #include "nav_print.h"
-
+#include "dvdread_internal.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);
+  CHECK_VALUE((dtime->hour>>4) < 0xa && (dtime->hour&0xf) < 0xa);
+  CHECK_VALUE((dtime->minute>>4) < 0x7 && (dtime->minute&0xf) < 0xa);
+  CHECK_VALUE((dtime->second>>4) < 0x7 && (dtime->second&0xf) < 0xa);
+  CHECK_VALUE((dtime->frame_u&0xf) < 0xa);
   
   printf("%02x:%02x:%02x.%02x", 
 	 dtime->hour,
--- a/libmpdvdkit2/nav_print.h	Thu Jun 30 22:41:40 2005 +0000
+++ b/libmpdvdkit2/nav_print.h	Thu Jun 30 22:48:26 2005 +0000
@@ -2,8 +2,8 @@
 #define NAV_PRINT_H_INCLUDED
 
 /*
- * Copyright (C) 2001 Billy Biggs <vektor@dumbterm.net>,
- *                    Håkan Hjort <d95hjort@dtek.chalmers.se>
+ * Copyright (C) 2001, 2002 Billy Biggs <vektor@dumbterm.net>,
+ *                          Håkan Hjort <d95hjort@dtek.chalmers.se>
  *
  * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
  * detailed CVS changelog at http://www.mplayerhq.hu/cgi-bin/cvsweb.cgi/main/
@@ -26,17 +26,25 @@
 
 #include "nav_types.h"
 
+/**
+ * Pretty printing of the NAV packets, PCI and DSI structs.
+ */
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /**
  * Prints information contained in the PCI to stdout.
+ *
+ * @param pci Pointer to the PCI data structure to be printed.
  */
 void navPrint_PCI(pci_t *);
   
 /**
  * Prints information contained in the DSI to stdout.
+ *
+ * @param dsi Pointer to the DSI data structure to be printed.
  */
 void navPrint_DSI(dsi_t *);
 
--- a/libmpdvdkit2/nav_read.c	Thu Jun 30 22:41:40 2005 +0000
+++ b/libmpdvdkit2/nav_read.c	Thu Jun 30 22:48:26 2005 +0000
@@ -1,9 +1,5 @@
 /*
- * Copyright (C) 2000, 2001, 2002 Håkan Hjort <d95hjort@dtek.chalmers.se>
- *
- * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
- * detailed CVS changelog at http://www.mplayerhq.hu/cgi-bin/cvsweb.cgi/main/
- * $Id$
+ * Copyright (C) 2000, 2001, 2002, 2003 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
@@ -20,20 +16,21 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#include "config.h"
+
 #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"
+#include "dvdread_internal.h"
 
 void navRead_PCI(pci_t *pci, unsigned char *buffer) {
-  int i, j, k;
+  int i, j;
 
-  assert(sizeof(pci_t) == PCI_BYTES - 1); // -1 for substream id
+  CHECK_VALUE(sizeof(pci_t) == PCI_BYTES - 1); // -1 for substream id
   
   memcpy(pci, buffer, sizeof(pci_t));
 
@@ -61,84 +58,99 @@
     for(j = 0; j < 2; j++)
       B2N_32(pci->hli.btn_colit.btn_coli[i][j]);
 
-#if !defined(WORDS_BIGENDIAN)
+  /* NOTE: I've had to change the structure from the disk layout to get
+   * the packing to work with Sun's Forte C compiler. */
+  
   /* 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);
+    char tmp[sizeof(pci->hli.btnit[i])], swap;
+    memcpy(tmp, &(pci->hli.btnit[i]), sizeof(pci->hli.btnit[i]));
+    /* Byte 4 to 7 are 'rotated' was: ABCD EFGH IJ is: ABCG DEFH IJ */
+    swap   = tmp[6]; 
+    tmp[6] = tmp[5];
+    tmp[5] = tmp[4];
+    tmp[4] = tmp[3];
+    tmp[3] = swap;
+    
+    /* Then there are the two B2N_24(..) calls */
+#ifndef WORDS_BIGENDIAN
+    swap = tmp[0];
+    tmp[0] = tmp[2];
+    tmp[2] = swap;
+    
+    swap = tmp[4];
+    tmp[4] = tmp[6];
+    tmp[6] = swap;
+#endif
+    memcpy(&(pci->hli.btnit[i]), tmp, sizeof(pci->hli.btnit[i]));
   }
-#endif
 
 
+#ifndef NDEBUG
   /* Asserts */
 
   /* pci pci gi */ 
-  assert(pci->pci_gi.zero1 == 0);
+  CHECK_VALUE(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);
+  CHECK_VALUE(pci->hli.hl_gi.zero1 == 0);
+  CHECK_VALUE(pci->hli.hl_gi.zero2 == 0);
+  CHECK_VALUE(pci->hli.hl_gi.zero3 == 0);
+  CHECK_VALUE(pci->hli.hl_gi.zero4 == 0);
+  CHECK_VALUE(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); 
+    CHECK_VALUE(pci->hli.hl_gi.btn_ns != 0); 
+    CHECK_VALUE(pci->hli.hl_gi.btngr_ns != 0); 
   } else {
-    assert((pci->hli.hl_gi.btn_ns != 0 && pci->hli.hl_gi.btngr_ns != 0) 
+    CHECK_VALUE((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++) {
-#ifdef HAVE_ASSERT_H
       int n = (36 / pci->hli.hl_gi.btngr_ns) * i + j;
-#endif
-      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);
+      CHECK_VALUE(pci->hli.btnit[n].zero1 == 0);
+      CHECK_VALUE(pci->hli.btnit[n].zero2 == 0);
+      CHECK_VALUE(pci->hli.btnit[n].zero3 == 0);
+      CHECK_VALUE(pci->hli.btnit[n].zero4 == 0);
+      CHECK_VALUE(pci->hli.btnit[n].zero5 == 0);
+      CHECK_VALUE(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);
+	CHECK_VALUE(pci->hli.btnit[n].x_start <= pci->hli.btnit[n].x_end);
+	CHECK_VALUE(pci->hli.btnit[n].y_start <= pci->hli.btnit[n].y_end);
+	CHECK_VALUE(pci->hli.btnit[n].up <= pci->hli.hl_gi.btn_ns);
+	CHECK_VALUE(pci->hli.btnit[n].down <= pci->hli.hl_gi.btn_ns);
+	CHECK_VALUE(pci->hli.btnit[n].left <= pci->hli.hl_gi.btn_ns);
+	CHECK_VALUE(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);
+	int k;
+	CHECK_VALUE(pci->hli.btnit[n].btn_coln == 0);
+	CHECK_VALUE(pci->hli.btnit[n].auto_action_mode == 0);
+	CHECK_VALUE(pci->hli.btnit[n].x_start == 0);
+	CHECK_VALUE(pci->hli.btnit[n].y_start == 0);
+	CHECK_VALUE(pci->hli.btnit[n].x_end == 0);
+	CHECK_VALUE(pci->hli.btnit[n].y_end == 0);
+	CHECK_VALUE(pci->hli.btnit[n].up == 0);
+	CHECK_VALUE(pci->hli.btnit[n].down == 0);
+	CHECK_VALUE(pci->hli.btnit[n].left == 0);
+	CHECK_VALUE(pci->hli.btnit[n].right == 0);
 	for (k = 0; k < 8; k++)
-	  assert(pci->hli.btnit[n].cmd.bytes[k] == 0); //CHECK_ZERO?
+	  CHECK_VALUE(pci->hli.btnit[n].cmd.bytes[k] == 0); //CHECK_ZERO?
       }
     }
   }
+#endif /* !NDEBUG */
 }
 
 void navRead_DSI(dsi_t *dsi, unsigned char *buffer) {
   int i;
 
-  assert(sizeof(dsi_t) == DSI_BYTES - 1); // -1 for substream id
+  CHECK_VALUE(sizeof(dsi_t) == DSI_BYTES - 1); // -1 for substream id
   
   memcpy(dsi, buffer, sizeof(dsi_t));
 
@@ -187,6 +199,6 @@
   /* Asserts */
 
   /* dsi dsi gi */
-  assert(dsi->dsi_gi.zero1 == 0);
+  CHECK_VALUE(dsi->dsi_gi.zero1 == 0);
 }
 
--- a/libmpdvdkit2/nav_read.h	Thu Jun 30 22:41:40 2005 +0000
+++ b/libmpdvdkit2/nav_read.h	Thu Jun 30 22:48:26 2005 +0000
@@ -2,7 +2,7 @@
 #define NAV_READ_H_INCLUDED
 
 /*
- * Copyright (C) 2000, 2001 Håkan Hjort <d95hjort@dtek.chalmers.se>.
+ * Copyright (C) 2000, 2001, 2002 Håkan Hjort <d95hjort@dtek.chalmers.se>.
  *
  * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
  * detailed CVS changelog at http://www.mplayerhq.hu/cgi-bin/cvsweb.cgi/main/
@@ -25,17 +25,27 @@
 
 #include "nav_types.h"
 
+/**
+ * Parsing of NAV data, PCI and DSI parts.
+ */
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /**
- * Reads the PCI packet data pointed to into pci struct.
- */
+ * Reads the PCI packet data pointed to into th pci struct.
+ * 
+ * @param pci Pointer to the PCI data structure to be filled in.
+ * @param bufffer Pointer to the buffer of the on disc PCI data.
+ */  
 void navRead_PCI(pci_t *, unsigned char *);
 
 /**
  * Reads the DSI packet data pointed to into dsi struct.
+ * 
+ * @param dsi Pointer to the DSI data structure to be filled in.
+ * @param bufffer Pointer to the buffer of the on disc DSI data.
  */
 void navRead_DSI(dsi_t *, unsigned char *);
 
--- a/libmpdvdkit2/nav_types.h	Thu Jun 30 22:41:40 2005 +0000
+++ b/libmpdvdkit2/nav_types.h	Thu Jun 30 22:48:26 2005 +0000
@@ -2,7 +2,7 @@
 #define NAV_TYPES_H_INCLUDED
 
 /*
- * Copyright (C) 2000, 2001 Håkan Hjort <d95hjort@dtek.chalmers.se>
+ * Copyright (C) 2000, 2001, 2002 Håkan Hjort <d95hjort@dtek.chalmers.se>
  *
  * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
  * detailed CVS changelog at http://www.mplayerhq.hu/cgi-bin/cvsweb.cgi/main/
@@ -34,7 +34,7 @@
  */
 
 #include <inttypes.h>
-#include "ifo_types.h" // only dvd_time_t, vm_cmd_t and user_ops_t
+#include "ifo_types.h" /* only dvd_time_t, vm_cmd_t and user_ops_t */
 
 
 #undef ATTRIBUTE_PACKED
@@ -74,14 +74,14 @@
  * 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;
+  uint32_t nv_pck_lbn;      /**< sector address of this nav pack */
+  uint16_t vobu_cat;        /**< 'category' of vobu */
+  uint16_t zero1;           /**< reserved */
+  user_ops_t vobu_uop_ctl;  /**< UOP of vobu */
+  uint32_t vobu_s_ptm;      /**< start presentation time of vobu */
+  uint32_t vobu_e_ptm;      /**< end presentation time of vobu */
+  uint32_t vobu_se_e_ptm;   /**< end ptm of sequence end in vobu */
+  dvd_time_t e_eltm;        /**< Cell elapsed time */
   char vobu_isrc[32];
 } ATTRIBUTE_PACKED pci_gi_t;
 
@@ -89,26 +89,32 @@
  * Non Seamless Angle Information
  */
 typedef struct {
-  uint32_t nsml_agl_dsta[9]; 
+  uint32_t nsml_agl_dsta[9];  /**< address of destination vobu in AGL_C#n */
 } ATTRIBUTE_PACKED nsml_agli_t;
 
 /** 
  * Highlight General Information 
+ *
+ * For btngrX_dsp_ty the bits have the following meaning:
+ * 000b: normal 4/3 only buttons
+ * XX1b: wide (16/9) buttons
+ * X1Xb: letterbox buttons
+ * 1XXb: pan&scan buttons
  */
 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;
+  uint16_t hli_ss; /**< status, only low 2 bits 0: no buttons, 1: different 2: equal 3: eual except for button cmds */
+  uint32_t hli_s_ptm;              /**< start ptm of hli */
+  uint32_t hli_e_ptm;              /**< end ptm of hli */
+  uint32_t btn_se_e_ptm;           /**< end ptm of button select */
 #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;
+  unsigned int zero1 : 2;          /**< reserved */
+  unsigned int btngr_ns : 2;       /**< number of button groups 1, 2 or 3 with 36/18/12 buttons */
+  unsigned int zero2 : 1;          /**< reserved */
+  unsigned int btngr1_dsp_ty : 3;  /**< display type of subpic stream for button group 1 */
+  unsigned int zero3 : 1;          /**< reserved */
+  unsigned int btngr2_dsp_ty : 3;  /**< display type of subpic stream for button group 2 */
+  unsigned int zero4 : 1;          /**< reserved */
+  unsigned int btngr3_dsp_ty : 3;  /**< display type of subpic stream for button group 3 */
 #else
   unsigned int btngr1_dsp_ty : 3;
   unsigned int zero2 : 1;
@@ -119,56 +125,69 @@
   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
+  uint8_t btn_ofn;     /**< button offset number range 0-255 */
+  uint8_t btn_ns;      /**< number of valid buttons  <= 36/18/12 (low 6 bits) */  
+  uint8_t nsl_btn_ns;  /**< number of buttons selectable by U_BTNNi (low 6 bits)   nsl_btn_ns <= btn_ns */
+  uint8_t zero5;       /**< reserved */
+  uint8_t fosl_btnn;   /**< forcedly selected button  (low 6 bits) */
+  uint8_t foac_btnn;   /**< forcedly activated button (low 6 bits) */
 } ATTRIBUTE_PACKED hl_gi_t;
 
 
 /** 
  * Button Color Information Table 
+ * Each entry beeing a 32bit word that contains the color indexs and alpha
+ * values to use.  They are all represented by 4 bit number and stored
+ * like this [Ci3, Ci2, Ci1, Ci0, A3, A2, A1, A0].   The actual palette
+ * that the indexes reference is in the PGC.
+ * @TODO split the uint32_t into a struct
  */
 typedef struct {
-  uint32_t btn_coli[3][2];
+  uint32_t btn_coli[3][2];  /**< [button color number-1][select:0/action:1] */
 } ATTRIBUTE_PACKED btn_colit_t;
 
 /** 
  * Button Information
+ *
+ * NOTE: I've had to change the structure from the disk layout to get
+ * the packing to work with Sun's Forte C compiler.
+ * The 4 and 7 bytes are 'rotated' was: ABC DEF GHIJ  is: ABCG DEFH IJ
  */
 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 btn_coln         : 2;  /**< button color number */
+  unsigned int x_start          : 10; /**< x start offset within the overlay */
+  unsigned int zero1            : 2;  /**< reserved */
+  unsigned int x_end            : 10; /**< x end offset within the overlay */
+
+  unsigned int zero3            : 2;  /**< reserved */
+  unsigned int up               : 6;  /**< button index when pressing up */
 
-  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;
+  unsigned int auto_action_mode : 2;  /**< 0: no, 1: activated if selected */
+  unsigned int y_start          : 10; /**< y start offset within the overlay */
+  unsigned int zero2            : 2;  /**< reserved */
+  unsigned int y_end            : 10; /**< y end offset within the overlay */
+
+  unsigned int zero4            : 2;  /**< reserved */
+  unsigned int down             : 6;  /**< button index when pressing down */
+  unsigned int zero5            : 2;  /**< reserved */
+  unsigned int left             : 6;  /**< button index when pressing left */
+  unsigned int zero6            : 2;  /**< reserved */
+  unsigned int right            : 6;  /**< button index when pressing right */
 #else
   unsigned int x_end            : 10;
   unsigned int zero1            : 2;
   unsigned int x_start          : 10;
   unsigned int btn_coln         : 2;
+
+  unsigned int up               : 6;
+  unsigned int zero3            : 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;
@@ -206,27 +225,27 @@
  */
 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;
+  uint32_t nv_pck_lbn;      /**< sector address of this nav pack */
+  uint32_t vobu_ea;         /**< end address of this VOBU */
+  uint32_t vobu_1stref_ea;  /**< end address of the 1st reference image */
+  uint32_t vobu_2ndref_ea;  /**< end address of the 2nd reference image */
+  uint32_t vobu_3rdref_ea;  /**< end address of the 3rd reference image */
+  uint16_t vobu_vob_idn;    /**< VOB Id number that this VOBU is part of */
+  uint8_t  zero1;           /**< reserved */
+  uint8_t  vobu_c_idn;      /**< Cell Id number that this VOBU is part of */
+  dvd_time_t c_eltm;        /**< Cell elapsed time */
 } 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
+  uint16_t category;       /**< 'category' of seamless VOBU */
+  uint32_t ilvu_ea;        /**< end address of interleaved Unit */
+  uint32_t ilvu_sa;        /**< start address of next interleaved unit */
+  uint16_t size;           /**< size of next interleaved unit */
+  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;
@@ -239,8 +258,8 @@
  * 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.
+    uint32_t address; /**< offset to next ILVU, high bit is before/after */
+    uint16_t size;    /**< byte size of the ILVU pointed to by address */
 } ATTRIBUTE_PACKED sml_agl_data_t;
 
 /**
@@ -254,11 +273,11 @@
  * VOBU Search Information 
  */
 typedef struct {
-  uint32_t next_video; ///< Next vobu that contains video
-  uint32_t fwda[19];   ///< Forwards, time
+  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 bwda[19];   /**< Backwards, time */
   uint32_t prev_video;
 } ATTRIBUTE_PACKED vobu_sri_t;
 
@@ -268,8 +287,8 @@
  * 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
+  uint16_t a_synca[8];   /**< offset to first audio packet for this VOBU */
+  uint32_t sp_synca[32]; /**< offset to first subpicture packet */
 } ATTRIBUTE_PACKED synci_t;
 
 /**