changeset 27466:ea01824701a5

Rename internal libdvdread fork from dvdread to libdvdread to avoid clashing with external libdvdread. (Sync with libdvdread r1122)
author rathann
date Sat, 30 Aug 2008 12:22:21 +0000
parents b3bfe83d77f9
children 239a049163eb
files Copyright Makefile configure dvdread/bswap.h dvdread/cmd_print.c dvdread/cmd_print.h dvdread/dvd_input.c dvdread/dvd_input.h dvdread/dvd_reader.c dvdread/dvd_reader.h dvdread/dvd_udf.c dvdread/dvd_udf.h dvdread/dvdread_internal.h dvdread/ifo_print.c dvdread/ifo_print.h dvdread/ifo_read.c dvdread/ifo_read.h dvdread/ifo_types.h dvdread/libdvdread_changes.diff dvdread/md5.c dvdread/md5.h dvdread/nav_print.c dvdread/nav_print.h dvdread/nav_read.c dvdread/nav_read.h dvdread/nav_types.h libdvdread/bswap.h libdvdread/cmd_print.c libdvdread/cmd_print.h libdvdread/dvd_input.c libdvdread/dvd_input.h libdvdread/dvd_reader.c libdvdread/dvd_reader.h libdvdread/dvd_udf.c libdvdread/dvd_udf.h libdvdread/dvdread_internal.h libdvdread/ifo_print.c libdvdread/ifo_print.h libdvdread/ifo_read.c libdvdread/ifo_read.h libdvdread/ifo_types.h libdvdread/libdvdread_changes.diff libdvdread/md5.c libdvdread/md5.h libdvdread/nav_print.c libdvdread/nav_print.h libdvdread/nav_read.c libdvdread/nav_read.h libdvdread/nav_types.h stream/stream_dvd.h stream/stream_dvd_common.c stream/stream_dvd_common.h
diffstat 52 files changed, 11012 insertions(+), 11012 deletions(-) [+]
line wrap: on
line diff
--- a/Copyright	Sat Aug 30 11:21:11 2008 +0000
+++ b/Copyright	Sat Aug 30 12:22:21 2008 +0000
@@ -51,7 +51,7 @@
 Name:       libdvdread
 Version:    0.9.7 + patches
 Homepage:   http://www.dtek.chalmers.se/groups/dvd/development.shtml
-Directory:  dvdread
+Directory:  libdvdread
 Copyright:  1998, 1999 Eric Smith <eric@brouhaha.com>
             1999 Christian Wolff for convergence integrated media
             2000-2001 Billy Biggs <vektor@dumbterm.net>,
--- a/Makefile	Sat Aug 30 11:21:11 2008 +0000
+++ b/Makefile	Sat Aug 30 12:22:21 2008 +0000
@@ -236,15 +236,15 @@
 SRCS_COMMON-$(DVDNAV)                += stream/stream_dvdnav.c
 SRCS_COMMON-$(DVDREAD)               += stream/stream_dvd.c \
                                         stream/stream_dvd_common.c
-SRCS_COMMON-$(DVDREAD_INTERNAL)      += dvdread/cmd_print.c \
-                                        dvdread/dvd_input.c \
-                                        dvdread/dvd_reader.c \
-                                        dvdread/dvd_udf.c \
-                                        dvdread/ifo_print.c \
-                                        dvdread/ifo_read.c \
-                                        dvdread/md5.c \
-                                        dvdread/nav_print.c \
-                                        dvdread/nav_read.c \
+SRCS_COMMON-$(DVDREAD_INTERNAL)      += libdvdread/cmd_print.c \
+                                        libdvdread/dvd_input.c \
+                                        libdvdread/dvd_reader.c \
+                                        libdvdread/dvd_udf.c \
+                                        libdvdread/ifo_print.c \
+                                        libdvdread/ifo_read.c \
+                                        libdvdread/md5.c \
+                                        libdvdread/nav_print.c \
+                                        libdvdread/nav_read.c \
 
 SRCS_COMMON-$(FAAD)                  += libmpcodecs/ad_faad.c
 SRCS_COMMON-$(FAAD_INTERNAL)         += libfaad2/bits.c \
@@ -655,7 +655,6 @@
 INSTALL_TARGETS-$(MPLAYER)  += install-mplayer  install-mplayer-man
 
 DIRS =  . \
-        dvdread \
         gui \
         gui/mplayer \
         gui/mplayer/gtk \
@@ -679,6 +678,7 @@
         libavformat \
         libavutil \
         libdvdcss \
+        libdvdread \
         libfaad2 \
         libmenu \
         libmpcodecs \
@@ -783,8 +783,8 @@
 mencoder.d mplayer.d vobsub.d gui/win32/gui.d libmpdemux/muxer_avi.d osdep/mplayer-rc.o stream/network.d stream/stream_cddb.d: version.h
 $(DEPS): help_mp.h
 
-dvdread/%.o dvdread/%.d: CFLAGS += -D__USE_UNIX98 -D_GNU_SOURCE -DHAVE_CONFIG_H $(CFLAGS_LIBDVDCSS_DVDREAD)
 libdvdcss/%.o libdvdcss/%.d: CFLAGS += -D__USE_UNIX98 -D_GNU_SOURCE -DVERSION=\"1.2.9\" $(CFLAGS_LIBDVDCSS)
+libdvdread/%.o libdvdread/%.d: CFLAGS += -D__USE_UNIX98 -D_GNU_SOURCE -DHAVE_CONFIG_H $(CFLAGS_LIBDVDCSS_DVDREAD)
 libfaad2/%.o libfaad2/%.d: CFLAGS += -Ilibfaad2 -D_GNU_SOURCE -DHAVE_CONFIG_H $(CFLAGS_FAAD_FIXED)
 
 loader/% loader/%: CFLAGS += -Iloader -fno-omit-frame-pointer $(CFLAGS_NO_OMIT_LEAF_FRAME_POINTER)
--- a/configure	Sat Aug 30 11:21:11 2008 +0000
+++ b/configure	Sat Aug 30 12:22:21 2008 +0000
@@ -5475,10 +5475,10 @@
   if test "$_dl" = yes; then
     cat > $TMPC << EOF
 #include <inttypes.h>
-#include <libdvdread/dvd_reader.h>
-#include <libdvdread/ifo_types.h>
-#include <libdvdread/ifo_read.h>
-#include <libdvdread/nav_read.h>
+#include <dvdread/dvd_reader.h>
+#include <dvdread/ifo_types.h>
+#include <dvdread/ifo_read.h>
+#include <dvdread/nav_read.h>
 int main(void) { return 0; }
 EOF
   fi
--- a/dvdread/bswap.h	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-#ifndef DVDREAD_BSWAP_H
-#define DVDREAD_BSWAP_H
-
-#include "libavutil/bswap.h"
-
-#ifdef WORDS_BIGENDIAN
-#define B2N_16(x)
-#define B2N_32(x)
-#define B2N_64(x)
-#else
-#define B2N_16(x) x = bswap_16(x)
-#define B2N_32(x) x = bswap_32(x)
-#define B2N_64(x) x = bswap_64(x)
-#endif
-
-#endif
--- a/dvdread/cmd_print.c	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,550 +0,0 @@
-/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
-/*
- * Copyright (C) 2000, 2001, 2002, 2003 Martin Norbäck, Håkan Hjort
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, 
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "config.h"
-
-#include <stdio.h>
-#include <ctype.h>
-
-#if defined(HAVE_INTTYPES_H)
-#include <inttypes.h>
-#elif defined(HAVE_STDINT_H)
-#include <stdint.h>
-#endif
-
-#include "cmd_print.h"
-
-
-typedef struct
-{
-  uint8_t bits[8];
-  uint8_t examined[8];
-} cmd_t;
-
-
-static const char *cmp_op_table[] = {
-  NULL, "&", "==", "!=", ">=", ">", "<=", "<"
-};
-static const char *set_op_table[] = {
-  NULL, "=", "<->", "+=", "-=", "*=", "/=", "%=", "rnd", "&=", "|=", "^="
-};
-
-static const char *link_table[] = {
-  "LinkNoLink",  "LinkTopC",    "LinkNextC",   "LinkPrevC",
-  NULL,          "LinkTopPG",   "LinkNextPG",  "LinkPrevPG",
-  NULL,          "LinkTopPGC",  "LinkNextPGC", "LinkPrevPGC",
-  "LinkGoUpPGC", "LinkTailPGC", NULL,          NULL,
-  "RSM"
-};
-
-static const char *system_reg_table[] = {
-  "Menu Description Language Code",
-  "Audio Stream Number",
-  "Sub-picture Stream Number",
-  "Angle Number",
-  "Title Track Number",
-  "VTS Title Track Number",
-  "VTS PGC Number",
-  "PTT Number for One_Sequential_PGC_Title",
-  "Highlighted Button Number",
-  "Navigation Timer",
-  "Title PGC Number for Navigation Timer",
-  "Audio Mixing Mode for Karaoke",
-  "Country Code for Parental Management",
-  "Parental Level",
-  "Player Configurations for Video",
-  "Player Configurations for Audio",
-  "Initial Language Code for Audio",
-  "Initial Language Code Extension for Audio",
-  "Initial Language Code for Sub-picture",
-  "Initial Language Code Extension for Sub-picture",
-  "Player Regional Code",
-  "Reserved 21",
-  "Reserved 22",
-  "Reserved 23"
-};
-
-static const char *system_reg_abbr_table[] = {
-  NULL,
-  "ASTN",
-  "SPSTN",
-  "AGLN",
-  "TTN",
-  "VTS_TTN",
-  "TT_PGCN",
-  "PTTN",
-  "HL_BTNN",
-  "NVTMR",
-  "NV_PGCN",
-  NULL,
-  "CC_PLT",
-  "PLT",
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-};
-    
-
-
-static unsigned int bits(cmd_t *cmd, int byte, int bit, int count) {
-  unsigned int val = 0;
-  unsigned int bit_mask;
-  
-  while(count--) {
-    if(bit > 7) {
-      bit = 0;
-      byte++;
-    }
-    bit_mask = 0x01 << (7-bit);
-    val <<= 1;
-    if((cmd->bits[byte]) & bit_mask)
-      val |= 1;
-    cmd->examined[byte] |= bit_mask;
-    bit++;
-  }
-  return val;
-}
-
-
-static void print_system_reg(unsigned int reg) {
-  if(reg < sizeof(system_reg_abbr_table) / sizeof(char *))
-    fprintf(stdout, system_reg_table[reg]);
-  else
-    fprintf(stdout, " WARNING: Unknown system register ");
-}
-
-static void print_reg(unsigned int reg) {
-  if(reg & 0x80)
-    print_system_reg(reg & 0x7f);
-  else
-    if(reg < 16)
-      fprintf(stdout, "g[%u]", reg);
-    else
-      fprintf(stdout, " WARNING: Unknown general register ");
-}
-
-static void print_cmp_op(unsigned int op) {
-  if(op < sizeof(cmp_op_table) / sizeof(char *) && cmp_op_table[op] != NULL)
-    fprintf(stdout, " %s ", cmp_op_table[op]);
-  else
-    fprintf(stdout, " WARNING: Unknown compare op ");
-}
-
-static void print_set_op(unsigned int op) {
-  if(op < sizeof(set_op_table) / sizeof(char *) && set_op_table[op] != NULL)
-    fprintf(stdout, " %s ", set_op_table[op]);
-  else
-    fprintf(stdout, " WARNING: Unknown set op ");
-}
-
-static void print_reg_or_data(cmd_t *cmd, unsigned int immediate, int byte) {
-  if(immediate) {
-    int i = bits(cmd,byte,0,16);
-    
-    fprintf(stdout, "0x%x", i);
-    if(isprint(i & 0xff) && isprint((i>>8) & 0xff))
-      fprintf(stdout, " (\"%c%c\")", (char)((i>>8) & 0xff), (char)(i & 0xff));
-  } else {
-    print_reg(bits(cmd,byte + 1,0,8));
-  }
-}
-
-static void print_reg_or_data_2(cmd_t *cmd, unsigned int immediate, int byte) {
-  if(immediate)
-    fprintf(stdout, "0x%x", bits(cmd,byte,1,7));
-  else
-    fprintf(stdout, "g[%u]", bits(cmd,byte,4,4));
-}
-
-static void print_if_version_1(cmd_t *cmd) {
-  unsigned int op = bits(cmd,1,1,3);
-  
-  if(op) {
-    fprintf(stdout, "if (");
-    print_reg(bits(cmd,3,0,8));
-    print_cmp_op(op);
-    print_reg_or_data(cmd,bits(cmd,1,0,1), 4);
-    fprintf(stdout, ") ");
-  }
-}
-
-static void print_if_version_2(cmd_t *cmd) {
-  unsigned int op = bits(cmd,1,1,3);
-  
-  if(op) {
-    fprintf(stdout, "if (");
-    print_reg(bits(cmd,6,0,8));
-    print_cmp_op(op);
-    print_reg(bits(cmd,7,0,8));
-    fprintf(stdout, ") ");
-  }
-}
-
-static void print_if_version_3(cmd_t *cmd) {
-  unsigned int op = bits(cmd,1,1,3);
-  
-  if(op) {
-    fprintf(stdout, "if (");
-    print_reg(bits(cmd,2,0,8));
-    print_cmp_op(op);
-    print_reg_or_data(cmd,bits(cmd,1,0,1), 6);
-    fprintf(stdout, ") ");
-  }
-}
-
-static void print_if_version_4(cmd_t *cmd) {
-  unsigned int op = bits(cmd,1,1,3);
-  
-  if(op) {
-    fprintf(stdout, "if (");
-    print_reg(bits(cmd,1,4,4));
-    print_cmp_op(op);
-    print_reg_or_data(cmd,bits(cmd,1,0,1), 4);
-    fprintf(stdout, ") ");
-  }
-}
-
-static void print_if_version_5(cmd_t *cmd) {
-  unsigned int op = bits(cmd,1,1,3);
-  
-  if(op) {
-    fprintf(stdout, "if (");
-    print_reg(bits(cmd,4,0,8));
-    print_cmp_op(op);
-    print_reg(bits(cmd,5,0,8));
-    fprintf(stdout, ") ");
-  }
-}
-
-static void print_special_instruction(cmd_t *cmd) {
-  unsigned int op = bits(cmd,1,4,4);
-  
-  switch(op) {
-  case 0: // NOP
-    fprintf(stdout, "Nop");
-    break;
-  case 1: // Goto line
-    fprintf(stdout, "Goto %u", bits(cmd,7,0,8));
-    break;
-  case 2: // Break
-    fprintf(stdout, "Break");
-    break;
-  case 3: // Parental level
-    fprintf(stdout, "SetTmpPML %u, Goto %u", 
-            bits(cmd,6,4,4), bits(cmd,7,0,8));
-    break;
-  default:
-    fprintf(stdout, "WARNING: Unknown special instruction (%u)", 
-            bits(cmd,1,4,4));
-  }
-}
-
-static void print_linksub_instruction(cmd_t *cmd) {
-  unsigned int linkop = bits(cmd,7,3,5);
-  unsigned int button = bits(cmd,6,0,6);
-  
-  if(linkop < sizeof(link_table)/sizeof(char *) && link_table[linkop] != NULL)
-    fprintf(stdout, "%s (button %u)", link_table[linkop], button);
-  else
-    fprintf(stdout, "WARNING: Unknown linksub instruction (%u)", linkop);
-}
-
-static void print_link_instruction(cmd_t *cmd, int optional) {
-  unsigned int op = bits(cmd,1,4,4);
-  
-  if(optional && op)
-    fprintf(stdout, ", ");
-  
-  switch(op) {
-  case 0:
-    if(!optional)
-      fprintf(stdout, "WARNING: NOP (link)!");
-    break;
-  case 1:
-    print_linksub_instruction(cmd);
-    break;
-  case 4:
-    fprintf(stdout, "LinkPGCN %u", bits(cmd,6,1,15));
-    break;
-  case 5:
-    fprintf(stdout, "LinkPTT %u (button %u)", 
-            bits(cmd,6,6,10), bits(cmd,6,0,6));
-    break;
-  case 6:
-    fprintf(stdout, "LinkPGN %u (button %u)", 
-            bits(cmd,7,1,7), bits(cmd,6,0,6));
-    break;
-  case 7:
-    fprintf(stdout, "LinkCN %u (button %u)", 
-            bits(cmd,7,0,8), bits(cmd,6,0,6));
-    break;
-  default:
-    fprintf(stdout, "WARNING: Unknown link instruction");
-  }
-}
-
-static void print_jump_instruction(cmd_t *cmd) {
-  switch(bits(cmd,1,4,4)) {
-  case 1:
-    fprintf(stdout, "Exit");
-    break;
-  case 2:
-    fprintf(stdout, "JumpTT %u", bits(cmd,5,1,7));
-    break;
-  case 3:
-    fprintf(stdout, "JumpVTS_TT %u", bits(cmd,5,1,7));
-    break;
-  case 5:
-    fprintf(stdout, "JumpVTS_PTT %u:%u", bits(cmd,5,1,7), bits(cmd,2,6,10));
-    break;
-  case 6:
-    switch(bits(cmd,5,0,2)) {
-    case 0:
-      fprintf(stdout, "JumpSS FP");
-      break;
-    case 1:
-      fprintf(stdout, "JumpSS VMGM (menu %u)", bits(cmd,5,4,4));
-      break;
-    case 2:
-      fprintf(stdout, "JumpSS VTSM (vts %u, title %u, menu %u)", 
-              bits(cmd,4,0,8), bits(cmd,3,0,8), bits(cmd,5,4,4));
-      break;
-    case 3:
-      fprintf(stdout, "JumpSS VMGM (pgc %u)", bits(cmd,2,1,15));
-      break;
-    }
-    break;
-  case 8:
-    switch(bits(cmd,5,0,2)) {
-    case 0:
-      fprintf(stdout, "CallSS FP (rsm_cell %u)",
-              bits(cmd,4,0,8));
-      break;
-    case 1:
-      fprintf(stdout, "CallSS VMGM (menu %u, rsm_cell %u)",
-              bits(cmd,5,4,4), bits(cmd,4,0,8));
-      break;
-    case 2:
-      fprintf(stdout, "CallSS VTSM (menu %u, rsm_cell %u)",
-              bits(cmd,5,4,4), bits(cmd,4,0,8));
-      break;
-    case 3:
-      fprintf(stdout, "CallSS VMGM (pgc %u, rsm_cell %u)", 
-              bits(cmd,2,1,15), bits(cmd,4,0,8));
-      break;
-    }
-    break;
-  default:
-    fprintf(stdout, "WARNING: Unknown Jump/Call instruction");
-  }
-}
-
-static void print_system_set(cmd_t *cmd) {
-  int i;
-  
-  switch(bits(cmd,0,4,4)) {
-  case 1: // Set system reg 1 &| 2 &| 3 (Audio, Subp. Angle)
-    for(i = 1; i <= 3; i++) {
-      if(bits(cmd,2+i,0,1)) {
-        print_system_reg((unsigned int)i);
-        fprintf(stdout, " = ");
-        print_reg_or_data_2(cmd,bits(cmd,0,3,1), 2 + i);
-        fprintf(stdout, " ");
-      }
-    }
-    break;
-  case 2: // Set system reg 9 & 10 (Navigation timer, Title PGC number)
-    print_system_reg(9);
-    fprintf(stdout, " = ");
-    print_reg_or_data(cmd,bits(cmd,0,3,1), 2);
-    fprintf(stdout, " ");
-    print_system_reg(10);
-    fprintf(stdout, " = %u", bits(cmd,5,0,8)); // ??
-    break;
-  case 3: // Mode: Counter / Register + Set
-    fprintf(stdout, "SetMode ");
-    if(bits(cmd,5,0,1))
-      fprintf(stdout, "Counter ");
-    else
-      fprintf(stdout, "Register ");
-    print_reg(bits(cmd,5,4,4));
-    print_set_op(0x1); // '='
-    print_reg_or_data(cmd,bits(cmd,0,3,1), 2);
-    break;
-  case 6: // Set system reg 8 (Highlighted button)
-    print_system_reg(8);
-    if(bits(cmd,0,3,1)) // immediate
-      fprintf(stdout, " = 0x%x (button no %u)", 
-              bits(cmd,4,0,16), bits(cmd,4,0,6));
-    else
-      fprintf(stdout, " = g[%u]", bits(cmd,5,4,4));
-    break;
-  default:
-    fprintf(stdout, "WARNING: Unknown system set instruction (%u)", 
-            bits(cmd,0,4,4));
-  }
-}
-
-static void print_set_version_1(cmd_t *cmd) {
-  unsigned int set_op = bits(cmd,0,4,4);
-  
-  if(set_op) {
-    print_reg(bits(cmd,3,0,8));
-    print_set_op(set_op);
-    print_reg_or_data(cmd,bits(cmd,0,3,1), 4);
-  } else {
-    fprintf(stdout, "NOP");
-  }
-}
-
-static void print_set_version_2(cmd_t *cmd) {
-  unsigned int set_op = bits(cmd,0,4,4);
-  
-  if(set_op) {
-    print_reg(bits(cmd,1,4,4));
-    print_set_op(set_op);
-    print_reg_or_data(cmd,bits(cmd,0,3,1), 2);
-  } else {
-    fprintf(stdout, "NOP");
-  }
-}
-
-static void print_set_version_3(cmd_t *cmd) {
-  unsigned int set_op = bits(cmd,0,4,4);
-  
-  if(set_op) {
-    print_reg(bits(cmd,1,4,4));
-    print_set_op(set_op);
-    if(bits(cmd,0,3,1)) { // print_reg_or_data
-      unsigned int i = bits(cmd,2,0,16);
-      
-      fprintf(stdout, "0x%x", i);
-      if(isprint(i & 0xff) && isprint((i>>8) & 0xff))
-        fprintf(stdout, " (\"%c%c\")", 
-                (char)((i>>8) & 0xff), (char)(i & 0xff));
-    } else {
-      print_reg(bits(cmd,2,0,8));
-    }
-  } else {
-    fprintf(stdout, "NOP");
-  }
-}
-
-static void print_command(cmd_t *cmd) {
-  switch(bits(cmd,0,0,3)) { /* three first bits */
-  case 0: // Special instructions
-    print_if_version_1(cmd);
-    print_special_instruction(cmd);
-    break;
-  case 1: // Jump/Call or Link instructions
-    if(bits(cmd,0,3,1)) {
-      print_if_version_2(cmd);
-      print_jump_instruction(cmd);
-    } else {
-      print_if_version_1(cmd);
-      print_link_instruction(cmd,0); // must be pressent
-    }
-    break;
-  case 2: // Set System Parameters instructions
-    print_if_version_2(cmd);
-    print_system_set(cmd);
-    print_link_instruction(cmd,1); // either 'if' or 'link'
-    break;
-  case 3: // Set General Parameters instructions
-    print_if_version_3(cmd);
-    print_set_version_1(cmd);
-    print_link_instruction(cmd,1); // either 'if' or 'link'
-    break;
-  case 4: // Set, Compare -> LinkSub instructions
-    print_set_version_2(cmd);
-    fprintf(stdout, ", ");
-    print_if_version_4(cmd);
-    print_linksub_instruction(cmd);
-    break;
-  case 5: // Compare -> (Set and LinkSub) instructions
-    if(bits(cmd,0,3,1))
-      print_if_version_5(cmd);
-    else
-      print_if_version_1(cmd);
-    fprintf(stdout, "{ ");
-    print_set_version_3(cmd);
-    fprintf(stdout, ", ");
-    print_linksub_instruction(cmd);
-    fprintf(stdout, " }");
-    break;
-  case 6: // Compare -> Set, always LinkSub instructions
-    if(bits(cmd,0,3,1))
-      print_if_version_5(cmd);
-    else
-      print_if_version_1(cmd);
-    fprintf(stdout, "{ ");
-    print_set_version_3(cmd);
-    fprintf(stdout, " } ");
-    print_linksub_instruction(cmd);
-    break;
-  default:
-    fprintf(stdout, "WARNING: Unknown instruction type (%i)", 
-            bits(cmd,0,0,3));
-  }
-}
-
-void cmdPrint_mnemonic(vm_cmd_t *command)  {
-  int i, extra_bits;
-  cmd_t cmd;
-  
-  for(i = 0; i < 8; i++) {
-    cmd.bits[i] = command->bytes[i];
-    cmd.examined[i] = 0;
-  }
-
-  print_command(&cmd);
-  
-  // Check if there still are bits set that were not examined
-  extra_bits = 0;
-  for(i = 0; i < 8; i++)
-    if(cmd.bits[i] & ~ cmd.examined[i]) {
-      extra_bits = 1;
-      break;
-    }
-  if(extra_bits) {
-    fprintf(stdout, " [WARNING, unknown bits:");
-    for(i = 0; i < 8; i++)
-      fprintf(stdout, " %02x", cmd.bits[i] & ~ cmd.examined[i]);
-    fprintf(stdout, "]");
-  }
-}
-
-void cmdPrint_CMD(int row, vm_cmd_t *command) {
-  int i;
-
-  fprintf(stdout, "(%03d) ", row + 1);
-  for(i = 0; i < 8; i++)
-    fprintf(stdout, "%02x ", command->bytes[i]);
-  fprintf(stdout, "| ");
-
-  cmdPrint_mnemonic(command);
-  fprintf(stdout, "\n");
-}
--- a/dvdread/cmd_print.h	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
-#ifndef CMD_PRINT_H_INCLUDED
-#define CMD_PRINT_H_INCLUDED
-
-/*
- * Copyright (C) 2000, 2001, 2002, 2003 Martin Norbäck, Håkan Hjort
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, 
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include <dvdread/ifo_types.h>
-
-/**
- * Pretty printing of the DVD commands (vm instructions).
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-  
-/**
- * Prints a text representation of the commands to stdout.
- *
- * @param command Pointer to the DVD command to be printed.
- */
-void cmdPrint_mnemonic(vm_cmd_t *command);
-  
-/**
- * Prints row, then a hex dump of the command followed by the text
- * representation of the commands, as given by cmdPrint_mnemonic to
- * stdout.
- *
- * @param command Pointer to the DVD command to be printed.  */
-void cmdPrint_CMD(int row, vm_cmd_t *command);
-
-#ifdef __cplusplus
-};
-#endif
-#endif /* CMD_PRINT_H_INCLUDED */
--- a/dvdread/dvd_input.c	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,414 +0,0 @@
-/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
-/*
- * Copyright (C) 2002 Samuel Hocevar <sam@zoy.org>,
- *                    Håkan Hjort <d95hjort@dtek.chalmers.se>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
- */
-
-#include "config.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#define __USE_GNU /* to get O_DIRECT in linux */
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "dvd_reader.h"
-#include "dvd_input.h"
-
-#include "dvdread_internal.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); 
-/**
- *  pointer must be aligned to 2048 bytes
- *  if reading from a raw/O_DIRECT file
- */
-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/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 {
-  /* libdvdcss handle */
-  dvdcss_handle dvdcss;
-  
-  /* dummy file input */
-  int fd;
-};
-
-
-/**
- * initialize and open a DVD device or file.
- */
-static dvd_input_t css_open(const char *target)
-{
-  dvd_input_t dev;
-    
-  /* Allocate the handle structure */
-  dev = (dvd_input_t) malloc(sizeof(struct dvd_input_s));
-  if(dev == NULL) {
-    /* malloc has set errno to ENOMEM */
-    return NULL;
-  }
-  
-  /* Really open it with libdvdcss */
-  dev->dvdcss = DVDcss_open(target);
-  if(dev->dvdcss == 0) {
-    free(dev);
-    dev = NULL;
-  }
-  
-  return dev;
-}
-
-/**
- * return the last error message
- */
-static char *css_error(dvd_input_t dev)
-{
-  return DVDcss_error(dev->dvdcss);
-}
-
-/**
- * seek into the device.
- */
-static int css_seek(dvd_input_t dev, int blocks)
-{
-  /* DVDINPUT_NOFLAGS should match the DVDCSS_NOFLAGS value. */
-  return DVDcss_seek(dev->dvdcss, blocks, DVDINPUT_NOFLAGS);
-}
-
-/**
- * set the block for the begining of a new title (key).
- */
-static int css_title(dvd_input_t dev, int block)
-{
-  return DVDcss_title(dev->dvdcss, block);
-}
-
-/**
- * read data from the device.
- */
-static int css_read(dvd_input_t dev, void *buffer, int blocks, int flags)
-{
-  return DVDcss_read(dev->dvdcss, buffer, blocks, flags);
-}
-
-/**
- * close the DVD device and clean up the library.
- */
-static int css_close(dvd_input_t dev)
-{
-  int ret;
-
-  ret = DVDcss_close(dev->dvdcss);
-
-  if(ret < 0)
-    return ret;
-
-  free(dev);
-
-  return 0;
-}
-
-/* Need to use O_BINARY for WIN32 */
-#ifndef O_BINARY
-#ifdef _O_BINARY
-#define O_BINARY _O_BINARY
-#else
-#define O_BINARY 0
-#endif
-#endif
-
-/**
- * initialize and open a DVD device or file.
- */
-static dvd_input_t file_open(const char *target)
-{
-  dvd_input_t dev;
-  char *use_odirect;
-  int oflags;
-  
-  oflags = O_RDONLY | O_BINARY;
-  use_odirect = getenv("DVDREAD_USE_DIRECT");
-  if(use_odirect) {
-#ifndef O_DIRECT
-#define O_DIRECT 0
-#endif
-    oflags |= O_DIRECT;
-  }
-  /* Allocate the library structure */
-  dev = (dvd_input_t) malloc(sizeof(struct dvd_input_s));
-  if(dev == NULL) {
-    return NULL;
-  }
-  
-  /* Open the device */
-  dev->fd = open(target, oflags);
-  if(dev->fd < 0) {
-    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 = (off_t)blocks * (off_t)DVD_VIDEO_LB_LEN;
-
-  pos = lseek(dev->fd, pos, 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;
-  unsigned char *buf = buffer;
-
-  len = (size_t)blocks * DVD_VIDEO_LB_LEN;
-  
-  while(len > 0) {
-    
-    ret = read(dev->fd, buf, 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);
-    }
-    
-    buf+=ret;
-    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;
-}
-
-
-static void *dvdcss_library = NULL;
-static int dvdcss_library_init = 0;
-
-/**
- * Free any objects allocated by dvdinput_setup.
- * Should only be called when libdvdread is not to be used any more.
- * Closes dlopened libraries.
- */
-void dvdinput_free(void)
-{
-#ifdef HAVE_DVDCSS_DVDCSS_H
-  /* linked statically, nothing to free */
-  return;
-#else
-  if(dvdcss_library) {
-    dlclose(dvdcss_library);
-    dvdcss_library = NULL;
-  }
-  dvdcss_library_init = 0;
-  return;
-#endif
-}
-
-
-/**
- * Setup read functions with either libdvdcss or minimal DVD access.
- */
-int dvdinput_setup(void)
-{
-  char **dvdcss_version = NULL;
-  int verbose;
-
-  /* dlopening libdvdcss */
-  if(dvdcss_library_init) {
-    /* libdvdcss is already dlopened, function ptrs set */
-    if(dvdcss_library) {
-      return 1; /* css available */
-    } else {
-      return 0; /* css not available */
-    }
-  }
-
-  verbose = get_verbose();
-  
-#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
-
-  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")) {
-      if(verbose >= 0) {
-        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) {
-      if(verbose >= 0) {
-        fprintf(stderr,  "libdvdread: Missing symbols in libdvdcss.so.2, "
-                "this shouldn't happen !\n");
-      }
-      dlclose(dvdcss_library);
-      dvdcss_library = NULL;
-    }
-  }
-#endif /* HAVE_DVDCSS_DVDCSS_H */
-
-  dvdcss_library_init = 1;
-  
-  if(dvdcss_library) {
-    /*
-      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);
-    */
-    if(verbose >= 1) {
-      fprintf(stderr, "libdvdread: Using libdvdcss version %s for DVD access\n",
-              *dvdcss_version);
-    }
-    /* 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 {
-    if(verbose >= 1) {
-      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/dvdread/dvd_input.h	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
-#ifndef DVD_INPUT_H_INCLUDED
-#define DVD_INPUT_H_INCLUDED
-
-/*
- * Copyright (C) 2001, 2002 Samuel Hocevar <sam@zoy.org>,
- *                          Håkan Hjort <d95hjort@dtek.chalmers.se>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
- */
-
-/**
- * Defines and flags.  Make sure they fit the libdvdcss API!
- */
-#define DVDINPUT_NOFLAGS         0
-
-#define DVDINPUT_READ_DECRYPT    (1 << 0)
-
-typedef struct dvd_input_s *dvd_input_t;
-
-/**
- * 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);
-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);
-
-/**
- * Free any objects allocated by dvdinput_setup.
- * Should only be called when libdvdread is not to be used any more.
- * Closes dlopened libraries.
- */
-void dvdinput_free(void);
-
-/**
- * Setup function accessed by dvd_reader.c.  Returns 1 if there is CSS support.
- */
-int dvdinput_setup(void);
-
-#endif /* DVD_INPUT_H_INCLUDED */
--- a/dvdread/dvd_reader.c	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1611 +0,0 @@
-/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
-/*
- * 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 changelog at http://svn.mplayerhq.hu/mplayer/trunk/
- * $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 (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
- */
-
-#include "config.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/time.h> /* For the timing of dvdcss_title crack. */
-#include <fcntl.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-#include <limits.h>
-#include <dirent.h>
- 
-#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__) || defined(__DARWIN__) || defined(__DragonFly__)
-#define SYS_BSD 1
-#endif
-
-#if defined(__sun)
-#include <sys/mnttab.h>
-#elif defined(hpux)
-#include </usr/conf/h/mnttab.h>
-#elif defined(SYS_BSD)
-#include <fstab.h>
-#elif defined(__linux__) || defined(__CYGWIN__)
-#include <mntent.h>
-#endif
-
-#include "dvd_reader.h"
-#include "dvd_input.h"
-#include "dvd_udf.h"
-#include "md5.h"
-
-#include "dvdread_internal.h"
-
-#define DEFAULT_UDF_CACHE_LEVEL 0
-
-struct dvd_reader_s {
-  /* Basic information. */
-  int isImageFile;
-  
-  /* Hack for keeping track of the css status. 
-   * 0: no css, 1: perhaps (need init of keys), 2: have done init */
-  int css_state;
-  int css_title; /* Last title that we have called dvdinpute_title for. */
-
-  /* Information required for an image file. */
-  dvd_input_t dev;
-
-  /* Information required for a directory path drive. */
-  char *path_root;
-  
-  /* Filesystem cache */
-  int udfcache_level; /* 0 - turned off, 1 - on */
-  void *udfcache;
-
-  /* block aligned malloc */
-  void *align;
-  
-  /* error message verbosity level */
-  int verbose;
-};
-
-struct dvd_file_s {
-  /* Basic information. */
-  dvd_reader_t *dvd;
-  
-  /* Hack for selecting the right css title. */
-  int css_title;
-
-  /* Information required for an image file. */
-  uint32_t lb_start;
-  uint32_t seek_pos;
-
-  /* Information required for a directory path drive. */
-  size_t title_sizes[ 9 ];
-  dvd_input_t title_devs[ 9 ];
-
-  /* Calculated at open-time, size in blocks. */
-  ssize_t filesize;
-};
-
-
-#define DVDREAD_VERBOSE_DEFAULT 0
-
-int get_verbose(void)
-{
-  char *dvdread_verbose;
-  int verbose;
-  
-  dvdread_verbose = getenv("DVDREAD_VERBOSE");
-  if(dvdread_verbose) {
-    verbose = (int)strtol(dvdread_verbose, NULL, 0);
-  } else {
-    verbose = DVDREAD_VERBOSE_DEFAULT;
-  }
-  return verbose;
-}
-
-int dvdread_verbose(dvd_reader_t *dvd)
-{
-  return dvd->verbose;
-}
-
-dvd_reader_t *device_of_file(dvd_file_t *file)
-{
-  return file->dvd;
-}
-
-/**
- * Returns the compiled version. (DVDREAD_VERSION as an int)
- */
-int DVDVersion(void)
-{
-  return DVDREAD_VERSION;
-}
-
-
-/**
- * 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;
-}
-
-void *GetAlignHandle(dvd_reader_t *device)
-{
-  struct dvd_reader_s *dev = (struct dvd_reader_s *)device;
-  
-  return dev->align;
-}
-
-void SetAlignHandle(dvd_reader_t *device, void *align)
-{
-  struct dvd_reader_s *dev = (struct dvd_reader_s *)device;
-
-  dev->align = align;
-}
-
-
-/* Loop over all titles and call dvdcss_title to crack the keys. */
-static int initAllCSSKeys( dvd_reader_t *dvd )
-{
-  struct timeval all_s, all_e;
-  struct timeval t_s, t_e;
-  char filename[ MAX_UDF_FILE_NAME_LEN ];
-  uint32_t start, len;
-  int title;
-        
-  char *nokeys_str = getenv("DVDREAD_NOKEYS");
-  if(nokeys_str != NULL)
-    return 0;
-    
-  if(dvd->verbose >= 1) {
-    fprintf( stderr, "\n" );
-    fprintf( stderr, "libdvdread: Attempting to retrieve all CSS keys\n" );
-    fprintf( stderr, "libdvdread: This can take a _long_ time, "
-             "please be patient\n\n" );
-  }
-  gettimeofday(&all_s, NULL);
-        
-  for( title = 0; title < 100; title++ ) {
-    gettimeofday( &t_s, NULL );
-    if( title == 0 ) {
-      sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
-    } else {
-      sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 0 );
-    }
-    start = UDFFindFile( dvd, filename, &len );
-    if( start != 0 && len != 0 ) {
-      /* Perform CSS key cracking for this title. */
-      if(dvd->verbose >= 1) {
-        fprintf( stderr, "libdvdread: Get key for %s at 0x%08x\n", 
-                 filename, start );
-      }
-      if( dvdinput_title( dvd->dev, (int)start ) < 0 ) {
-        if(dvd->verbose >= 0) {
-          fprintf( stderr, "libdvdread: Error cracking CSS key for %s (0x%08x)\n", filename, start);
-        }
-      }
-      gettimeofday( &t_e, NULL );
-      if(dvd->verbose >= 1) {
-        fprintf( stderr, "libdvdread: Elapsed time %ld\n",  
-                 (long int) t_e.tv_sec - t_s.tv_sec );
-      }
-    }
-            
-    if( title == 0 ) continue;
-            
-    gettimeofday( &t_s, NULL );
-    sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 1 );
-    start = UDFFindFile( dvd, filename, &len );
-    if( start == 0 || len == 0 ) break;
-            
-    /* Perform CSS key cracking for this title. */
-    if(dvd->verbose >= 1) {
-      fprintf( stderr, "libdvdread: Get key for %s at 0x%08x\n", 
-               filename, start );
-    }
-    if( dvdinput_title( dvd->dev, (int)start ) < 0 ) {
-      if(dvd->verbose >= 0) {
-        fprintf( stderr, "libdvdread: Error cracking CSS key for %s (0x%08x)!!\n", filename, start);
-      }
-    }
-    gettimeofday( &t_e, NULL );
-    if(dvd->verbose >= 1) {
-      fprintf( stderr, "libdvdread: Elapsed time %ld\n",  
-               (long int) t_e.tv_sec - t_s.tv_sec );
-    }
-  }
-  title--;
-    
-  if(dvd->verbose >= 1) {
-    fprintf( stderr, "libdvdread: Found %d VTS's\n", title );
-  }
-  gettimeofday(&all_e, NULL);
-  if(dvd->verbose >= 1) {
-    fprintf( stderr, "libdvdread: Elapsed time %ld\n",  
-             (long int) all_e.tv_sec - all_s.tv_sec );
-  }
-  return 0;
-}
-
-
-
-/**
- * Open a DVD image or block device file.
- * Checks if the root directory in the udf image file can be found.
- * If not it assumes this isn't a valid udf image and returns NULL
- */
-static dvd_reader_t *DVDOpenImageFile( const char *location, int have_css )
-{
-  dvd_reader_t *dvd;
-  dvd_input_t dev;
-  int verbose;
-
-  verbose = get_verbose();
-
-  dev = dvdinput_open( location );
-  if( !dev ) {
-    if(verbose >= 1) {
-      fprintf( stderr, "libdvdread: Can't open '%s' for reading: %s\n",
-               location, strerror(errno));
-    }
-    return NULL;
-  }
-
-  dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
-  if( !dvd ) {
-    int tmp_errno = errno;
-    dvdinput_close(dev);
-    errno = tmp_errno;
-    return NULL;
-  }
-  dvd->verbose = verbose;
-  dvd->isImageFile = 1;
-  dvd->dev = dev;
-  dvd->path_root = NULL;
-    
-  dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
-  dvd->udfcache = NULL;
-
-  dvd->align = 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
-     * don't have that information. */
-    
-    dvd->css_state = 1; /* Need key init. */
-  }
-  dvd->css_title = 0;
-  
-  /* sanity check, is it a valid UDF image, can we find the root dir */
-  if(!UDFFindFile(dvd, "/", NULL)) {
-    dvdinput_close(dvd->dev);
-    if(dvd->udfcache) {
-      FreeUDFCache(dvd, dvd->udfcache);
-    }
-    if(dvd->align) {
-      if(dvd->verbose >= 0) {
-        fprintf(stderr, "libdvdread: DVDOpenImageFile(): Memory leak in align functions 1\n");
-      }
-    }
-    free(dvd);
-    return NULL;
-  }
-  return dvd;
-}
-
-static dvd_reader_t *DVDOpenPath( const char *path_root )
-{
-  dvd_reader_t *dvd;
-
-  dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
-  if( !dvd ) {
-    return NULL;
-  }
-  dvd->verbose = get_verbose();
-  dvd->isImageFile = 0;
-  dvd->dev = 0;
-  dvd->path_root = strdup( path_root );
-  if(!dvd->path_root) {
-    free(dvd);
-    return 0;
-  }
-  dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
-  dvd->udfcache = NULL;
-
-  dvd->align = NULL;
-
-  dvd->css_state = 0; /* Only used in the UDF path */
-  dvd->css_title = 0; /* Only matters in the UDF path */
-
-  return dvd;
-}
-
-#if defined(__sun)
-/* /dev/rdsk/c0t6d0s0 (link to /devices/...)
-   /vol/dev/rdsk/c0t6d0/??
-   /vol/rdsk/<name> */
-static char *sun_block2char( const char *path )
-{
-  char *new_path;
-
-  /* Must contain "/dsk/" */ 
-  if( !strstr( path, "/dsk/" ) ) return (char *) strdup( path );
-
-  /* Replace "/dsk/" with "/rdsk/" */
-  new_path = malloc( strlen(path) + 2 );
-  strcpy( new_path, path );
-  strcpy( strstr( new_path, "/dsk/" ), "" );
-  strcat( new_path, "/rdsk/" );
-  strcat( new_path, strstr( path, "/dsk/" ) + strlen( "/dsk/" ) );
-
-  return new_path;
-}
-#endif
-
-#if defined(SYS_BSD)
-/* FreeBSD /dev/(r)(a)cd0c (a is for atapi), recomended to _not_ use r
-   update: FreeBSD and DragonFly no longer uses the prefix so don't add it.
-
-   OpenBSD /dev/rcd0c, it needs to be the raw device
-   NetBSD  /dev/rcd0[d|c|..] d for x86, c (for non x86), perhaps others
-   Darwin  /dev/rdisk0,  it needs to be the raw device
-   BSD/OS  /dev/sr0c (if not mounted) or /dev/rsr0c ('c' any letter will do)
-   
-   returns a string allocated with strdup which should be free()'d when
-   no longer used.
-*/
-static char *bsd_block2char( const char *path )
-{
-#if defined(__FreeBSD__) || defined(__DragonFly__)
-  return (char *) strdup( path );
-#else
-  char *new_path;
-
-  /* If it doesn't start with "/dev/" or does start with "/dev/r" exit */ 
-  if( strncmp( path, "/dev/",  5 ) || !strncmp( path, "/dev/r", 6 ) ) 
-    return (char *) strdup( path );
-
-  /* Replace "/dev/" with "/dev/r" */
-  new_path = malloc( strlen(path) + 2 );
-  strcpy( new_path, "/dev/r" );
-  strcat( new_path, path + strlen( "/dev/" ) );
-
-  return new_path;
-#endif /* __FreeBSD__ || __DragonFly__ */
-}
-#endif
-
-
-dvd_reader_t *DVDOpen( const char *path )
-{
-  struct stat fileinfo;
-  int ret, have_css;
-  char *dev_name = NULL;
-  int internal_errno = 0;
-  int verbose;
-
-  if( path == NULL ) {
-    errno = EINVAL;
-    return NULL;
-  }
-  
-  verbose = get_verbose();
-
-#ifdef WIN32
-  /* Stat doesn't work on devices under mingwin/cygwin. */
-  if( path[0] && path[1] == ':' && path[2] == '\0' )
-    {
-      /* Don't try to stat the file */
-      fileinfo.st_mode = S_IFBLK;
-    }
-  else
-#endif
-    {
-      ret = stat( path, &fileinfo );
-      if( ret < 0 ) {
-        int tmp_errno = errno;
-        /* If we can't stat the file, give up */
-        if(verbose >= 1) {
-          fprintf( stderr, "libdvdread: Can't stat '%s': %s\n",
-                   path, strerror(errno));
-        }
-        errno = tmp_errno;
-        return NULL;
-      }
-    }
-
-  /* Try to open libdvdcss or fall back to standard functions */
-  have_css = dvdinput_setup();
-
-  /* First check if this is a block/char device or a file*/
-  if( S_ISBLK( fileinfo.st_mode ) || 
-      S_ISCHR( fileinfo.st_mode ) || 
-      S_ISREG( fileinfo.st_mode ) ) {
-    /**
-     * Block devices and regular files are assumed to be DVD-Video images.
-     */
-    dvd_reader_t *dvd = NULL;
-#if defined(__sun)
-    dev_name = sun_block2char( path );
-#elif defined(SYS_BSD)
-    dev_name = bsd_block2char( path );
-#else
-    dev_name = strdup( path );
-#endif
-    dvd = DVDOpenImageFile( dev_name, have_css );
-    free( dev_name );
-    
-    return dvd;
-  } else if( S_ISDIR( fileinfo.st_mode ) ) {
-    dvd_reader_t *auth_drive = 0;
-    char *path_copy;
-#if defined(SYS_BSD)
-    struct fstab* fe;
-#elif defined(__sun) || defined(__linux__) || defined(__CYGWIN__)
-    FILE *mntfile;
-#endif
-
-    /* XXX: We should scream real loud here. */
-    if( !(path_copy = strdup( path ) ) ) return 0;
-
-#ifndef WIN32 /* don't have fchdir, and getcwd( NULL, ... ) is strange */
-    /* Resolve any symlinks and get the absolut dir name. */
-    {
-      char *new_path;
-      char *current_path;
-
-      current_path = malloc(PATH_MAX);
-      if(current_path) {
-        if(!getcwd(current_path, PATH_MAX)) {
-          free(current_path);
-          current_path = NULL;
-        }
-      }
-      if(current_path) {
-        chdir( path_copy );
-        new_path = malloc(PATH_MAX);
-        if(new_path) {
-          if(!getcwd(new_path, PATH_MAX )) {
-            free(new_path);
-            new_path = NULL;
-          }
-        }
-
-        chdir(current_path);
-        free(current_path);
-        if( new_path ) {
-          free( path_copy );
-          path_copy = new_path;
-        }
-      }
-    }
-#endif
-        
-    /**
-     * If we're being asked to open a directory, check if that directory
-     * is the mountpoint for a DVD-ROM which we can use instead.
-     */
-
-    if( strlen( path_copy ) > 1 ) {
-      if( path_copy[ strlen( path_copy ) - 1 ] == '/' ) {
-        path_copy[ strlen( path_copy ) - 1 ] = '\0';
-      }
-    }
-
-    if( strlen( path_copy ) >= 9 ) {
-      if( !strcasecmp( &(path_copy[ strlen( path_copy ) - 9 ]), 
-                       "/video_ts" ) ) {
-        path_copy[ strlen( path_copy ) - 9 ] = '\0';
-        if(path_copy[0] == '\0') {
-          path_copy[0] = '/';
-          path_copy[1] = '\0';
-        }
-      }
-    }
-
-#if defined(SYS_BSD)
-    if( ( fe = getfsfile( path_copy ) ) ) {
-      dev_name = bsd_block2char( fe->fs_spec );
-      if(verbose >= 1) {
-        fprintf( stderr,
-                 "libdvdread: Attempting to use device %s"
-                 " mounted on %s%s\n",
-                 dev_name,
-                 fe->fs_file,
-                 have_css ? " for CSS authentication" : "");
-      }
-      auth_drive = DVDOpenImageFile( dev_name, have_css );
-      if(!auth_drive) {
-        internal_errno = errno;
-      }
-    }
-#elif defined(__sun)
-    mntfile = fopen( MNTTAB, "r" );
-    if( mntfile ) {
-      struct mnttab mp;
-      int res;
-      
-      while( ( res = getmntent( mntfile, &mp ) ) != -1 ) {
-        if( res == 0 && !strcmp( mp.mnt_mountp, path_copy ) ) {
-          dev_name = sun_block2char( mp.mnt_special );
-          if(verbose >= 1) {
-            fprintf( stderr, 
-                     "libdvdread: Attempting to use device %s"
-                     " mounted on %s%s\n",
-                     dev_name,
-                     mp.mnt_mountp,
-                     have_css ? " for CSS authentication" : "");
-          }
-          auth_drive = DVDOpenImageFile( dev_name, have_css );
-          if(!auth_drive) {
-            internal_errno = errno;
-          }
-          break;
-        }
-      }
-      fclose( mntfile );
-    }
-#elif defined(__linux__) || defined(__CYGWIN__)
-    mntfile = fopen( MOUNTED, "r" );
-    if( mntfile ) {
-      struct mntent *me;
- 
-      while( ( me = getmntent( mntfile ) ) ) {
-        if( !strcmp( me->mnt_dir, path_copy ) ) {
-          if(verbose >= 1) {
-            fprintf( stderr, 
-                     "libdvdread: Attempting to use device %s"
-                     " mounted on %s%s\n",
-                     me->mnt_fsname,
-                     me->mnt_dir,
-                     have_css ? " for CSS authentication" : "");
-          }
-          auth_drive = DVDOpenImageFile( me->mnt_fsname, have_css );
-          if(!auth_drive) {
-            internal_errno = errno;
-          }
-          dev_name = strdup(me->mnt_fsname);
-          break;
-        }
-      }
-      fclose( mntfile );
-    }
-#elif defined(__MINGW32__)
-    dev_name = strdup(path);
-    auth_drive = DVDOpenImageFile( path, have_css );
-#endif
-    if( !dev_name ) {
-      if(verbose >= 1) {
-        fprintf( stderr, "libdvdread: Couldn't find device name.\n" );
-      }
-    } else if( !auth_drive ) {
-      if(verbose >= 1) {
-        fprintf( stderr, "libdvdread: Device %s inaccessible%s: %s\n",
-                 dev_name,
-                 have_css ? ", CSS authentication not available" : "",
-                 strerror(internal_errno));
-      }
-    }
-
-    free( dev_name );
-    free( path_copy );
-
-    /**
-     * If we've opened a drive, just use that.
-     */
-    if( auth_drive ) {
-      return auth_drive;
-    }
-    /**
-     * Otherwise, we now try to open the directory tree instead.
-     */
-    return DVDOpenPath( path );
-  }
-
-  /* If it's none of the above, screw it. */
-  if(verbose >= 1) {
-    fprintf( stderr, "libdvdread: Could not open %s\n", path );
-  }
-  return 0;
-}
-
-void DVDClose( dvd_reader_t *dvd )
-{
-  if( dvd ) {
-    if( dvd->dev ) dvdinput_close( dvd->dev );
-    if( dvd->path_root ) free( dvd->path_root );
-    if( dvd->udfcache ) FreeUDFCache( dvd, dvd->udfcache );
-    if(dvd->align) {
-      if(dvd->verbose >= 0) {
-        fprintf(stderr, "libdvdread: DVDClose(): Memory leak in align functions\n");
-      }
-    }
-
-    free( dvd );
-  }
-}
-
-void DVDInit(void)
-{
-  dvdinput_setup();
-}
-
-void DVDFinish(void)
-{
-  dvdinput_free();
-}
-
-/**
- * Open an unencrypted file on a DVD image file.
- */
-static dvd_file_t *DVDOpenFileUDF( dvd_reader_t *dvd, char *filename )
-{
-  uint32_t start, len;
-  dvd_file_t *dvd_file;
-
-  start = UDFFindFile( dvd, filename, &len );
-  if( !start ) return 0;
-
-  dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
-  if( !dvd_file ) return 0;
-  dvd_file->dvd = dvd;
-  dvd_file->lb_start = start;
-  dvd_file->seek_pos = 0;
-  memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
-  memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
-  dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
-
-  return dvd_file;
-}
-
-/**
- * Searches for <file> in directory <path>, ignoring case.
- * Returns 0 and full filename in <filename>.
- *     or -1 on file not found.
- *     or -2 on path not found.
- */
-static int findDirFile( const char *path, const char *file, char *filename ) 
-{
-  DIR *dir;
-  struct dirent *ent;
-
-  dir = opendir( path );
-  if( !dir ) return -2;
-
-  while( ( ent = readdir( dir ) ) != NULL ) {
-    if( !strcasecmp( ent->d_name, file ) ) {
-      sprintf( filename, "%s%s%s", path,
-               ( ( path[ strlen( path ) - 1 ] == '/' ) ? "" : "/" ),
-               ent->d_name );
-      closedir(dir);
-      return 0;
-    }
-  }
-  closedir(dir);
-  return -1;
-}
-
-static int findDVDFile( dvd_reader_t *dvd, const char *file, char *filename )
-{
-  char video_path[ PATH_MAX + 1 ];
-  const char *nodirfile;
-  int ret;
-
-  /* Strip off the directory for our search */
-  if( !strncasecmp( "/VIDEO_TS/", file, 10 ) ) {
-    nodirfile = &(file[ 10 ]);
-  } else {
-    nodirfile = file;
-  }
-
-  ret = findDirFile( dvd->path_root, nodirfile, filename );
-  if( ret < 0 ) {
-    /* Try also with adding the path, just in case. */
-    sprintf( video_path, "%s/VIDEO_TS/", dvd->path_root );
-    ret = findDirFile( video_path, nodirfile, filename );
-    if( ret < 0 ) {
-      /* Try with the path, but in lower case. */
-      sprintf( video_path, "%s/video_ts/", dvd->path_root );
-      ret = findDirFile( video_path, nodirfile, filename );
-      if( ret < 0 ) {
-        return 0;
-      }
-    }
-  }
-
-  return 1;
-}
-
-/**
- * Open an unencrypted file from a DVD directory tree.
- */
-static dvd_file_t *DVDOpenFilePath( dvd_reader_t *dvd, char *filename )
-{
-  char full_path[ PATH_MAX + 1 ];
-  dvd_file_t *dvd_file;
-  struct stat fileinfo;
-  dvd_input_t dev;
-
-  /* Get the full path of the file. */
-  if( !findDVDFile( dvd, filename, full_path ) ) return 0;
-
-  dev = dvdinput_open( full_path );
-  if( !dev ) return 0;
-
-  dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
-  if( !dvd_file ) return 0;
-  dvd_file->dvd = dvd;
-  dvd_file->lb_start = 0;
-  dvd_file->seek_pos = 0;
-  memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
-  memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
-  dvd_file->filesize = 0;
-
-  if( stat( full_path, &fileinfo ) < 0 ) {
-    if(dvd->verbose >= 1) {
-      fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
-    }
-    free( dvd_file );
-    return 0;
-  }
-  dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
-  dvd_file->title_devs[ 0 ] = dev;
-  dvd_file->filesize = dvd_file->title_sizes[ 0 ];
-
-  return dvd_file;
-}
-
-static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *dvd, int title, int menu )
-{
-  char filename[ MAX_UDF_FILE_NAME_LEN ];
-  uint32_t start, len;
-  dvd_file_t *dvd_file;
-
-  if( title == 0 ) {
-    sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
-  } else {
-    sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
-  }
-  start = UDFFindFile( dvd, filename, &len );
-  if( start == 0 ) return 0;
-
-  dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
-  if( !dvd_file ) return 0;
-  dvd_file->dvd = dvd;
-  /*Hack*/ dvd_file->css_title = title << 1 | menu;
-  dvd_file->lb_start = start;
-  dvd_file->seek_pos = 0;
-  memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
-  memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
-  dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
-
-  /* Calculate the complete file size for every file in the VOBS */
-  if( !menu ) {
-    int cur;
-
-    for( cur = 2; cur < 10; cur++ ) {
-      sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur );
-      if( !UDFFindFile( dvd, filename, &len ) ) break;
-      dvd_file->filesize += len / DVD_VIDEO_LB_LEN;
-    }
-  }
-    
-  if( dvd->css_state == 1 /* Need key init */ ) {
-//    initAllCSSKeys( dvd );
-//    dvd->css_state = 2;
-  }
-  /*    
-        if( dvdinput_title( dvd_file->dvd->dev, (int)start ) < 0 ) {
-        fprintf( stderr, "libdvdread: Error cracking CSS key for %s\n",
-        filename );
-        }
-  */
-    
-  return dvd_file;
-}
-
-static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *dvd, int title, int menu )
-{
-  char filename[ MAX_UDF_FILE_NAME_LEN ];
-  char full_path[ PATH_MAX + 1 ];
-  struct stat fileinfo;
-  dvd_file_t *dvd_file;
-  int i;
-
-  dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
-  if( !dvd_file ) return 0;
-  dvd_file->dvd = dvd;
-  /*Hack*/ dvd_file->css_title = title << 1 | menu;
-  dvd_file->lb_start = 0;
-  dvd_file->seek_pos = 0;
-  memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
-  memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
-  dvd_file->filesize = 0;
-    
-  if( menu ) {
-    dvd_input_t dev;
-
-    if( title == 0 ) {
-      sprintf( filename, "VIDEO_TS.VOB" );
-    } else {
-      sprintf( filename, "VTS_%02i_0.VOB", title );
-    }
-    if( !findDVDFile( dvd, filename, full_path ) ) {
-      free( dvd_file );
-      return 0;
-    }
-
-    dev = dvdinput_open( full_path );
-    if( dev == NULL ) {
-      free( dvd_file );
-      return 0;
-    }
-
-    if( stat( full_path, &fileinfo ) < 0 ) {
-      if(dvd->verbose >= 1) {
-        fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
-      }
-      free( dvd_file );
-      return 0;
-    }
-    dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
-    dvd_file->title_devs[ 0 ] = dev;
-    dvdinput_title( dvd_file->title_devs[0], 0);
-    dvd_file->filesize = dvd_file->title_sizes[ 0 ];
-
-  } else {
-    for( i = 0; i < 9; ++i ) {
-
-      sprintf( filename, "VTS_%02i_%i.VOB", title, i + 1 );
-      if( !findDVDFile( dvd, filename, full_path ) ) {
-        break;
-      }
-
-      if( stat( full_path, &fileinfo ) < 0 ) {
-        if(dvd->verbose >= 1) {
-          fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
-        }
-        break;
-      }
-
-      dvd_file->title_sizes[ i ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
-      dvd_file->title_devs[ i ] = dvdinput_open( full_path );
-      dvdinput_title( dvd_file->title_devs[ i ], 0 );
-      dvd_file->filesize += dvd_file->title_sizes[ i ];
-    }
-    if( !dvd_file->title_devs[ 0 ] ) {
-      free( dvd_file );
-      return 0;
-    }
-  }
-
-  return dvd_file;
-}
-
-dvd_file_t *DVDOpenFile( dvd_reader_t *dvd, int titlenum, 
-                         dvd_read_domain_t domain )
-{
-  char filename[ MAX_UDF_FILE_NAME_LEN ];
-    
-  /* Check arguments. */
-  if( dvd == NULL || titlenum < 0 ) {
-    errno = EINVAL;
-    return NULL;
-  }
-
-  switch( domain ) {
-  case DVD_READ_INFO_FILE:
-    if( titlenum == 0 ) {
-      sprintf( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
-    } else {
-      sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );
-    }
-    break;
-  case DVD_READ_INFO_BACKUP_FILE:
-    if( titlenum == 0 ) {
-      sprintf( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
-    } else {
-      sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );
-    }
-    break;
-  case DVD_READ_MENU_VOBS:
-    if( dvd->isImageFile ) {
-      return DVDOpenVOBUDF( dvd, titlenum, 1 );
-    } else {
-      return DVDOpenVOBPath( dvd, titlenum, 1 );
-    }
-    break;
-  case DVD_READ_TITLE_VOBS:
-    if( titlenum == 0 ) return 0;
-    if( dvd->isImageFile ) {
-      return DVDOpenVOBUDF( dvd, titlenum, 0 );
-    } else {
-      return DVDOpenVOBPath( dvd, titlenum, 0 );
-    }
-    break;
-  default:
-    if(dvd->verbose >= 1) {
-      fprintf( stderr, "libdvdread: Invalid domain for file open.\n" );
-    }
-    errno = EINVAL;
-    return NULL;
-  }
-    
-  if( dvd->isImageFile ) {
-    return DVDOpenFileUDF( dvd, filename );
-  } else {
-    return DVDOpenFilePath( dvd, filename );
-  }
-}
-
-void DVDCloseFile( dvd_file_t *dvd_file )
-{
-  int i;
-
-  if( dvd_file ) {
-    if( dvd_file->dvd->isImageFile ) {
-      ;
-    } else {
-      for( i = 0; i < 9; ++i ) {
-        if( dvd_file->title_devs[ i ] ) {
-          dvdinput_close( dvd_file->title_devs[i] );
-        }
-      }
-    }
-
-    free( dvd_file );
-    dvd_file = 0;
-  }
-}
-
-static int DVDFileStatVOBUDF(dvd_reader_t *dvd, int title, 
-                             int menu, dvd_stat_t *statbuf)
-{
-  char filename[ MAX_UDF_FILE_NAME_LEN ];
-  uint32_t size;
-  off_t tot_size;
-  off_t parts_size[9];
-  int nr_parts = 0;
-  int n;
- 
-  if( title == 0 ) {
-    sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
-  } else {
-    sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
-  }
-  if(!UDFFindFile( dvd, filename, &size )) {
-    return -1;
-  }
-  tot_size = size;
-  nr_parts = 1;
-  parts_size[0] = size;
-
-  if( !menu ) {
-    int cur;
-
-    for( cur = 2; cur < 10; cur++ ) {
-      sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur );
-      if( !UDFFindFile( dvd, filename, &size ) ) {
-        break;
-      }
-      parts_size[nr_parts] = size;
-      tot_size += size;
-      nr_parts++;
-    }
-  }
-  
-  statbuf->size = tot_size;
-  statbuf->nr_parts = nr_parts;
-  for(n = 0; n < nr_parts; n++) {
-    statbuf->parts_size[n] = parts_size[n];
-  }
-  return 0;
-}
-
-
-static int DVDFileStatVOBPath( dvd_reader_t *dvd, int title,
-                                       int menu, dvd_stat_t *statbuf )
-{
-  char filename[ MAX_UDF_FILE_NAME_LEN ];
-  char full_path[ PATH_MAX + 1 ];
-  struct stat fileinfo;
-  off_t tot_size;
-  off_t parts_size[9];
-  int nr_parts = 0;
-  int n;
-
- 
-    
-  if( title == 0 ) {
-    sprintf( filename, "VIDEO_TS.VOB" );
-  } else {
-    sprintf( filename, "VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
-  }
-  if( !findDVDFile( dvd, filename, full_path ) ) {
-    return -1;
-  }
-  
-  if( stat( full_path, &fileinfo ) < 0 ) {
-    if(dvd->verbose >= 1) {
-      fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
-    }
-    return -1;
-  }
-  
-
-  tot_size = fileinfo.st_size;
-  nr_parts = 1;
-  parts_size[0] = fileinfo.st_size;
-
-  if( !menu ) {
-    int cur;
-    
-    for( cur = 2; cur < 10; cur++ ) {
-
-      sprintf( filename, "VTS_%02d_%d.VOB", title, cur );
-      if( !findDVDFile( dvd, filename, full_path ) ) {
-        break;
-      }
-
-      if( stat( full_path, &fileinfo ) < 0 ) {
-        if(dvd->verbose >= 1) {
-          fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
-        }
-        break;
-      }
-      
-      parts_size[nr_parts] = fileinfo.st_size;
-      tot_size += parts_size[nr_parts];
-      nr_parts++;
-    }
-  }
-
-  statbuf->size = tot_size;
-  statbuf->nr_parts = nr_parts;
-  for(n = 0; n < nr_parts; n++) {
-    statbuf->parts_size[n] = parts_size[n];
-  }
-  return 0;
-}
-
-
-int DVDFileStat(dvd_reader_t *dvd, int titlenum, 
-                dvd_read_domain_t domain, dvd_stat_t *statbuf)
-{
-  char filename[ MAX_UDF_FILE_NAME_LEN ];
-  char full_path[ PATH_MAX + 1 ];
-  struct stat fileinfo;
-  uint32_t size;
-
-  /* Check arguments. */
-  if( dvd == NULL || titlenum < 0 ) {
-    errno = EINVAL;
-    return -1;
-  }
-
-  switch( domain ) {
-  case DVD_READ_INFO_FILE:
-    if( titlenum == 0 ) {
-      sprintf( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
-    } else {
-      sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );
-    }
-    break;
-  case DVD_READ_INFO_BACKUP_FILE:
-    if( titlenum == 0 ) {
-      sprintf( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
-    } else {
-      sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );
-    }
-    break;
-  case DVD_READ_MENU_VOBS:
-    if( dvd->isImageFile ) {
-      return DVDFileStatVOBUDF( dvd, titlenum, 1, statbuf );
-    } else {
-      return DVDFileStatVOBPath( dvd, titlenum, 1, statbuf );
-    }
-    break;
-  case DVD_READ_TITLE_VOBS:
-    if( titlenum == 0 ) {
-      return -1;
-    }
-    if( dvd->isImageFile ) {
-      return DVDFileStatVOBUDF( dvd, titlenum, 0, statbuf );
-    } else {
-      return DVDFileStatVOBPath( dvd, titlenum, 0, statbuf );
-    }
-    break;
-  default:
-    if(dvd->verbose >= 1) {
-      fprintf( stderr, "libdvdread: Invalid domain for file stat.\n" );
-    }
-    errno = EINVAL;
-    return -1;
-  }
-  
-  if( dvd->isImageFile ) {
-    if( UDFFindFile( dvd, filename, &size ) ) {
-      statbuf->size = size;
-      statbuf->nr_parts = 1;
-      statbuf->parts_size[0] = size;
-      return 0;
-    }
-  } else {
-    if( findDVDFile( dvd, filename, full_path ) )  {
-      if( stat( full_path, &fileinfo ) < 0 ) {
-        if(dvd->verbose >= 1) {
-          fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
-        }
-      } else {
-        statbuf->size = fileinfo.st_size;
-        statbuf->nr_parts = 1;
-        statbuf->parts_size[0] = statbuf->size;
-        return 0;
-      }
-    }
-  }
-  return -1;
-}
-
-/**
- * Internal, but used from dvd_udf.c 
- *
- * @param device A read handle.
- * @param lb_number Logical block number to start read from.
- * @param block_count Number of logical blocks to read.
- * @param data Pointer to buffer where read data should be stored.
- *             This buffer must be large enough to hold lb_number*2048 bytes.
- *             The pointer must be aligned to the logical block size when
- *             reading from a raw/O_DIRECT device.
- * @param encrypted 0 if no decryption shall be performed,
- *                  1 if decryption shall be performed
- * @param return Returns number of blocks read on success, negative on error
- */
-int UDFReadBlocksRaw( dvd_reader_t *device, uint32_t lb_number,
-                      size_t block_count, unsigned char *data, 
-                      int encrypted )
-{
-  int ret;
-
-  if( !device->dev ) {
-    if(device->verbose >= 1) {
-      fprintf( stderr, "libdvdread: Fatal error in block read.\n" );
-    }
-    return 0;
-  }
-
-  ret = dvdinput_seek( device->dev, (int) lb_number );
-  if( ret != (int) lb_number ) {
-    if(device->verbose >= 1) {
-      fprintf( stderr,
-               "libdvdread: UDFReadBlocksRaw: Can't seek to block %u\n",
-               lb_number );
-    }
-    return 0;
-  }
-
-  return dvdinput_read( device->dev, (char *) data, 
-                        (int) block_count, encrypted );
-}
-
-/**
- * This is using a single input and starting from 'dvd_file->lb_start' offset.
- *
- * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
- * into the buffer located at 'data' and if 'encrypted' is set
- * descramble the data if it's encrypted.  Returning either an
- * negative error or the number of blocks read.
- *
- * @param data Pointer to buffer where read data should be placed.
- *             This buffer must be large enough to hold block_count*2048 bytes.
- *             The pointer must be aligned to 2048 bytes when reading from
- *             a raw/O_DIRECT device.
- * @return Returns the number of blocks read on success or a negative error.
- */
-static int DVDReadBlocksUDF( dvd_file_t *dvd_file, uint32_t offset,
-                             size_t block_count, unsigned char *data,
-                             int 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'.
- * data must be aligned to logical block size (2048 bytes) of the device
- * for raw/O_DIRECT devices to work
- * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
- * into the buffer located at 'data' and if 'encrypted' is set
- * descramble the data if it's encrypted.  Returning either an
- * negative error or the number of blocks read.
- *
- * @param dvd_file A file read handle.
- * @param offset Block offset from start of file.
- * @return Returns number of blocks read on success, negative on error.
- */
-static int DVDReadBlocksPath( dvd_file_t *dvd_file, unsigned int offset,
-                              size_t block_count, unsigned char *data,
-                              int encrypted )
-{
-  int i;
-  int ret, ret2, off;
-
-  ret = 0;
-  ret2 = 0;
-  for( i = 0; i < 9; ++i ) {
-    if( !dvd_file->title_sizes[ i ] ) return 0; /* Past end of file */
-
-    if( offset < dvd_file->title_sizes[ i ] ) {
-      if( ( offset + block_count ) <= dvd_file->title_sizes[ i ] ) {
-        off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );
-        if( off < 0 || off != (int)offset ) {
-          if(dvd_file->dvd->verbose >= 1) {
-            fprintf( stderr, "libdvdread: DVDReadBlocksPath1: Can't seek to block %d\n", 
-                     offset );
-          }
-          return off < 0 ? off : 0;
-        }
-        ret = dvdinput_read( dvd_file->title_devs[ i ], data,
-                             (int)block_count, encrypted );
-        break;
-      } else {
-        size_t part1_size = dvd_file->title_sizes[ i ] - offset;
-        /* FIXME: Really needs to be a while loop.
-         * (This is only true if you try and read >1GB at a time) */
-                
-        /* Read part 1 */
-        off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );
-        if( off < 0 || off != (int)offset ) {
-          if(dvd_file->dvd->verbose >= 1) {
-            fprintf( stderr, "libdvdread: DVDReadBlocksPath2: Can't seek to block %d\n", 
-                     offset );
-          }
-          return off < 0 ? off : 0;
-        }
-        ret = dvdinput_read( dvd_file->title_devs[ i ], data,
-                             (int)part1_size, encrypted );
-        if( ret < 0 ) return ret;
-        /* FIXME: This is wrong if i is the last file in the set. 
-         * also error from this read will not show in ret. */
-                
-        /* 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 );
-        if( off < 0 || off != 0 ) {
-          if(dvd_file->dvd->verbose >= 1) {
-            fprintf( stderr, "libdvdread: DVDReadBlocksPath3: Can't seek to block %d\n", 0 );
-          }
-          return off < 0 ? off : 0;
-        }
-        ret2 = dvdinput_read( dvd_file->title_devs[ i + 1 ], 
-                              data + ( part1_size
-                                       * (int64_t)DVD_VIDEO_LB_LEN ),
-                              (int)(block_count - part1_size),
-                              encrypted );
-        if( ret2 < 0 ) return ret2;
-        break;
-      }
-    } else {
-      offset -= dvd_file->title_sizes[ i ];
-    }
-  }
-
-  return ret + ret2;
-}
-
-/**
- * This is broken reading more than 2Gb at a time if ssize_t is 32-bit.
- */
-ssize_t DVDReadBlocks( dvd_file_t *dvd_file, int offset, 
-                       size_t block_count, unsigned char *data )
-{
-  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 );
-    } 
-    /* 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 ) {
-    ret = DVDReadBlocksUDF( dvd_file, (uint32_t)offset, 
-                            block_count, data, DVDINPUT_READ_DECRYPT );
-  } else {
-    ret = DVDReadBlocksPath( dvd_file, (unsigned int)offset, 
-                             block_count, data, DVDINPUT_READ_DECRYPT );
-  }
-    
-  return (ssize_t)ret;
-}
-
-int DVDFileSeek( dvd_file_t *dvd_file, int offset )
-{
-  /* Check arguments. */
-  if( dvd_file == NULL || offset < 0 )
-    return -1;
-    
-  if( offset > dvd_file->filesize * DVD_VIDEO_LB_LEN ) {
-    return -1;
-  }
-  dvd_file->seek_pos = (uint32_t) offset;
-  return offset;
-}
-
-#ifndef HAVE_UINTPTR_T
-#warning "Assuming that (unsigned long) can hold (void *)"
-typedef unsigned long uintptr_t;
-#endif
-
-#define DVD_ALIGN(ptr) (void *)((((uintptr_t)(ptr)) + (DVD_VIDEO_LB_LEN-1)) \
-                                / DVD_VIDEO_LB_LEN * DVD_VIDEO_LB_LEN)
-
-ssize_t DVDReadBytes( dvd_file_t *dvd_file, void *data, size_t byte_size )
-{
-  unsigned char *secbuf_start;
-  unsigned char *secbuf; //must be aligned to 2048-bytes for raw/O_DIRECT
-  unsigned int numsec, seek_sector, seek_byte;
-  int ret;
-    
-  /* Check arguments. */
-  if( dvd_file == NULL || data == NULL ) {
-    errno = EINVAL;
-    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 ) +
-    ( ( ( seek_byte + byte_size ) % DVD_VIDEO_LB_LEN ) ? 1 : 0 );
-
-  /* must align to 2048 bytes if we are reading from raw/O_DIRECT */
-  secbuf_start = (unsigned char *) malloc( (numsec+1) * DVD_VIDEO_LB_LEN );
-  if( !secbuf_start ) {
-    /* errno will be set to ENOMEM by malloc */
-    return -1;
-  }
-
-  secbuf = DVD_ALIGN(secbuf_start);
-
-  if( dvd_file->dvd->isImageFile ) {
-    ret = DVDReadBlocksUDF( dvd_file, (uint32_t) seek_sector, 
-                            (size_t) numsec, secbuf, DVDINPUT_NOFLAGS );
-  } else {
-    ret = DVDReadBlocksPath( dvd_file, seek_sector, 
-                             (size_t) numsec, secbuf, DVDINPUT_NOFLAGS );
-  }
-
-  if( ret != (int) numsec ) {
-    free( secbuf_start );
-    return ret < 0 ? ret : 0;
-  }
-
-  memcpy( data, &(secbuf[ seek_byte ]), byte_size );
-  free( secbuf_start );
-
-  dvd_file->seek_pos += byte_size;
-  return byte_size;
-}
-
-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;
-  int nr_of_files = 0;
-  int tmp_errno;
-  int nofiles_errno = ENOENT;
-  /* Check arguments. */
-  if( dvd == NULL || discid == NULL ) {
-    errno = EINVAL;
-    return -1;
-  }
-  /* 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 );
-
-      nr_of_files++;
-
-      if( buffer == NULL ) {
-        /* errno will be set to ENOMEM by malloc */
-        return -1;
-      }
-
-      bytes_read = DVDReadBytes( dvd_file, buffer, file_size );
-      if( bytes_read != file_size ) {
-        tmp_errno = errno;
-        if(dvd->verbose >= 1) {
-          fprintf( stderr, "libdvdread: DVDDiscId read returned %d bytes"
-                   ", wanted %d\n", (int)bytes_read, (int)file_size );
-        }
-        free(buffer);
-        DVDCloseFile( dvd_file );
-        errno = tmp_errno;
-        return -1;
-      }
-            
-      md5_process_bytes( buffer, file_size,  &ctx );
-            
-      DVDCloseFile( dvd_file );
-      free( buffer );
-    } else {
-      if(errno != ENOENT) {
-        nofiles_errno = errno;
-      }
-    }
-  }
-  md5_finish_ctx( &ctx, discid );
-  if(nr_of_files == 0) {
-    errno = nofiles_errno;
-    return -1;
-  }
-  return 0;
-}
-
-
-int DVDISOVolumeInfo( dvd_reader_t *dvd,
-                      char *volid, unsigned int volid_size,
-                      unsigned char *volsetid, unsigned int volsetid_size )
-{
-  unsigned char *buffer; /* must be aligned to 2048 for raw/O_DIRECT */
-  unsigned char *buffer_start; 
-  int ret;
-
-  /* Check arguments. */
-  if( dvd == NULL ) {
-    errno = EINVAL;
-    return -1;
-  }
-  
-  if( dvd->dev == NULL ) {
-    /* No block access, so no ISO... */
-    errno = EINVAL;
-    return -1;
-  }
-  
-  buffer_start = malloc( 2 * DVD_VIDEO_LB_LEN );
-  if( buffer_start == NULL ) {
-    return -1;
-  }
-
-  buffer = DVD_ALIGN(buffer_start);
-  
-  ret = UDFReadBlocksRaw( dvd, 16, 1, buffer, 0 );
-  if( ret != 1 ) {
-    if(dvd->verbose >= 1) {
-      fprintf( stderr, "libdvdread: DVDISOVolumeInfo, failed to "
-               "read ISO9660 Primary Volume Descriptor!\n" );
-    }
-    free(buffer_start);
-    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);
-  }
-  free(buffer_start);
-
-  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/dvdread/dvd_reader.h	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,344 +0,0 @@
-/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
-#ifndef DVD_READER_H_INCLUDED
-#define DVD_READER_H_INCLUDED
-
-/*
- * Copyright (C) 2001, 2002 Billy Biggs <vektor@dumbterm.net>,
- *                          Håkan Hjort <d95hjort@dtek.chalmers.se>,
- *                          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
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include <sys/types.h>
-
-/**
- * The DVD access interface.
- *
- * This file contains the functions that form the interface to to
- * reading files located on a DVD.
- */
-
-/**
- * The current version. (0.9.4 => 904, 1.2.3 => 10203)
- */
-#define DVDREAD_VERSION 907
-
-
-/**
- * The length of one Logical Block of a DVD.
- */
-#define DVD_VIDEO_LB_LEN 2048
-
-/**
- * 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;
-
-/**
- * Returns the compiled version. (DVDREAD_VERSION as an int)
- */
-int DVDVersion(void);
-
-
-/**
- * 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.
- *
- * If the given file is a block device, or is the mountpoint for a block
- * device, then that device is used for CSS authentication using libdvdcss.
- * If no device is available, then no CSS authentication is performed, 
- * and we hope that the image is decrypted.
- *
- * If the path given is a directory, then the files in that directory may be
- * in any one of these formats:
- *
- *   path/VIDEO_TS/VTS_01_1.VOB
- *   path/video_ts/vts_01_1.vob
- *   path/VTS_01_1.VOB
- *   path/vts_01_1.vob
- *
- * @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);
- *
- * Threads: this function uses chdir() and getcwd().
- * The current working directory is global to all threads,
- * so using chdir/getcwd in another thread could give unexpected results.
- */
-dvd_reader_t *DVDOpen( const char * );
-
-/**
- * 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 * );
-
-/**
- * Initializes libdvdread to be used with multithreading apps.
- *
- * You must call this function before using any other functions of libdvdread
- * if you are going to use libdvdread in multiple threads in your program.
- * If you are not using threads, or using libdvdread from just one thread,
- * you do not need to call this, but you are allowed to do so anyway.
- * 
- * There are several restrictions on how you can use libdvdread in
- * multithreading apps, see further documentation.
- *
- * If you have called DVDFinish() you need to call DVDInit again to use
- * libdvdread in multiple threads.
- *
- * DVDInit(void);
- */
-void DVDInit(void);
-
-/**
- * frees any dlopened objects.
- *
- * You must DVDClose all handles opened with DVDOpen before calling this.
- * Use this function if you need to close the dlopened libs and any other
- * objects that have been dynamically allocated by libdvdread.
- * 
- * DVDFinish(void);
- */
-void DVDFinish(void);
-
-/**
- * 
- */
-typedef enum {
-  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;
-
-/**
- *
- */
-typedef struct {
-  off_t size;          /**< Total size of file in bytes */
-  int nr_parts;           /**< Number of file parts */
-  off_t parts_size[9]; /**< Size of each part in bytes */
-} dvd_stat_t;
-
-/**
- * Stats a file on the DVD given the title number and domain.
- * The information about the file is stored in a dvd_stat_t
- * which contains information about the size of the file and
- * the number of parts in case of a multipart file and the respective
- * sizes of the parts.
- * A multipart file is for instance VTS_02_1.VOB, VTS_02_2.VOB, VTS_02_3.VOB
- * The size of VTS_02_1.VOB will be stored in stat->parts_size[0],
- * VTS_02_2.VOB in stat->parts_size[1], ...
- * The total size (sum of all parts) is stored in stat->size and
- * stat->nr_parts will hold the number of parts.
- * Only DVD_READ_TITLE_VOBS (VTS_??_[1-9].VOB) can be multipart files.
- * 
- * This function is only of use if you want to get the size of each file
- * in the filesystem. These sizes are not needed to use any other
- * functions in libdvdread. 
- *
- * @param dvd  A dvd read handle.
- * @param titlenum Which Video Title Set should be used, VIDEO_TS is 0.
- * @param domain Which domain. 
- * @param stat Pointer to where the result is stored.
- * @return If successful 0, otherwise -1.
- *
- * int DVDFileStat(dvd, titlenum, domain, stat);
- */
-int DVDFileStat(dvd_reader_t *, int, dvd_read_domain_t, dvd_stat_t *);
-  
-/**
- * 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.
- *
- * @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 );
-
-/**
- * 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 * );
-
-/**
- * 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.
- *             It must be aligned to the logical block size of the device when
- *             reading from a raw/O_DIRECT device (2048 bytes for DVD)
- * @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 * );
-
-/**
- * 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 );
-
-/**
- * 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 );
-  
-/**
- * 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
-#endif /* DVD_READER_H_INCLUDED */
--- a/dvdread/dvd_udf.c	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1217 +0,0 @@
-/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
-/*
- * This code is based on dvdudf by:
- *   Christian Wolff <scarabaeus@convergence.de>.
- *
- * 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
- * project's page is at http://linuxtv.org/dvd/
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.  Or, point your browser to
- * http://www.gnu.org/copyleft/gpl.html
- */
- 
-#include "config.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <errno.h>
-
-#if defined(HAVE_INTTYPES_H)
-#include <inttypes.h>
-#elif defined(HAVE_STDINT_H)
-#include <stdint.h>
-#endif
-
-#include "dvd_reader.h"
-#include "dvd_udf.h"
-#include "dvdread_internal.h"
-
-#ifndef EMEDIUMTYPE
-#define EMEDIUMTYPE ENOENT
-#endif
-
-#ifndef HAVE_UINTPTR_T
-#warning "Assuming that (unsigned long) can hold (void *)"
-typedef unsigned long uintptr_t;
-#endif
-
-#define DVD_ALIGN(ptr) (void *)((((uintptr_t)(ptr)) + (DVD_VIDEO_LB_LEN-1)) \
-                                / DVD_VIDEO_LB_LEN * DVD_VIDEO_LB_LEN)
-
-typedef struct {
-  void *start;
-  void *aligned;
-} dvdalign_ptrs_t;
-
-typedef struct {
-  dvdalign_ptrs_t *ptrs;
-  uint32_t ptrs_in_use;
-  uint32_t ptrs_max;
-} dvdalign_t;
-
-extern void *GetAlignHandle(dvd_reader_t *device);
-extern void SetAlignHandle(dvd_reader_t *device, void *align);
-
-/**
- * Allocates aligned memory (for use with reads from raw/O_DIRECT devices).
- * This memory must be freed with dvdalign_free()
- * The size of the memory that is allocate is num_lbs*2048 bytes.
- * The memory will be suitably aligned for use with
- * block reads from raw/O_DIRECT device.
- * @param num_lbs Number of logical blocks (2048 bytes) to allocate.
- * @return Returns pointer to allocated memory, or NULL on failure
- * This isn't supposed to be fast/efficient, if that is needed
- * this function should be rewritten to use posix_memalign or similar.
- * It's just needed for aligning memory for small block reads from
- * raw/O_DIRECT devices. 
- * We assume that 2048 is enough alignment for all systems at the moment.
- * Not thread safe. Only use this from one thread.
- * Depends on sizeof(unsigned long) being at least as large as sizeof(void *)
- */
-static void *dvdalign_lbmalloc(dvd_reader_t *device, uint32_t num_lbs)
-{
-  void *m;
-  int n;
-  dvdalign_t *a;
-  
-  m = malloc((num_lbs+1)*DVD_VIDEO_LB_LEN);
-  if(m == NULL) {
-    return m;
-  }
-  a = (dvdalign_t *)GetAlignHandle(device);
-  if(a == NULL) {
-    a = malloc(sizeof(dvdalign_t));
-    if(a == NULL) {
-      return a;
-    }
-    a->ptrs = NULL;
-    a->ptrs_in_use = 0;
-    a->ptrs_max = 0;
-    SetAlignHandle(device, (void *)a);
-  }
-  
-  if(a->ptrs_in_use >= a->ptrs_max) {
-    a->ptrs = realloc(a->ptrs, (a->ptrs_max+10)*sizeof(dvdalign_ptrs_t));
-    if(a->ptrs == NULL) {
-      free(m);
-      return NULL;
-    }
-    a->ptrs_max+=10;
-    for(n = a->ptrs_in_use; n < a->ptrs_max; n++) {
-      a->ptrs[n].start = NULL;
-      a->ptrs[n].aligned = NULL;
-    }
-    n = a->ptrs_in_use;
-  } else {
-    for(n = 0; n < a->ptrs_max; n++) {
-      if(a->ptrs[n].start == NULL) {
-        break;
-      }
-    }
-  }
-
-  a->ptrs[n].start = m;
-  a->ptrs[n].aligned = DVD_ALIGN(m);
-
-  a->ptrs_in_use++;
-
-  /* If this function starts to be used too much print a warning.
-     Either there is a memory leak somewhere or we need to rewrite this to
-     a more efficient version.
-  */
-  if(a->ptrs_in_use > 50) {
-    if(dvdread_verbose(device) >= 0) {
-      fprintf(stderr, "libdvdread: dvdalign_lbmalloc(), more allocs than supposed: %u\n", a->ptrs_in_use);
-    }
-  }
-
-  return  a->ptrs[n].aligned;
-}
-
-/**
- * Frees memory allocated with dvdalign_lbmemory() 
- * @param ptr Pointer to memory space to free
- * Not thread safe.
- */
-static void dvdalign_lbfree(dvd_reader_t *device, void *ptr)
-{
-  int n;
-  dvdalign_t *a;
-
-  a = (dvdalign_t *)GetAlignHandle(device);
-  if(a && a->ptrs) {
-    for(n = 0; n < a->ptrs_max; n++) {
-      if(a->ptrs[n].aligned == ptr) {
-        free(a->ptrs[n].start);
-        a->ptrs[n].start = NULL;
-        a->ptrs[n].aligned = NULL;
-        a->ptrs_in_use--;
-        if(a->ptrs_in_use == 0) {
-          free(a->ptrs);
-          a->ptrs = NULL;
-          a->ptrs_max = 0;
-          free(a);
-          a = NULL;
-          SetAlignHandle(device, (void *)a);
-        }
-        return;
-      }
-    }
-  }
-  if(dvdread_verbose(device) >= 0) {
-    fprintf(stderr, "libdvdread: dvdalign_lbfree(), error trying to free mem: %08lx (%u)\n", (unsigned long)ptr, a ? a->ptrs_in_use : 0);
-  }
-}
-
-
-/* Private but located in/shared with dvd_reader.c */
-extern int UDFReadBlocksRaw( dvd_reader_t *device, uint32_t lb_number,
-                             size_t block_count, unsigned char *data, 
-                             int encrypted );
-
-/** @internal
- * Its required to either fail or deliver all the blocks asked for. 
- *
- * @param data Pointer to a buffer where data is returned. This must be large
- *   enough to hold lb_number*2048 bytes.
- *   It must be aligned to system specific (2048) logical blocks size when
- *   reading from raw/O_DIRECT device.
- */
-static int DVDReadLBUDF( dvd_reader_t *device, uint32_t lb_number,
-                         size_t block_count, unsigned char *data, 
-                         int encrypted )
-{
-  int ret;
-  size_t count = block_count;
-  
-  while(count > 0) {
-    
-    ret = UDFReadBlocksRaw(device, lb_number, count, data, encrypted);
-        
-    if(ret <= 0) {
-      /* One of the reads failed or nothing more to read, too bad.
-       * We won't even bother returning the reads that went ok. */
-      return ret;
-    }
-    
-    count -= (size_t)ret;
-    lb_number += (uint32_t)ret;
-  }
-
-  return block_count;
-}
-
-
-#ifndef NULL
-#define NULL ((void *)0)
-#endif
-
-struct Partition {
-  int valid;
-  char VolumeDesc[128];
-  uint16_t Flags;
-  uint16_t Number;
-  char Contents[32];
-  uint32_t AccessType;
-  uint32_t Start;
-  uint32_t Length;
-};
-
-struct AD {
-  uint32_t Location;
-  uint32_t Length;
-  uint8_t  Flags;
-  uint16_t Partition;
-};
-
-struct extent_ad {
-  uint32_t location;
-  uint32_t length;
-};
-
-struct avdp_t {
-  struct extent_ad mvds;
-  struct extent_ad rvds;
-};
-
-struct pvd_t {
-  uint8_t VolumeIdentifier[32];
-  uint8_t VolumeSetIdentifier[128];
-};
-
-struct lbudf {
-  uint32_t lb;
-  uint8_t *data;
-};
-
-struct icbmap {
-  uint32_t lbn;
-  struct AD file;
-  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(dvd_reader_t *device, void *cache)
-{
-  int n;
-  
-  struct udf_cache *c = (struct udf_cache *)cache;
-  if(c == NULL) {
-    return;
-  }
-
-  for(n = 0; n < c->lb_num; n++) {
-    if(c->lbs[n].data) {
-      /* free data */
-      dvdalign_lbfree(device, c->lbs[n].data);
-    }
-  }
-  c->lb_num = 0;
-
-  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))
-#define GETN3(p) ((uint32_t)data[p] | ((uint32_t)data[(p) + 1] << 8)    \
-                  | ((uint32_t)data[(p) + 2] << 16))
-#define GETN4(p) ((uint32_t)data[p]                     \
-                  | ((uint32_t)data[(p) + 1] << 8)      \
-                  | ((uint32_t)data[(p) + 2] << 16)     \
-                  | ((uint32_t)data[(p) + 3] << 24))
-/* This is wrong with regard to endianess */
-#define GETN(p, n, target) memcpy(target, &data[p], n)
-
-static int Unicodedecode( uint8_t *data, int len, char *target ) 
-{
-  int p = 1, i = 0;
-
-  if( ( data[ 0 ] == 8 ) || ( data[ 0 ] == 16 ) ) do {
-    if( data[ 0 ] == 16 ) p++;  /* Ignore MSB of unicode16 */
-    if( p < len ) {
-      target[ i++ ] = data[ p++ ];
-    }
-  } while( p < len );
-
-  target[ i ] = '\0';
-  return 0;
-}
-
-static int UDFDescriptor( uint8_t *data, uint16_t *TagID ) 
-{
-  *TagID = GETN2(0);
-  // TODO: check CRC 'n stuff
-  return 0;
-}
-
-static int UDFExtentAD( uint8_t *data, uint32_t *Length, uint32_t *Location ) 
-{
-  *Length   = GETN4(0);
-  *Location = GETN4(4);
-  return 0;
-}
-
-static int UDFShortAD( uint8_t *data, struct AD *ad, 
-                       struct Partition *partition ) 
-{
-  ad->Length = GETN4(0);
-  ad->Flags = ad->Length >> 30;
-  ad->Length &= 0x3FFFFFFF;
-  ad->Location = GETN4(4);
-  ad->Partition = partition->Number; // use number of current partition
-  return 0;
-}
-
-static int UDFLongAD( uint8_t *data, struct AD *ad )
-{
-  ad->Length = GETN4(0);
-  ad->Flags = ad->Length >> 30;
-  ad->Length &= 0x3FFFFFFF;
-  ad->Location = GETN4(4);
-  ad->Partition = GETN2(8);
-  //GETN(10, 6, Use);
-  return 0;
-}
-
-static int UDFExtAD( uint8_t *data, struct AD *ad )
-{
-  ad->Length = GETN4(0);
-  ad->Flags = ad->Length >> 30;
-  ad->Length &= 0x3FFFFFFF;
-  ad->Location = GETN4(12);
-  ad->Partition = GETN2(16);
-  //GETN(10, 6, Use);
-  return 0;
-}
-
-static int UDFICB( uint8_t *data, uint8_t *FileType, uint16_t *Flags )
-{
-  *FileType = GETN1(11);
-  *Flags = GETN2(18);
-  return 0;
-}
-
-
-static int UDFPartition( uint8_t *data, uint16_t *Flags, uint16_t *Number,
-                         char *Contents, uint32_t *Start, uint32_t *Length )
-{
-  *Flags = GETN2(20);
-  *Number = GETN2(22);
-  GETN(24, 32, Contents);
-  *Start = GETN4(188);
-  *Length = GETN4(192);
-  return 0;
-}
-
-/**
- * Reads the volume descriptor and checks the parameters.  Returns 0 on OK, 1
- * on error.
- */
-static int UDFLogVolume( uint8_t *data, char *VolumeDescriptor )
-{
-  uint32_t lbsize, MT_L, N_PM;
-  Unicodedecode(&data[84], 128, VolumeDescriptor);
-  lbsize = GETN4(212);  // should be 2048
-  MT_L = GETN4(264);    // should be 6
-  N_PM = GETN4(268);    // should be 1
-  if (lbsize != DVD_VIDEO_LB_LEN) return 1;
-  return 0;
-}
-
-static int UDFFileEntry( uint8_t *data, uint8_t *FileType, 
-                         struct Partition *partition, struct AD *ad )
-{
-  uint16_t flags;
-  uint32_t L_EA, L_AD;
-  unsigned int p;
-
-  UDFICB( &data[ 16 ], FileType, &flags );
-   
-  /* Init ad for an empty file (i.e. there isn't a AD, L_AD == 0 ) */
-  ad->Length = GETN4( 60 ); // Really 8 bytes a 56
-  ad->Flags = 0;
-  ad->Location = 0; // what should we put here? 
-  ad->Partition = partition->Number; // use number of current partition
-
-  L_EA = GETN4( 168 );
-  L_AD = GETN4( 172 );
-  p = 176 + L_EA;
-  while( p < 176 + L_EA + L_AD ) {
-    switch( flags & 0x0007 ) {
-    case 0: UDFShortAD( &data[ p ], ad, partition ); p += 8;  break;
-    case 1: UDFLongAD( &data[ p ], ad );  p += 16; break;
-    case 2: UDFExtAD( &data[ p ], ad );   p += 20; break;
-    case 3:
-      switch( L_AD ) {
-      case 8:  UDFShortAD( &data[ p ], ad, partition ); break;
-      case 16: UDFLongAD( &data[ p ], ad );  break;
-      case 20: UDFExtAD( &data[ p ], ad );   break;
-      }
-      p += L_AD;
-      break;
-    default:
-      p += L_AD; break;
-    }
-  }
-  return 0;
-}
-
-static int UDFFileIdentifier( uint8_t *data, uint8_t *FileCharacteristics,
-                              char *FileName, struct AD *FileICB )
-{
-  uint8_t L_FI;
-  uint16_t L_IU;
-
-  *FileCharacteristics = GETN1(18);
-  L_FI = GETN1(19);
-  UDFLongAD(&data[20], FileICB);
-  L_IU = GETN2(36);
-  if (L_FI) Unicodedecode(&data[38 + L_IU], L_FI, FileName);
-  else FileName[0] = '\0';
-  return 4 * ((38 + L_FI + L_IU + 3) / 4);
-}
-
-/**
- * Maps ICB to FileAD
- * ICB: Location of ICB of directory to scan
- * FileType: Type of the file
- * File: Location of file the ICB is pointing to
- * return 1 on success, 0 on error;
- */
-static int UDFMapICB( dvd_reader_t *device, struct AD ICB, uint8_t *FileType,
-                      struct Partition *partition, struct AD *File ) 
-{
-  uint8_t *LogBlock;
-  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;
-  }
-
-  LogBlock = dvdalign_lbmalloc(device, 1);
-  if(!LogBlock) {
-    return 0;
-  }
-    
-  do {
-    if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) {
-      TagID = 0;
-    } else {
-      UDFDescriptor( LogBlock, &TagID );
-    }
-
-    if( TagID == 261 ) {
-      UDFFileEntry( LogBlock, FileType, partition, File );
-      tmpmap.file = *File;
-      tmpmap.filetype = *FileType;
-      SetUDFCache(device, MapCache, tmpmap.lbn, &tmpmap);
-      dvdalign_lbfree(device, LogBlock);
-      return 1;
-    };
-  } while( ( lbnum <= partition->Start + ICB.Location + ( ICB.Length - 1 )
-             / DVD_VIDEO_LB_LEN ) && ( TagID != 261 ) );
-
-  dvdalign_lbfree(device, LogBlock);
-  return 0;
-}
-
-/**
- * Dir: Location of directory to scan
- * FileName: Name of file to look for
- * FileICB: Location of ICB of the found file
- * return 1 on success, 0 on error;
- */
-static int UDFScanDir( dvd_reader_t *device, struct AD Dir, char *FileName,
-                       struct Partition *partition, struct AD *FileICB,
-                       int cache_file_info) 
-{
-  char filename[ MAX_UDF_FILE_NAME_LEN ];
-  uint8_t *directory;
-  uint32_t lbnum;
-  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 = dvdalign_lbmalloc(device, dir_lba)) == NULL) {
-        return 0;
-      }
-      if( DVDReadLBUDF( device, lbnum, dir_lba, cached_dir, 0) <= 0 ) {
-        dvdalign_lbfree(device, cached_dir);
-        cached_dir = NULL;
-      }
-      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;
-  }
-
-  directory = dvdalign_lbmalloc(device, 2);
-  if(!directory) {
-    return 0;
-  }
-  if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) {
-    dvdalign_lbfree(device, directory);
-    return 0;
-  }
-
-  p = 0;
-  while( p < Dir.Length ) {
-    if( p > DVD_VIDEO_LB_LEN ) {
-      ++lbnum;
-      p -= DVD_VIDEO_LB_LEN;
-      Dir.Length -= DVD_VIDEO_LB_LEN;
-      if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) {
-        dvdalign_lbfree(device, directory);
-        return 0;
-      }
-    }
-    UDFDescriptor( &directory[ p ], &TagID );
-    if( TagID == 257 ) {
-      p += UDFFileIdentifier( &directory[ p ], &filechar,
-                              filename, FileICB );
-      if( !strcasecmp( FileName, filename ) ) {
-        dvdalign_lbfree(device, directory);
-        return 1;
-      }
-    } else {
-      dvdalign_lbfree(device, directory);
-      return 0;
-    }
-  }
-
-  dvdalign_lbfree(device, directory);
-  return 0;
-}
-
-
-static int UDFGetAVDP( dvd_reader_t *device,
-                       struct avdp_t *avdp)
-{
-  uint8_t *Anchor;
-  uint32_t lbnum, MVDS_location, MVDS_length;
-  uint16_t TagID;
-  uint32_t lastsector;
-  int terminate;
-  struct avdp_t; 
-  
-  if(GetUDFCache(device, AVDPCache, 0, avdp)) {
-    return 1;
-  }
-  
-  /* Find Anchor */
-  lastsector = 0;
-  lbnum = 256;   /* Try #1, prime anchor */
-  terminate = 0;
-  
-  Anchor = dvdalign_lbmalloc(device, 1);
-  if(!Anchor) {
-    return 0;
-  }
-  for(;;) {
-    if( DVDReadLBUDF( device, lbnum, 1, Anchor, 0 ) > 0 ) {
-      UDFDescriptor( Anchor, &TagID );
-    } else {
-      TagID = 0;
-    }
-    if (TagID != 2) {
-      /* Not an anchor */
-      if( terminate ) {
-        dvdalign_lbfree(device, Anchor);
-        errno = EMEDIUMTYPE;
-        return 0; /* Final try failed */
-      } 
-      
-      if( lastsector ) {
-        /* We already found the last sector.  Try #3, alternative
-         * backup anchor.  If that fails, don't try again.
-         */
-        lbnum = lastsector;
-        terminate = 1;
-      } else {
-        /* TODO: Find last sector of the disc (this is optional). */
-        if( lastsector ) {
-          /* Try #2, backup anchor */
-          lbnum = lastsector - 256;
-        } else {
-          /* Unable to find last sector */
-          dvdalign_lbfree(device, Anchor);
-          errno = EMEDIUMTYPE;
-          return 0;
-        }
-      }
-    } else {
-      /* It's an anchor! We can leave */
-      break;
-    }
-  }
-  /* Main volume descriptor */
-  UDFExtentAD( &Anchor[ 16 ], &MVDS_length, &MVDS_location );
-  avdp->mvds.location = MVDS_location;
-  avdp->mvds.length = MVDS_length;
-  
-  /* Backup volume descriptor */
-  UDFExtentAD( &Anchor[ 24 ], &MVDS_length, &MVDS_location );
-  avdp->rvds.location = MVDS_location;
-  avdp->rvds.length = MVDS_length;
-  
-  SetUDFCache(device, AVDPCache, 0, avdp);
-  
-  dvdalign_lbfree(device, Anchor);
-  return 1;
-}
-
-/**
- * Looks for partition on the disc.  Returns 1 if partition found, 0 on error.
- *   partnum: Number of the partition, starting at 0.
- *   part: structure to fill with the partition information
- */
-static int UDFFindPartition( dvd_reader_t *device, int partnum,
-                             struct Partition *part ) 
-{
-  uint8_t *LogBlock;
-  uint32_t lbnum, MVDS_location, MVDS_length;
-  uint16_t TagID;
-  int i, volvalid;
-  struct avdp_t avdp;
-
-    
-  if(!UDFGetAVDP(device, &avdp)) {
-    return 0;
-  }
-
-  LogBlock = dvdalign_lbmalloc(device, 1);
-  if(!LogBlock) {
-    return 0;
-  }
-  /* Main volume descriptor */
-  MVDS_location = avdp.mvds.location;
-  MVDS_length = avdp.mvds.length;
-
-  part->valid = 0;
-  volvalid = 0;
-  part->VolumeDesc[ 0 ] = '\0';
-  i = 1;
-  do {
-    /* Find Volume Descriptor */
-    lbnum = MVDS_location;
-    do {
-
-      if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) {
-        TagID = 0;
-      } else {
-        UDFDescriptor( LogBlock, &TagID );
-      }
-
-      if( ( TagID == 5 ) && ( !part->valid ) ) {
-        /* Partition Descriptor */
-        UDFPartition( LogBlock, &part->Flags, &part->Number,
-                      part->Contents, &part->Start, &part->Length );
-        part->valid = ( partnum == part->Number );
-      } else if( ( TagID == 6 ) && ( !volvalid ) ) {
-        /* Logical Volume Descriptor */
-        if( UDFLogVolume( LogBlock, part->VolumeDesc ) ) {  
-          /* TODO: sector size wrong! */
-        } else {
-          volvalid = 1;
-        }
-      }
-
-    } while( ( lbnum <= MVDS_location + ( MVDS_length - 1 )
-               / DVD_VIDEO_LB_LEN ) && ( TagID != 8 )
-             && ( ( !part->valid ) || ( !volvalid ) ) );
-
-    if( ( !part->valid) || ( !volvalid ) ) {
-      /* Backup volume descriptor */
-      MVDS_location = avdp.mvds.location;
-      MVDS_length = avdp.mvds.length;
-    }
-  } while( i-- && ( ( !part->valid ) || ( !volvalid ) ) );
-
-  dvdalign_lbfree(device, LogBlock);
-  /* We only care for the partition, not the volume */
-  return part->valid;
-}
-
-uint32_t UDFFindFile( dvd_reader_t *device, char *filename,
-                      uint32_t *filesize )
-{
-  uint8_t *LogBlock;
-  uint32_t lbnum;
-  uint16_t TagID;
-  struct Partition partition;
-  struct AD RootICB, File, ICB;
-  char tokenline[ MAX_UDF_FILE_NAME_LEN ];
-  char *token;
-  uint8_t filetype;
-  
-  if(filesize) {
-    *filesize = 0;
-  }
-  tokenline[0] = '\0';
-  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);
-    
-    LogBlock = dvdalign_lbmalloc(device, 1);
-    if(!LogBlock) {
-      return 0;
-    }
-    /* Find root dir ICB */
-    lbnum = partition.Start;
-    do {
-      if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) {
-        TagID = 0;
-      } else {
-        UDFDescriptor( LogBlock, &TagID );
-      }
-
-      /* File Set Descriptor */
-      if( TagID == 256 ) {  // File Set Descriptor
-        UDFLongAD( &LogBlock[ 400 ], &RootICB );
-      }
-    } while( ( lbnum < partition.Start + partition.Length )
-             && ( TagID != 8 ) && ( TagID != 256 ) );
-
-    dvdalign_lbfree(device, LogBlock);
-      
-    /* 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,
-                       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, "/" );
-    }
-  } 
-
-  /* Sanity check. */
-  if( File.Partition != 0 ) {
-    return 0;
-  }
-
-  if(filesize) {
-    *filesize = File.Length;
-  }
-  /* Hack to not return partition.Start for empty files. */
-  if( !File.Location ) {
-    return 0;
-  } else {
-    return partition.Start + File.Location;
-  }
-}
-
-
-
-/**
- * Gets a Descriptor .
- * Returns 1 if descriptor found, 0 on error.
- * id, tagid of descriptor
- * bufsize, size of BlockBuf (must be >= DVD_VIDEO_LB_LEN)
- * and aligned for raw/O_DIRECT read.
- */
-static int UDFGetDescriptor( dvd_reader_t *device, int id,
-                             uint8_t *descriptor, int bufsize) 
-{
-  uint32_t lbnum, MVDS_location, MVDS_length;
-  struct avdp_t avdp;
-  uint16_t TagID;
-  uint32_t lastsector;
-  int i, terminate;
-  int desc_found = 0;
-  /* Find Anchor */
-  lastsector = 0;
-  lbnum = 256;   /* Try #1, prime anchor */
-  terminate = 0;
-  if(bufsize < DVD_VIDEO_LB_LEN) {
-    return 0;
-  }
-  
-  if(!UDFGetAVDP(device, &avdp)) {
-    return 0;
-  }
-
-  /* Main volume descriptor */
-  MVDS_location = avdp.mvds.location;
-  MVDS_length = avdp.mvds.length;
-  
-  i = 1;
-  do {
-    /* Find  Descriptor */
-    lbnum = MVDS_location;
-    do {
-      
-      if( DVDReadLBUDF( device, lbnum++, 1, descriptor, 0 ) <= 0 ) {
-        TagID = 0;
-      } else {
-        UDFDescriptor( descriptor, &TagID );
-      }
-      
-      if( (TagID == id) && ( !desc_found ) ) {
-        /* Descriptor */
-        desc_found = 1;
-      }
-    } while( ( lbnum <= MVDS_location + ( MVDS_length - 1 )
-               / DVD_VIDEO_LB_LEN ) && ( TagID != 8 )
-             && ( !desc_found) );
-    
-    if( !desc_found ) {
-      /* Backup volume descriptor */
-      MVDS_location = avdp.rvds.location;
-      MVDS_length = avdp.rvds.length;
-    }
-  } while( i-- && ( !desc_found )  );
-
-  
-  return desc_found;
-}
-
-
-static int UDFGetPVD(dvd_reader_t *device, struct pvd_t *pvd)
-{
-  uint8_t *pvd_buf;
-  
-  if(GetUDFCache(device, PVDCache, 0, pvd)) {
-    return 1;
-  }
-  
-  pvd_buf = dvdalign_lbmalloc(device, 1);
-  if(!pvd_buf) {
-    return 0;
-  }
-  if(!UDFGetDescriptor( device, 1, pvd_buf, 1*DVD_VIDEO_LB_LEN)) {
-    dvdalign_lbfree(device, pvd_buf);
-    return 0;
-  }
-  
-  memcpy(pvd->VolumeIdentifier, &pvd_buf[24], 32);
-  memcpy(pvd->VolumeSetIdentifier, &pvd_buf[72], 128);
-  SetUDFCache(device, PVDCache, 0, pvd);
-  
-  dvdalign_lbfree(device, pvd_buf);
-
-  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/dvdread/dvd_udf.h	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
-#ifndef DVD_UDF_H_INCLUDED
-#define DVD_UDF_H_INCLUDED
-
-/*
- * This code is based on dvdudf by:
- *   Christian Wolff <scarabaeus@convergence.de>.
- *
- * Modifications by:
- *   Billy Biggs <vektor@dumbterm.net>.
- *   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
- * project's page is at http://linuxtv.org/dvd/
- * 
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.  Or, point your browser to
- * http://www.gnu.org/copyleft/gpl.html
- */
-
-#if defined(HAVE_INTTYPES_H)
-#include <inttypes.h>
-#elif defined(HAVE_STDINT_H)
-#include <stdint.h>
-#endif
-
-#include "dvd_reader.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Looks for a file on the UDF disc/imagefile and returns the block number
- * where it begins, or 0 if it is not found.  The filename should be an
- * absolute pathname on the UDF filesystem, starting with '/'.  For example,
- * '/VIDEO_TS/VTS_01_1.IFO'.  On success, filesize will be set to the size of
- * the file in bytes.
- * This implementation relies on that the file size is less than 2^32
- * A DVD file can at most be 2^30 (-2048 ?).
- */
-uint32_t UDFFindFile( dvd_reader_t *device, char *filename, uint32_t *size );
-  
-void FreeUDFCache(dvd_reader_t *device, 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
-#endif /* DVD_UDF_H_INCLUDED */
--- a/dvdread/dvdread_internal.h	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
-#ifndef DVDREAD_INTERNAL_H
-#define DVDREAD_INTERNAL_H
-
-
-#define CHECK_VALUE(arg)
-
-
-int get_verbose(void);
-int dvdread_verbose(dvd_reader_t *dvd);
-dvd_reader_t *device_of_file(dvd_file_t *file);
-
-#endif /* DVDREAD_INTERNAL_H */
--- a/dvdread/ifo_print.c	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1190 +0,0 @@
-/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
-/* 
- * Copyright (C) 2000, 2001, 2002, 2003
- *               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 changelog at http://svn.mplayerhq.hu/mplayer/trunk/
- * $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
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "config.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <ctype.h>
-
-#if defined(HAVE_INTTYPES_H)
-#include <inttypes.h>
-#elif defined(HAVE_STDINT_H)
-#include <stdint.h>
-#endif
-
-#include "ifo_types.h"
-#include "ifo_read.h"
-#include "ifo_print.h"
-#include "cmd_print.h"
-#include "dvdread_internal.h"
-
-/* Put this in some other file / package?  It's used in nav_print too. */
-static void ifoPrint_time(dvd_time_t *dtime) {
-  const char *rate;
-  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,
-         dtime->minute,
-         dtime->second,
-         dtime->frame_u & 0x3f);
-  switch((dtime->frame_u & 0xc0) >> 6) {
-  case 1:
-    rate = "25.00";
-    break;
-  case 3:
-    rate = "29.97";
-    break;
-  default:
-    if(dtime->hour == 0 && dtime->minute == 0 
-       && dtime->second == 0 && dtime->frame_u == 0)
-      rate = "no";
-    else
-      rate = "(please send a bug report)";
-    break;
-  } 
-  printf(" @ %s fps", rate);
-}
-
-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)) */
-  if(attr->mpeg_version == 0 
-     && attr->video_format == 0 
-     && attr->display_aspect_ratio == 0 
-     && attr->permitted_df == 0 
-     && attr->unknown1 == 0 
-     && attr->line21_cc_1 == 0 
-     && attr->line21_cc_2 == 0 
-     && attr->bit_rate == 0 
-     && attr->video_format == 0 
-     && attr->letterboxed == 0 
-     && attr->film_mode == 0) {
-    printf("-- Unspecified --");
-    return;
-  }
-  
-  switch(attr->mpeg_version) {
-  case 0:
-    printf("mpeg1 ");
-    break;
-  case 1:
-    printf("mpeg2 ");
-    break;
-  default:
-    printf("(please send a bug report) ");
-  }
-  
-  switch(attr->video_format) {
-  case 0:
-    printf("ntsc ");
-    break;
-  case 1:
-    printf("pal ");
-    break;
-  default:
-    printf("(please send a bug report) ");
-  }
-  
-  switch(attr->display_aspect_ratio) {
-  case 0:
-    printf("4:3 ");
-    break;
-  case 3:
-    printf("16:9 ");
-    break;
-  default:
-    printf("(please send a bug report) ");
-  }
-  
-  // Wide is allways allowed..!!!
-  switch(attr->permitted_df) {
-  case 0:
-    printf("pan&scan+letterboxed ");
-    break;
-  case 1:
-    printf("only pan&scan "); //??
-    break;
-  case 2:
-    printf("only letterboxed ");
-    break;
-  case 3:
-    // not specified
-    break;
-  default:
-    printf("(please send a bug report)");
-  }
-  
-  printf("U%x ", attr->unknown1);
-  CHECK_VALUE(!attr->unknown1);
-  
-  if(attr->line21_cc_1 || attr->line21_cc_2) {
-    printf("NTSC CC ");
-    if(attr->line21_cc_1)
-      printf("1 ");
-    if(attr->line21_cc_2)
-      printf("2 ");
-  }
-
-  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;
-    if(attr->video_format != 0) 
-      height = 576;
-    switch(attr->picture_size) {
-    case 0:
-      printf("720x%d ", height);
-      break;
-    case 1:
-      printf("704x%d ", height);
-      break;
-    case 2:
-      printf("352x%d ", height);
-      break;
-    case 3:
-      printf("352x%d ", height/2);
-      break;      
-    default:
-      printf("(please send a bug report) ");
-    }
-  }
-
-  if(attr->letterboxed) {
-    printf("source letterboxed ");
-  }
-  
-  if(attr->film_mode) {
-    printf("film");
-  } else {
-    printf("video"); //camera
-  }
-}
-
-static void ifoPrint_audio_attributes(audio_attr_t *attr) {
-  
-  if(attr->audio_format == 0
-     && attr->multichannel_extension == 0
-     && attr->lang_type == 0
-     && attr->application_mode == 0
-     && attr->quantization == 0
-     && attr->sample_frequency == 0
-     && attr->channels == 0
-     && attr->lang_code == 0
-     && attr->lang_extension == 0
-     && attr->code_extension == 0
-     && attr->unknown3 == 0
-     && attr->unknown1 == 0) {
-    printf("-- Unspecified --");
-    return;
-  }
-  
-  switch(attr->audio_format) {
-  case 0:
-    printf("ac3 ");
-    break;
-  case 1:
-    printf("(please send a bug report) ");
-    break;
-  case 2:
-    printf("mpeg1 ");
-    break;
-  case 3:
-    printf("mpeg2ext ");
-    break;
-  case 4:
-    printf("lpcm ");
-    break;
-  case 5:
-    printf("(please send a bug report) ");
-    break;
-  case 6:
-    printf("dts ");
-    break;
-  default:
-    printf("(please send a bug report) ");
-  }
-  
-  if(attr->multichannel_extension)
-    printf("multichannel_extension ");
-  
-  switch(attr->lang_type) {
-  case 0:
-    // not specified
-    CHECK_VALUE(attr->lang_code == 0 || attr->lang_code == 0xffff);
-    break;
-  case 1:
-    printf("%c%c (%c) ", attr->lang_code>>8, attr->lang_code & 0xff,
-           attr->lang_extension ? attr->lang_extension : ' ');
-    if(attr->lang_extension) {
-      printf("(please send a bug report) lang_extension != 0");
-    }
-    break;
-  default:
-    printf("(please send a bug report) ");
-  }
-
-  switch(attr->application_mode) {
-  case 0:
-    // not specified
-    break;
-  case 1:
-    printf("karaoke mode ");
-    break;
-  case 2:
-    printf("surround sound mode ");
-    break;
-  default:
-    printf("(please send a bug report) ");
-  }
-  
-  switch(attr->audio_format) {
-  case 0: //ac3
-    if(attr->quantization != 3) {
-      printf("(please send a bug report) ac3 quant/drc not 3 (%d)",
-             attr->quantization);
-    }
-    break;
-  case 2: //mpeg 1 or mpeg 2 without extension stream
-  case 3: //mpeg 2 with extension stream
-    switch(attr->quantization) {
-    case 0: //no drc
-      printf("no drc ");
-      break;
-    case 1:
-      printf("drc ");
-      break;
-    default:
-      printf("(please send a bug report) mpeg reserved quant/drc  (%d)",
-             attr->quantization);
-      break;
-    }
-    break;
-  case 4:
-    switch(attr->quantization) {
-    case 0:
-      printf("16bit ");
-      break;
-    case 1:
-      printf("20bit ");
-      break;
-    case 2:
-      printf("24bit ");
-      break;
-    case 3:
-      printf("(please send a bug report) lpcm reserved quant/drc  (%d)",
-             attr->quantization);
-      break;
-    }
-    break;
-  case 6: //dts
-    if(attr->quantization != 3) {
-      printf("(please send a bug report) dts quant/drc not 3 (%d)",
-             attr->quantization);
-    }
-    break;
-  default:
-    break;
-  }
-
-  switch(attr->sample_frequency) {
-  case 0:
-    printf("48kHz ");
-    break;
-  case 1:
-    printf("96kHz ");
-    break;
-  default:
-    printf("sample_frequency %i (please send a bug report) ", 
-           attr->sample_frequency);
-  }
-  
-  printf("%dCh ", attr->channels + 1);
-  
-  switch(attr->code_extension) {
-  case 0:
-    printf("Not specified ");
-    break;
-  case 1: // Normal audio
-    printf("Normal Caption ");
-    break;
-  case 2: // visually imparied
-    printf("Audio for visually impaired ");
-    break;
-  case 3: // Directors 1
-    printf("Director's comments 1 ");
-    break;
-  case 4: // Directors 2
-    printf("Director's comments 2 ");
-    break;
-    //case 4: // Music score ?    
-  default:
-    printf("(please send a bug report) ");
-  }
-    
-  printf("%d ", attr->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(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->code_extension == 0) {
-    printf("-- Unspecified --");
-    return;
-  }
-  
-  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("lang not specified ");
-  }
-  
-  printf("%d ", attr->zero1);
-  printf("%d ", attr->zero2);
-  printf("%d ", attr->lang_extension);
-  
-  switch(attr->code_extension) {
-  case 0:
-    printf("Not specified ");
-    break;
-  case 1:
-    printf("Caption with normal size character ");
-    break;
-  case 2:
-    printf("Caption with bigger size character ");
-    break;
-  case 3: 
-    printf("Caption for children ");
-    break;
-  case 4:
-    printf("reserved ");
-    break;
-  case 5:
-    printf("Closed Caption with normal size character ");
-    break;
-  case 6:
-    printf("Closed Caption with bigger size character ");
-    break;
-  case 7:
-    printf("Closed Caption for children ");
-    break;
-  case 8:
-    printf("reserved ");
-    break;
-  case 9:
-    printf("Forced Caption");
-    break;
-  case 10:
-    printf("reserved ");
-    break;
-  case 11:
-    printf("reserved ");
-    break;
-  case 12:
-    printf("reserved ");
-    break;
-  case 13:
-    printf("Director's comments with normal size character ");
-    break;
-  case 14:
-    printf("Director's comments with bigger size character ");
-    break;
-  case 15:
-    printf("Director's comments for children ");
-    break;
-  default:
-    printf("(please send a bug report) ");
-  }
-
-}
-
-
-static void ifoPrint_USER_OPS(user_ops_t *user_ops) {
-  uint32_t uops;
-  unsigned char *ptr = (unsigned char *)user_ops;
-  
-  uops  = (*ptr++ << 24);
-  uops |= (*ptr++ << 16);
-  uops |= (*ptr++ << 8);
-  uops |= (*ptr++);
-  
-  if(uops == 0) {
-    printf("None\n");
-  } else if(uops == 0x01ffffff) {
-    printf("All\n");
-  } else {
-    if(user_ops->title_or_time_play)
-      printf("Title or Time Play, ");
-    if(user_ops->chapter_search_or_play)
-      printf("Chapter Search or Play, ");
-    if(user_ops->title_play)
-      printf("Title Play, ");
-    if(user_ops->stop)
-      printf("Stop, ");
-    if(user_ops->go_up)
-      printf("Go Up, ");
-    if(user_ops->time_or_chapter_search)
-      printf("Time or Chapter Search, ");
-    if(user_ops->prev_or_top_pg_search)
-      printf("Prev or Top PG Search, ");
-    if(user_ops->next_pg_search)
-      printf("Next PG Search, ");
-    if(user_ops->forward_scan)
-      printf("Forward Scan, ");
-    if(user_ops->backward_scan)
-      printf("Backward Scan, ");
-    if(user_ops->title_menu_call)
-      printf("Title Menu Call, ");
-    if(user_ops->root_menu_call)
-      printf("Root Menu Call, ");
-    if(user_ops->subpic_menu_call)
-      printf("SubPic Menu Call, ");
-    if(user_ops->audio_menu_call)
-      printf("Audio Menu Call, ");
-    if(user_ops->angle_menu_call)
-      printf("Angle Menu Call, ");
-    if(user_ops->chapter_menu_call)
-      printf("Chapter Menu Call, ");
-    if(user_ops->resume)
-      printf("Resume, ");
-    if(user_ops->button_select_or_activate)
-      printf("Button Select or Activate, ");
-    if(user_ops->still_off)
-      printf("Still Off, ");
-    if(user_ops->pause_on)
-      printf("Pause On, ");
-    if(user_ops->audio_stream_change)
-      printf("Audio Stream Change, ");
-    if(user_ops->subpic_stream_change)
-      printf("SubPic Stream Change, ");
-    if(user_ops->angle_change)
-      printf("Angle Change, ");
-    if(user_ops->karaoke_audio_pres_mode_change)
-      printf("Karaoke Audio Pres Mode Change, ");
-    if(user_ops->video_pres_mode_change)
-      printf("Video Pres Mode Change, ");
-    printf("\n");
-  }
-}
-
-
-void ifoPrint_VMGI_MAT(vmgi_mat_t *vmgi_mat) {
-  
-  printf("VMG Identifier: %.12s\n", vmgi_mat->vmg_identifier);
-  printf("Last Sector of VMG: %08x\n", vmgi_mat->vmg_last_sector);
-  printf("Last Sector of VMGI: %08x\n", vmgi_mat->vmgi_last_sector);
-  printf("Specification version number: %01x.%01x\n", 
-         vmgi_mat->specification_version >> 4, 
-         vmgi_mat->specification_version & 0xf);
-  /* Byte 2 of 'VMG Category' (00xx0000) is the Region Code */
-  printf("VMG Category: %08x\n", vmgi_mat->vmg_category);
-  printf("VMG Number of Volumes: %i\n", vmgi_mat->vmg_nr_of_volumes);
-  printf("VMG This Volume: %i\n", vmgi_mat->vmg_this_volume_nr);
-  printf("Disc side %i\n", vmgi_mat->disc_side);
-  printf("VMG Number of Title Sets %i\n", vmgi_mat->vmg_nr_of_title_sets);
-  printf("Provider ID: %.32s\n", vmgi_mat->provider_identifier);
-  printf("VMG POS Code: %08x", (uint32_t)(vmgi_mat->vmg_pos_code >> 32));
-  printf("%08x\n", (uint32_t)vmgi_mat->vmg_pos_code);
-  printf("End byte of VMGI_MAT: %08x\n", vmgi_mat->vmgi_last_byte);
-  printf("Start byte of First Play PGC FP PGC: %08x\n", 
-         vmgi_mat->first_play_pgc);
-  printf("Start sector of VMGM_VOBS: %08x\n", vmgi_mat->vmgm_vobs);
-  printf("Start sector of TT_SRPT: %08x\n", vmgi_mat->tt_srpt);
-  printf("Start sector of VMGM_PGCI_UT: %08x\n", vmgi_mat->vmgm_pgci_ut);
-  printf("Start sector of PTL_MAIT: %08x\n", vmgi_mat->ptl_mait);
-  printf("Start sector of VTS_ATRT: %08x\n", vmgi_mat->vts_atrt);
-  printf("Start sector of TXTDT_MG: %08x\n", vmgi_mat->txtdt_mgi);
-  printf("Start sector of VMGM_C_ADT: %08x\n", vmgi_mat->vmgm_c_adt);
-  printf("Start sector of VMGM_VOBU_ADMAP: %08x\n", 
-         vmgi_mat->vmgm_vobu_admap);
-  printf("Video attributes of VMGM_VOBS: ");
-  ifoPrint_video_attributes(&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(&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(&vmgi_mat->vmgm_subp_attr);
-    printf("\n");
-  }
-}
-
-
-void ifoPrint_VTSI_MAT(vtsi_mat_t *vtsi_mat) {
-  int i;
-
-  printf("VTS Identifier: %.12s\n", vtsi_mat->vts_identifier);
-  printf("Last Sector of VTS: %08x\n", vtsi_mat->vts_last_sector);
-  printf("Last Sector of VTSI: %08x\n", vtsi_mat->vtsi_last_sector);
-  printf("Specification version number: %01x.%01x\n", 
-         vtsi_mat->specification_version>>4, 
-         vtsi_mat->specification_version&0xf);
-  printf("VTS Category: %08x\n", vtsi_mat->vts_category);
-  printf("End byte of VTSI_MAT: %08x\n", vtsi_mat->vtsi_last_byte);
-  printf("Start sector of VTSM_VOBS:  %08x\n", vtsi_mat->vtsm_vobs);
-  printf("Start sector of VTSTT_VOBS: %08x\n", vtsi_mat->vtstt_vobs);
-  printf("Start sector of VTS_PTT_SRPT: %08x\n", vtsi_mat->vts_ptt_srpt);
-  printf("Start sector of VTS_PGCIT:    %08x\n", vtsi_mat->vts_pgcit);
-  printf("Start sector of VTSM_PGCI_UT: %08x\n", vtsi_mat->vtsm_pgci_ut);
-  printf("Start sector of VTS_TMAPT:    %08x\n", vtsi_mat->vts_tmapt);
-  printf("Start sector of VTSM_C_ADT:      %08x\n", vtsi_mat->vtsm_c_adt);
-  printf("Start sector of VTSM_VOBU_ADMAP: %08x\n",vtsi_mat->vtsm_vobu_admap);
-  printf("Start sector of VTS_C_ADT:       %08x\n", vtsi_mat->vts_c_adt);
-  printf("Start sector of VTS_VOBU_ADMAP:  %08x\n", vtsi_mat->vts_vobu_admap);
-
-  printf("Video attributes of VTSM_VOBS: ");
-  ifoPrint_video_attributes(&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(&vtsi_mat->vtsm_audio_attr);
-    printf("\n");
-  }
-  
-  printf("VTSM Number of Sub-picture attributes: %i\n", 
-         vtsi_mat->nr_of_vtsm_subp_streams);
-  if(vtsi_mat->nr_of_vtsm_subp_streams > 0) {
-    printf("\tstream %2i status: ", 1);
-    ifoPrint_subp_attributes(&vtsi_mat->vtsm_subp_attr);
-    printf("\n");
-  }
-  
-  printf("Video attributes of VTS_VOBS: ");
-  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(&vtsi_mat->vts_audio_attr[i]);
-    printf("\n");
-  }
-  
-  printf("VTS Number of Subpicture attributes: %i\n", 
-         vtsi_mat->nr_of_vts_subp_streams);
-  for(i = 0; i < vtsi_mat->nr_of_vts_subp_streams; i++) {
-    printf("\tstream %2i status: ", i);
-    ifoPrint_subp_attributes(&vtsi_mat->vts_subp_attr[i]);
-    printf("\n");
-  }
-  
-  /* FIXME:  Add printing of MultiChannel Extension */
-}
-
-
-static void ifoPrint_PGC_COMMAND_TBL(pgc_command_tbl_t *cmd_tbl) {
-  int i;
-  
-  if(cmd_tbl == NULL) {
-    printf("No Command table present\n");
-    return;
-  }
-  
-  printf("Number of Pre commands: %i\n", cmd_tbl->nr_of_pre);
-  for(i = 0; i < cmd_tbl->nr_of_pre; i++) {
-    cmdPrint_CMD(i, &cmd_tbl->pre_cmds[i]);
-  }
-
-  printf("Number of Post commands: %i\n", cmd_tbl->nr_of_post);
-  for(i = 0; i < cmd_tbl->nr_of_post; i++) {
-    cmdPrint_CMD(i, &cmd_tbl->post_cmds[i]);
-  }
-
-  printf("Number of Cell commands: %i\n", cmd_tbl->nr_of_cell);
-  for(i = 0; i < cmd_tbl->nr_of_cell; i++) {
-    cmdPrint_CMD(i, &cmd_tbl->cell_cmds[i]);
-  }
-}
-
-
-static void ifoPrint_PGC_PROGRAM_MAP(pgc_program_map_t *program_map, int nr) {
-  int i;
-  
-  if(program_map == NULL) {
-    printf("No Program map present\n");
-    return;
-  }
-  
-  for(i = 0; i < nr; i++) {
-    printf("Program %3i Entry Cell: %3i\n", i + 1, program_map[i]);
-  }
-}
-
-
-static void ifoPrint_CELL_PLAYBACK(cell_playback_t *cell_playback, int nr) {
-  int i;
-  
-  if(cell_playback == NULL) {
-    printf("No Cell Playback info present\n");
-    return;
-  }
-  
-  for(i=0;i<nr;i++) {
-    printf("Cell: %3i ", i + 1);
-
-    ifoPrint_time(&cell_playback[i].playback_time);
-    printf("\t");
-
-    if(cell_playback[i].block_mode || cell_playback[i].block_type) {
-      const char *s;
-      switch(cell_playback[i].block_mode) {
-      case 0:
-        s = "not a"; break;
-      case 1:
-        s = "the first"; break;
-      case 2:
-      default:
-        s = ""; break;
-      case 3:
-        s = "last"; break;
-      }
-      printf("%s cell in the block ", s);
-      
-      switch(cell_playback[i].block_type) {
-      case 0:
-        printf("not part of the block ");
-        break;
-      case 1:
-        printf("angle block ");
-        break;
-      case 2:
-      case 3:
-        printf("(send bug repport) ");
-        break;
-      }
-    }
-    if(cell_playback[i].seamless_play)
-      printf("presented seamlessly ");
-    if(cell_playback[i].interleaved)
-      printf("cell is interleaved ");
-    if(cell_playback[i].stc_discontinuity)
-      printf("STC_discontinuty ");
-    if(cell_playback[i].seamless_angle)
-      printf("only seamless angle ");
-    if(cell_playback[i].restricted)
-      printf("restricted cell ");
-    
-    if(cell_playback[i].still_time)
-      printf("still time %d ", cell_playback[i].still_time);
-    if(cell_playback[i].cell_cmd_nr)
-      printf("cell command %d", cell_playback[i].cell_cmd_nr);
-    
-    printf("\n\tStart sector: %08x\tFirst ILVU end  sector: %08x\n", 
-           cell_playback[i].first_sector, 
-           cell_playback[i].first_ilvu_end_sector);
-    printf("\tEnd   sector: %08x\tLast VOBU start sector: %08x\n", 
-           cell_playback[i].last_sector, 
-           cell_playback[i].last_vobu_start_sector);
-  }
-}
-
-static void ifoPrint_CELL_POSITION(cell_position_t *cell_position, int nr) {
-  int i;
-  
-  if(cell_position == NULL) {
-    printf("No Cell Position info present\n");
-    return;
-  }
-  
-  for(i=0;i<nr;i++) {
-    printf("Cell: %3i has VOB ID: %3i, Cell ID: %3i\n", i + 1, 
-           cell_position[i].vob_id_nr, cell_position[i].cell_nr);
-  }
-}
-
-
-void ifoPrint_PGC(pgc_t *pgc) {
-  int i;
-  
-  if(pgc == NULL) {
-    printf("Error: No PGC present\n");
-    return;
-  }
-
-  printf("Number of Programs: %i\n", pgc->nr_of_programs);
-  printf("Number of Cells: %i\n", pgc->nr_of_cells);
-  /* Check that time is 0:0:0:0 also if nr_of_programs==0 */
-  printf("Playback time: ");
-  ifoPrint_time(&pgc->playback_time); printf("\n");
-
-  /* If no programs/no time then does this mean anything? */
-  printf("Prohibited user operations: ");
-  ifoPrint_USER_OPS(&pgc->prohibited_ops);
-  
-  for(i = 0; i < 8; i++) {
-    if(pgc->audio_control[i] & 0x8000) { /* The 'is present' bit */
-      printf("Audio stream %i control: %04x\n", 
-             i, pgc->audio_control[i]);
-    }
-  }
-  
-  for(i = 0; i < 32; i++) {
-    if(pgc->subp_control[i] & 0x80000000) { /* The 'is present' bit */
-      printf("Subpicture stream %2i control: %08x\n", 
-             i, pgc->subp_control[i]);
-    }
-  }
-  
-  printf("Next PGC number: %i\n", pgc->next_pgc_nr);
-  printf("Prev PGC number: %i\n", pgc->prev_pgc_nr);
-  printf("GoUp PGC number: %i\n", pgc->goup_pgc_nr);
-  if(pgc->nr_of_programs != 0) {
-    printf("Still time: %i seconds (255=inf)\n", pgc->still_time);
-    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) {
-    for(i = 0; i < 16; i++) {
-      printf("Color %2i: %08x\n", i, pgc->palette[i]);
-    }
-  }
-  
-  /* Memmory offsets to div. tables. */
-  ifoPrint_PGC_COMMAND_TBL(pgc->command_tbl);
-  ifoPrint_PGC_PROGRAM_MAP(pgc->program_map, pgc->nr_of_programs);
-  ifoPrint_CELL_PLAYBACK(pgc->cell_playback, pgc->nr_of_cells);
-  ifoPrint_CELL_POSITION(pgc->cell_position, pgc->nr_of_cells);
-}
-
-
-void ifoPrint_TT_SRPT(tt_srpt_t *tt_srpt) {
-  int i;
-  
-  printf("Number of TitleTrack search pointers: %i\n",
-         tt_srpt->nr_of_srpts);
-  for(i=0;i<tt_srpt->nr_of_srpts;i++) {
-    printf("Title Track index %i\n", i + 1);
-    printf("\tTitle set number (VTS): %i", 
-           tt_srpt->title[i].title_set_nr);
-    printf("\tVTS_TTN: %i\n", tt_srpt->title[i].vts_ttn);
-    printf("\tNumber of PTTs: %i\n", tt_srpt->title[i].nr_of_ptts);
-    printf("\tNumber of angles: %i\n", 
-           tt_srpt->title[i].nr_of_angles);
-    printf("\tTitle playback type: %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", 
-           tt_srpt->title[i].title_set_sector);
-  }
-}
-
-
-void ifoPrint_VTS_PTT_SRPT(vts_ptt_srpt_t *vts_ptt_srpt) {
-  int i, j;
-  printf(" nr_of_srpts %i last byte %i\n", 
-         vts_ptt_srpt->nr_of_srpts, 
-         vts_ptt_srpt->last_byte);
-  for(i=0;i<vts_ptt_srpt->nr_of_srpts;i++) {
-    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, 
-             vts_ptt_srpt->title[i].ptt[j].pgcn,
-             vts_ptt_srpt->title[i].ptt[j].pgn );
-    }
-  }
-}
-
-
-void ifoPrint_PTL_MAIT(ptl_mait_t *ptl_mait) {
-  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);
-  
-  for(i = 0; i < ptl_mait->nr_of_countries; i++) {
-    
-    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);
-    
-    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;
-  
-  printf("Number of VOBs in this VOBS: %i\n", c_adt->nr_of_vobs);
-  //entries = c_adt->nr_of_vobs;
-  entries = (c_adt->last_byte + 1 - C_ADT_SIZE)/sizeof(cell_adr_t);
-  
-  for(i = 0; i < entries; i++) {
-    printf("VOB ID: %3i, Cell ID: %3i   ", 
-           c_adt->cell_adr_table[i].vob_id, c_adt->cell_adr_table[i].cell_id);
-    printf("Sector (first): 0x%08x   (last): 0x%08x\n",
-           c_adt->cell_adr_table[i].start_sector, 
-           c_adt->cell_adr_table[i].last_sector);
-  }
-}
-
-
-void ifoPrint_VOBU_ADMAP(vobu_admap_t *vobu_admap) {
-  int i, entries;
-  
-  entries = (vobu_admap->last_byte + 1 - VOBU_ADMAP_SIZE)/4;
-  for(i = 0; i < entries; i++) {
-    printf("VOBU %5i  First sector: 0x%08x\n", i + 1,
-           vobu_admap->vobu_start_sectors[i]);
-  }
-}
-
-
-void ifoPrint_PGCIT(pgcit_t *pgcit) {
-  int i;
-  
-  for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
-    printf("\nProgram (PGC): %3i\t", i + 1);
-    printf("PGC Category: Entry id 0x%02x, ", pgcit->pgci_srp[i].entry_id);
-    printf("Parental ID mask 0x%04x\n", pgcit->pgci_srp[i].ptl_id_mask);
-    ifoPrint_PGC(pgcit->pgci_srp[i].pgc);
-  }
-}
-
-
-void ifoPrint_PGCI_UT(pgci_ut_t *pgci_ut) {
-  int i;
-  
-  printf("Number of Menu Language Units (PGCI_LU): %3i\n", pgci_ut->nr_of_lus);
-  for(i = 0; i < pgci_ut->nr_of_lus; i++) {
-    printf("\nMenu Language Code: %c%c (%c)\n",
-           pgci_ut->lu[i].lang_code >> 8,
-           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);
-  }
-}
-
-
-static void ifoPrint_VTS_ATTRIBUTES(vts_attributes_t *vts_attributes) {
-  int i;
-  
-  printf("VTS_CAT Application type: %08x\n", vts_attributes->vts_cat);
- 
-  printf("Video attributes of VTSM_VOBS: ");
-  ifoPrint_video_attributes(&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(&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(&vts_attributes->vtsm_subp_attr);
-    printf("\n");
-  }
-   
-  printf("Video attributes of VTSTT_VOBS: ");
-  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(&vts_attributes->vtstt_audio_attr[i]);
-    printf("\n");
-  }
-  
-  printf("Number of Subpicture streams: %i\n", 
-         vts_attributes->nr_of_vtstt_subp_streams);
-  for(i = 0; i < vts_attributes->nr_of_vtstt_subp_streams; i++) {
-    printf("\tstream %2i attributes: ", i);    
-    ifoPrint_subp_attributes(&vts_attributes->vtstt_subp_attr[i]);
-    printf("\n");
-  }
-}
-
-
-void ifoPrint_VTS_ATRT(vts_atrt_t *vts_atrt) {
-  int i;
-  
-  printf("Number of Video Title Sets: %3i\n", vts_atrt->nr_of_vtss);
-  for(i = 0; i < vts_atrt->nr_of_vtss; i++) {
-    printf("\nVideo Title Set %i\n", i + 1);
-    printf("  offset %d relative to VMG_VTS_ATRT\n", 
-           vts_atrt->vts_atrt_offsets[i]);
-    ifoPrint_VTS_ATTRIBUTES(&vts_atrt->vts[i]);
-  }
-}
-
-
-void ifoPrint(dvd_reader_t *dvd, int title) {
-  ifo_handle_t *ifohandle;
-
-  ifohandle = ifoOpen(dvd, title);
-  if(!ifohandle) {
-    if(dvdread_verbose(dvd) >= 0) {
-      fprintf(stderr, "Can't open info file for title %d\n", title);
-    }
-    return;
-  }
-  
-  
-  if(ifohandle->vmgi_mat) {
-
-    printf("VMG top level\n-------------\n");
-    ifoPrint_VMGI_MAT(ifohandle->vmgi_mat);
-
-    printf("\nFirst Play PGC\n--------------\n");
-    if(ifohandle->first_play_pgc) {
-      ifoPrint_PGC(ifohandle->first_play_pgc);
-    } else {
-      printf("No First Play PGC present\n");
-    }
-
-    printf("\nTitle Track search pointer table\n");
-    printf(  "------------------------------------------------\n");
-    ifoPrint_TT_SRPT(ifohandle->tt_srpt);
-
-    printf("\nMenu PGCI Unit table\n");
-    printf(  "--------------------\n");
-    if(ifohandle->pgci_ut) {
-      ifoPrint_PGCI_UT(ifohandle->pgci_ut);
-    } else {
-      printf("No PGCI Unit table present\n");
-    }
-
-    printf("\nParental Manegment Information table\n");
-    printf(  "------------------------------------\n");
-    if(ifohandle->ptl_mait) {
-      ifoPrint_PTL_MAIT(ifohandle->ptl_mait);
-    } else {
-      printf("No Parental Management Information present\n");
-    }
-
-    printf("\nVideo Title Set Attribute Table\n");
-    printf(  "-------------------------------\n");
-    ifoPrint_VTS_ATRT(ifohandle->vts_atrt);
-    
-    printf("\nText Data Manager Information\n");
-    printf(  "-----------------------------\n");
-    if(ifohandle->txtdt_mgi) {
-      //ifoPrint_TXTDT_MGI(&(vmgi->txtdt_mgi));
-    } else {
-      printf("No Text Data Manager Information present\n");
-    }
-
-    printf("\nMenu Cell Adress table\n");
-    printf(  "-----------------\n");
-    if(ifohandle->menu_c_adt) {
-      ifoPrint_C_ADT(ifohandle->menu_c_adt);
-    } else {
-      printf("No Menu Cell Adress table present\n");
-    }
-
-    printf("\nVideo Manager Menu VOBU address map\n");
-    printf(  "-----------------\n");
-    if(ifohandle->menu_vobu_admap) {
-      ifoPrint_VOBU_ADMAP(ifohandle->menu_vobu_admap);
-    } else {
-      printf("No Menu VOBU address map present\n");   
-    }
-  }
-
-
-  if(ifohandle->vtsi_mat) {
-
-    printf("VTS top level\n-------------\n");
-    ifoPrint_VTSI_MAT(ifohandle->vtsi_mat);
-
-    printf("\nPart of Title Track search pointer table\n");
-    printf(  "----------------------------------------------\n");
-    ifoPrint_VTS_PTT_SRPT(ifohandle->vts_ptt_srpt);
-
-    printf("\nPGCI Unit table\n");
-    printf(  "--------------------\n");
-    ifoPrint_PGCIT(ifohandle->vts_pgcit);
-
-    printf("\nMenu PGCI Unit table\n");
-    printf(  "--------------------\n");
-    if(ifohandle->pgci_ut) {
-      ifoPrint_PGCI_UT(ifohandle->pgci_ut);
-    } else {
-      printf("No Menu PGCI Unit table present\n");
-    }
-    
-    printf("\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");
-    if(ifohandle->menu_c_adt) {
-      ifoPrint_C_ADT(ifohandle->menu_c_adt);
-    } else {
-      printf("No Cell Adress table present\n");
-    }
-
-    printf("\nVideo Title Set Menu VOBU address map\n");
-    printf(  "-----------------\n");
-    if(ifohandle->menu_vobu_admap) {
-      ifoPrint_VOBU_ADMAP(ifohandle->menu_vobu_admap);
-    } else {
-      printf("No Menu VOBU address map present\n");
-    }
-
-    printf("\nCell Adress table\n");
-    printf(  "-----------------\n");
-    ifoPrint_C_ADT(ifohandle->vts_c_adt);
-
-    printf("\nVideo Title Set VOBU address map\n");
-    printf(  "-----------------\n");
-    ifoPrint_VOBU_ADMAP(ifohandle->vts_vobu_admap);
-  } 
-
-  ifoClose(ifohandle);
-}
-
--- a/dvdread/ifo_print.h	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
-#ifndef IFO_PRINT_H_INCLUDED
-#define IFO_PRINT_H_INCLUDED
-
-/*
- * Copyright (C) 2000, 2001 Björn Englund <d4bjorn@dtek.chalmers.se>,
- *                          Håkan Hjort <d95hjort@dtek.chalmers.se>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include <dvdread/ifo_types.h>
-#include <dvdread/dvd_reader.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * This file provides example functions for printing information about the IFO
- * file to stdout.
- */
-
-/**
- * Print the complete parsing information for the given file.
- */
-
-/* ifoPrint(dvd, title); */
-void ifoPrint(dvd_reader_t *, int);
-
-void ifoPrint_VMGI_MAT(vmgi_mat_t *);
-void ifoPrint_VTSI_MAT(vtsi_mat_t *);
-
-void ifoPrint_PTL_MAIT(ptl_mait_t *);
-void ifoPrint_VTS_ATRT(vts_atrt_t *);
-void ifoPrint_TT_SRPT(tt_srpt_t *);
-void ifoPrint_VTS_PTT_SRPT(vts_ptt_srpt_t *);
-void ifoPrint_PGC(pgc_t *);
-void ifoPrint_PGCIT(pgcit_t *);
-void ifoPrint_PGCI_UT(pgci_ut_t *);
-void ifoPrint_VTS_TMAPT(vts_tmapt_t *);
-void ifoPrint_C_ADT(c_adt_t *);
-void ifoPrint_VOBU_ADMAP(vobu_admap_t *);
-
-#ifdef __cplusplus
-};
-#endif
-#endif /* IFO_PRINT_H_INCLUDED */
--- a/dvdread/ifo_read.c	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2185 +0,0 @@
-/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
-/*
- * Copyright (C) 2000, 2001, 2002, 2003
- *               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 changelog at http://svn.mplayerhq.hu/mplayer/trunk/
- * $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
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "config.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#if defined(HAVE_INTTYPES_H)
-#include <inttypes.h>
-#elif defined(HAVE_STDINT_H)
-#include <stdint.h>
-#endif
-
-#include <string.h>
-#include <errno.h>
-
-#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;                                                  \
-    fprintf(stderr, "*** Zero check failed in %s:%i\n    for %s = 0x",  \
-            __FILE__, __LINE__, # arg );                                \
-    for(i_CZ = 0; i_CZ < sizeof(arg); i_CZ++)                           \
-      fprintf(stderr, "%02x", *((uint8_t *)&arg + i_CZ));               \
-    fprintf(stderr, "\n");                                              \
-  }
-static const uint8_t my_friendly_zeros[2048];
-#else
-#define CHECK_ZERO0(arg) (void)(arg)
-#define CHECK_ZERO(arg) (void)(arg)
-#endif
-
-
-/* Prototypes for internal functions */
-static int ifoRead_VMG(ifo_handle_t *ifofile);
-static int ifoRead_VTS(ifo_handle_t *ifofile);
-static int ifoRead_PGC(ifo_handle_t *ifofile, pgc_t *pgc, unsigned int offset);
-static int ifoRead_PGC_COMMAND_TBL(ifo_handle_t *ifofile, 
-                                   pgc_command_tbl_t *cmd_tbl, 
-                                   unsigned int offset);
-static int ifoRead_PGC_PROGRAM_MAP(ifo_handle_t *ifofile, 
-                                   pgc_program_map_t *program_map, 
-                                   unsigned int nr, unsigned int offset);
-static int ifoRead_CELL_PLAYBACK_TBL(ifo_handle_t *ifofile, 
-                                     cell_playback_t *cell_playback, 
-                                     unsigned int nr, unsigned int offset);
-static int ifoRead_CELL_POSITION_TBL(ifo_handle_t *ifofile, 
-                                     cell_position_t *cell_position, 
-                                     unsigned int nr, unsigned int offset);
-static int ifoRead_VTS_ATTRIBUTES(ifo_handle_t *ifofile, 
-                                  vts_attributes_t *vts_attributes, 
-                                  unsigned int offset);
-static int ifoRead_C_ADT_internal(ifo_handle_t *ifofile, c_adt_t *c_adt, 
-                                  unsigned int sector);
-static int ifoRead_VOBU_ADMAP_internal(ifo_handle_t *ifofile, 
-                                       vobu_admap_t *vobu_admap, 
-                                       unsigned int sector);
-static int ifoRead_PGCIT_internal(ifo_handle_t *ifofile, pgcit_t *pgcit, 
-                                  unsigned int offset);
-
-static void ifoFree_PGC(pgc_t *pgc);
-static void ifoFree_PGC_COMMAND_TBL(pgc_command_tbl_t *cmd_tbl);
-static void ifoFree_PGCIT_internal(pgcit_t *pgcit);
-
-static ifo_handle_t *ifoOpen_File(ifo_handle_t *ifofile, int title, 
-                                  char *suffix);
-static ifo_handle_t *ifoOpenVMGI_File(ifo_handle_t *ifofile, char *suffix);
-static ifo_handle_t *ifoOpenVTSI_File(ifo_handle_t *ifofile, int title,
-                                      char *suffix);
-
-static inline int DVDFileSeek_( dvd_file_t *dvd_file, uint32_t offset ) {
-  return (DVDFileSeek(dvd_file, (int)offset) == (int)offset);
-}
-
-
-ifo_handle_t *ifoOpen(dvd_reader_t *dvd, int title) {
-  ifo_handle_t *ifofile;
-
-  ifofile = malloc(sizeof(ifo_handle_t));
-  if(!ifofile)
-    return NULL;
-
-  memset(ifofile, 0, sizeof(ifo_handle_t));
-
-  ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_FILE);
-  if(!ifoOpen_File(ifofile, title, "IFO")) {
-    if(title) {
-      if(dvdread_verbose(dvd) >= 1) {
-        fprintf(stderr, "libdvdread: Can't open file VTS_%02d_0.%s.\n", 
-                title, "IFO");
-      }
-    } else {
-      if(dvdread_verbose(dvd) >= 1) {
-        fprintf(stderr, "libdvdread: Can't open file VIDEO_TS.%s.\n", "IFO");
-      }
-    }
-    /* lower functions free the pointer, reallocate */
-    ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
-    if(!ifofile)
-      return NULL;
-
-    memset(ifofile, 0, sizeof(ifo_handle_t));
-
-    ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_BACKUP_FILE);
-    if(!ifoOpen_File(ifofile, title, "BUP")) {
-      if(title) {
-        if(dvdread_verbose(dvd) >= 1) {
-          fprintf(stderr, "libdvdread: Can't open file VTS_%02d_0.%s.\n", 
-                  title, "BUP");
-        }
-      } else {
-        if(dvdread_verbose(dvd) >= 1) {
-          fprintf(stderr, "libdvdread: Can't open file VIDEO_TS.%s.\n", "BUP");
-        }
-      }
-      return NULL;
-    }
-  }
-  return ifofile;
-}
-
-static ifo_handle_t *ifoOpen_File(ifo_handle_t *ifofile, int title, 
-                                  char *suffix) {
-  if(!ifofile->file) {
-    free(ifofile);
-    return NULL;
-  }
-
-  /* First check if this is a VMGI file. */
-  if(ifoRead_VMG(ifofile)) {
-
-    /* These are both mandatory. */
-    if(!ifoRead_FP_PGC(ifofile) || !ifoRead_TT_SRPT(ifofile)) {
-      if(dvdread_verbose(device_of_file(ifofile->file)) >= 0) {
-        fprintf(stderr, "libdvdread: Invalid main menu IFO (VIDEO_TS.%s).\n",
-                suffix);
-      }
-      ifoClose(ifofile);
-      return NULL;
-    }
-
-    ifoRead_PGCI_UT(ifofile);
-    ifoRead_PTL_MAIT(ifofile);
-
-    /* This is also mandatory. */
-    if(!ifoRead_VTS_ATRT(ifofile)) {
-      if(dvdread_verbose(device_of_file(ifofile->file)) >= 0) {
-        fprintf(stderr, "libdvdread: Invalid main menu IFO (VIDEO_TS.%s).\n",
-                suffix);
-      }
-      ifoClose(ifofile);
-      return NULL;
-    }
-
-    ifoRead_TXTDT_MGI(ifofile);
-    ifoRead_C_ADT(ifofile);
-    ifoRead_VOBU_ADMAP(ifofile);
-
-    return ifofile;
-  }
-
-  if(ifoRead_VTS(ifofile)) {
-
-    if(!ifoRead_VTS_PTT_SRPT(ifofile) || !ifoRead_PGCIT(ifofile)) {
-      if(dvdread_verbose(device_of_file(ifofile->file)) >= 0) {
-        fprintf(stderr, "libdvdread: Invalid title IFO (VTS_%02d_0.%s).\n",
-                title, suffix);
-      }
-      ifoClose(ifofile);
-      return NULL;
-    }
-
-    ifoRead_PGCI_UT(ifofile);
-    ifoRead_VTS_TMAPT(ifofile);
-    ifoRead_C_ADT(ifofile);
-    ifoRead_VOBU_ADMAP(ifofile);
-
-    if(!ifoRead_TITLE_C_ADT(ifofile) || !ifoRead_TITLE_VOBU_ADMAP(ifofile)) {
-      if(dvdread_verbose(device_of_file(ifofile->file)) >= 0) {
-        fprintf(stderr, "libdvdread: Invalid title IFO (VTS_%02d_0.%s).\n",
-                title, suffix);
-      }
-      ifoClose(ifofile);
-      return NULL;
-    }
-
-    return ifofile;
-  }
-
-  if(title) {
-    if(dvdread_verbose(device_of_file(ifofile->file)) >= 0) {
-      fprintf(stderr, "libdvdread: Invalid IFO for title %d (VTS_%02d_0.%s).\n",
-              title, title, suffix);
-    }
-  } else {
-    if(dvdread_verbose(device_of_file(ifofile->file)) >= 0) {
-      fprintf(stderr, "libdvdread: Invalid IFO for VMGM (VIDEO_TS.%s).\n", 
-              suffix);
-    }
-  }
-  ifoClose(ifofile);
-  return NULL;
-}
-
-
-ifo_handle_t *ifoOpenVMGI(dvd_reader_t *dvd) {
-  ifo_handle_t *ifofile;
-
-  ifofile = malloc(sizeof(ifo_handle_t));
-  if(!ifofile)
-    return NULL;
-
-  memset(ifofile, 0, sizeof(ifo_handle_t));
-
-  ifofile->file = DVDOpenFile(dvd, 0, DVD_READ_INFO_FILE);
-  if(!ifoOpenVMGI_File(ifofile, "IFO")) {
-    if(dvdread_verbose(dvd) >= 1) {
-      fprintf(stderr, "libdvdread: Can't open file VIDEO_TS.IFO: %s\n",
-              strerror(errno));
-    }
-
-    /* lower functions free the pointer, reallocate */
-    ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
-    if(!ifofile)
-      return NULL;
-
-    memset(ifofile, 0, sizeof(ifo_handle_t));
-
-    ifofile->file = DVDOpenFile(dvd, 0, DVD_READ_INFO_BACKUP_FILE);
-    if(!ifoOpenVMGI_File(ifofile, "BUP"))
-      if(dvdread_verbose(dvd) >= 1) {
-        fprintf(stderr, "libdvdread: Can't open file VIDEO_TS.BUP: %s\n",
-                strerror(errno));
-      }
-      return NULL;
-  }
-  return ifofile;
-}
-
-static ifo_handle_t *ifoOpenVMGI_File(ifo_handle_t *ifofile, char *suffix) {
-  if(!ifofile->file) {
-    free(ifofile);
-    return NULL;
-  }
-
-  if(ifoRead_VMG(ifofile))
-    return ifofile;
-
-  if(dvdread_verbose(device_of_file(ifofile->file)) >= 0) {
-    fprintf(stderr, "libdvdread: Invalid main menu IFO (VIDEO_TS.%s).\n", 
-            suffix);
-  }
-  ifoClose(ifofile);
-  return NULL;
-}
-
-
-ifo_handle_t *ifoOpenVTSI(dvd_reader_t *dvd, int title) {
-  ifo_handle_t *ifofile;
-  
-  ifofile = malloc(sizeof(ifo_handle_t));
-  if(!ifofile)
-    return NULL;
-
-  memset(ifofile, 0, sizeof(ifo_handle_t));
-  
-  if(title <= 0 || title > 99) {
-    if(dvdread_verbose(dvd) >= 0) {
-      fprintf(stderr, "libdvdread: ifoOpenVTSI invalid title (%d).\n", title);
-    }
-    free(ifofile);
-    errno = EINVAL;
-    return NULL;
-  }
-    
-  ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_FILE);
-  if(!ifoOpenVTSI_File(ifofile, title, "IFO")) {
-    if(dvdread_verbose(dvd) >= 1) {
-      fprintf(stderr, "libdvdread: Can't open file VTS_%02d_0.%s.\n", title, "IFO");
-    }
-    /* lower functions free the pointer, reallocate */
-    ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
-    if(!ifofile)
-      return NULL;
-
-    memset(ifofile, 0, sizeof(ifo_handle_t));
-
-    ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_BACKUP_FILE);
-    if(!ifoOpenVTSI_File(ifofile, title, "BUP"))
-      if(dvdread_verbose(dvd) >= 1) {
-        fprintf(stderr, "libdvdread: Can't open file VTS_%02d_0.%s.\n", title, "BUP");
-      }
-      return NULL;
-  }
-  return ifofile;
-}
-
-static ifo_handle_t *ifoOpenVTSI_File(ifo_handle_t* ifofile, int title, char *suffix) {
-  if(!ifofile->file) {
-    free(ifofile);
-    return NULL;
-  }
-
-  ifoRead_VTS(ifofile);
-  if(ifofile->vtsi_mat)
-    return ifofile;
-
-  if(dvdread_verbose(device_of_file(ifofile->file)) >= 0) {
-    fprintf(stderr, "libdvdread: Invalid IFO for title %d (VTS_%02d_0.%s).\n",
-            title, title, suffix);
-  }
-  ifoClose(ifofile);
-  return NULL;
-}
-
-
-void ifoClose(ifo_handle_t *ifofile) {
-  if(!ifofile)
-    return;
-  
-  ifoFree_VOBU_ADMAP(ifofile);
-  ifoFree_TITLE_VOBU_ADMAP(ifofile);
-  ifoFree_C_ADT(ifofile);
-  ifoFree_TITLE_C_ADT(ifofile);
-  ifoFree_TXTDT_MGI(ifofile);
-  ifoFree_VTS_ATRT(ifofile);
-  ifoFree_PTL_MAIT(ifofile);
-  ifoFree_PGCI_UT(ifofile);
-  ifoFree_TT_SRPT(ifofile);
-  ifoFree_FP_PGC(ifofile);
-  ifoFree_PGCIT(ifofile);
-  ifoFree_VTS_PTT_SRPT(ifofile);
-  ifoFree_VTS_TMAPT(ifofile);
-
-  if(ifofile->vmgi_mat)
-    free(ifofile->vmgi_mat);
-
-  if(ifofile->vtsi_mat)
-    free(ifofile->vtsi_mat);
-
-  DVDCloseFile(ifofile->file);
-  ifofile->file = 0;
-  free(ifofile);
-  ifofile = 0;
-}
-
-
-static int ifoRead_VMG(ifo_handle_t *ifofile) {
-  vmgi_mat_t *vmgi_mat;
-
-  vmgi_mat = malloc(sizeof(vmgi_mat_t));
-  if(!vmgi_mat)
-    return 0;
-
-  ifofile->vmgi_mat = vmgi_mat;
-
-  if(!DVDFileSeek_(ifofile->file, 0)) {
-    free(ifofile->vmgi_mat);
-    ifofile->vmgi_mat = 0;
-    return 0;
-  }
-
-  if(!DVDReadBytes(ifofile->file, vmgi_mat, sizeof(vmgi_mat_t))) {
-    free(ifofile->vmgi_mat);
-    ifofile->vmgi_mat = 0;
-    return 0;
-  }
-
-  if(strncmp("DVDVIDEO-VMG", vmgi_mat->vmg_identifier, 12) != 0) {
-    free(ifofile->vmgi_mat);
-    ifofile->vmgi_mat = 0;
-    return 0;
-  }
-  
-  B2N_32(vmgi_mat->vmg_last_sector);
-  B2N_32(vmgi_mat->vmgi_last_sector);
-  B2N_32(vmgi_mat->vmg_category);
-  B2N_16(vmgi_mat->vmg_nr_of_volumes);
-  B2N_16(vmgi_mat->vmg_this_volume_nr);
-  B2N_16(vmgi_mat->vmg_nr_of_title_sets);
-  B2N_64(vmgi_mat->vmg_pos_code);
-  B2N_32(vmgi_mat->vmgi_last_byte);
-  B2N_32(vmgi_mat->first_play_pgc);
-  B2N_32(vmgi_mat->vmgm_vobs);
-  B2N_32(vmgi_mat->tt_srpt);
-  B2N_32(vmgi_mat->vmgm_pgci_ut);
-  B2N_32(vmgi_mat->ptl_mait);
-  B2N_32(vmgi_mat->vts_atrt);
-  B2N_32(vmgi_mat->txtdt_mgi);
-  B2N_32(vmgi_mat->vmgm_c_adt);
-  B2N_32(vmgi_mat->vmgm_vobu_admap);
-  B2N_16(vmgi_mat->vmgm_audio_attr.lang_code);
-  B2N_16(vmgi_mat->vmgm_subp_attr.lang_code);
-
-
-  CHECK_ZERO(vmgi_mat->zero_1);
-  CHECK_ZERO(vmgi_mat->zero_2);
-  CHECK_ZERO(vmgi_mat->zero_3);
-  CHECK_ZERO(vmgi_mat->zero_4);
-  CHECK_ZERO(vmgi_mat->zero_5);
-  CHECK_ZERO(vmgi_mat->zero_6);
-  CHECK_ZERO(vmgi_mat->zero_7);
-  CHECK_ZERO(vmgi_mat->zero_8);
-  CHECK_ZERO(vmgi_mat->zero_9);
-  CHECK_ZERO(vmgi_mat->zero_10);  
-  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 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));
-  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);
-
-  CHECK_VALUE(vmgi_mat->nr_of_vmgm_audio_streams <= 1);
-  CHECK_VALUE(vmgi_mat->nr_of_vmgm_subp_streams <= 1);
-
-  return 1;
-}
-
-
-static int ifoRead_VTS(ifo_handle_t *ifofile) {
-  vtsi_mat_t *vtsi_mat;
-  int i;
-
-  vtsi_mat = malloc(sizeof(vtsi_mat_t));
-  if(!vtsi_mat)
-    return 0;
-  
-  ifofile->vtsi_mat = vtsi_mat;
-
-  if(!DVDFileSeek_(ifofile->file, 0)) {
-    free(ifofile->vtsi_mat);
-    ifofile->vtsi_mat = 0;
-    return 0;
-  }
-
-  if(!(DVDReadBytes(ifofile->file, vtsi_mat, sizeof(vtsi_mat_t)))) {
-    free(ifofile->vtsi_mat);
-    ifofile->vtsi_mat = 0;
-    return 0;
-  }
-
-  if(strncmp("DVDVIDEO-VTS", vtsi_mat->vts_identifier, 12) != 0) {
-    free(ifofile->vtsi_mat);
-    ifofile->vtsi_mat = 0;
-    return 0;
-  }
-
-  B2N_32(vtsi_mat->vts_last_sector);
-  B2N_32(vtsi_mat->vtsi_last_sector);
-  B2N_32(vtsi_mat->vts_category);
-  B2N_32(vtsi_mat->vtsi_last_byte);
-  B2N_32(vtsi_mat->vtsm_vobs);
-  B2N_32(vtsi_mat->vtstt_vobs);
-  B2N_32(vtsi_mat->vts_ptt_srpt);
-  B2N_32(vtsi_mat->vts_pgcit);
-  B2N_32(vtsi_mat->vtsm_pgci_ut);
-  B2N_32(vtsi_mat->vts_tmapt);
-  B2N_32(vtsi_mat->vtsm_c_adt);
-  B2N_32(vtsi_mat->vtsm_vobu_admap);
-  B2N_32(vtsi_mat->vts_c_adt);
-  B2N_32(vtsi_mat->vts_vobu_admap);
-  B2N_16(vtsi_mat->vtsm_audio_attr.lang_code);
-  B2N_16(vtsi_mat->vtsm_subp_attr.lang_code);
-  for(i = 0; i < 8; i++)
-    B2N_16(vtsi_mat->vts_audio_attr[i].lang_code);
-  for(i = 0; i < 32; i++)
-    B2N_16(vtsi_mat->vts_subp_attr[i].lang_code);
-
-
-  CHECK_ZERO(vtsi_mat->zero_1);
-  CHECK_ZERO(vtsi_mat->zero_2);
-  CHECK_ZERO(vtsi_mat->zero_3);
-  CHECK_ZERO(vtsi_mat->zero_4);
-  CHECK_ZERO(vtsi_mat->zero_5);
-  CHECK_ZERO(vtsi_mat->zero_6);
-  CHECK_ZERO(vtsi_mat->zero_7);
-  CHECK_ZERO(vtsi_mat->zero_8);
-  CHECK_ZERO(vtsi_mat->zero_9);
-  CHECK_ZERO(vtsi_mat->zero_10);
-  CHECK_ZERO(vtsi_mat->zero_11);
-  CHECK_ZERO(vtsi_mat->zero_12);
-  CHECK_ZERO(vtsi_mat->zero_13);
-  CHECK_ZERO(vtsi_mat->zero_14);
-  CHECK_ZERO(vtsi_mat->zero_15);
-  CHECK_ZERO(vtsi_mat->zero_16);
-  CHECK_ZERO(vtsi_mat->zero_17);
-  CHECK_ZERO(vtsi_mat->zero_18);
-  CHECK_ZERO(vtsi_mat->zero_19);
-  CHECK_ZERO(vtsi_mat->zero_20);
-  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));
-  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));
-  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);
-  
-  CHECK_VALUE(vtsi_mat->nr_of_vtsm_audio_streams <= 1);
-  CHECK_VALUE(vtsi_mat->nr_of_vtsm_subp_streams <= 1);
-
-  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]);
-
-  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;
-}
-
-
-static int ifoRead_PGC_COMMAND_TBL(ifo_handle_t *ifofile, 
-                                   pgc_command_tbl_t *cmd_tbl, 
-                                   unsigned int offset) {
-  unsigned int total;
-
-  memset(cmd_tbl, 0, sizeof(pgc_command_tbl_t));
-
-  if(!DVDFileSeek_(ifofile->file, offset))
-    return 0;
-
-  if(!(DVDReadBytes(ifofile->file, cmd_tbl, PGC_COMMAND_TBL_SIZE)))
-    return 0;
-
-  B2N_16(cmd_tbl->nr_of_pre);
-  B2N_16(cmd_tbl->nr_of_post);
-  B2N_16(cmd_tbl->nr_of_cell);
-  B2N_16(cmd_tbl->last_byte);
-  
-  total = cmd_tbl->nr_of_pre + cmd_tbl->nr_of_post + cmd_tbl->nr_of_cell;
-  CHECK_VALUE(PGC_COMMAND_TBL_SIZE + total * COMMAND_DATA_SIZE 
-              <= cmd_tbl->last_byte + 1U);
-  CHECK_VALUE(total <= 255);
-
-  if(cmd_tbl->nr_of_pre != 0) {
-    unsigned int pre_cmds_size  = cmd_tbl->nr_of_pre * COMMAND_DATA_SIZE;
-    cmd_tbl->pre_cmds = malloc(pre_cmds_size);
-    if(!cmd_tbl->pre_cmds)
-      return 0;
-
-    if(!(DVDReadBytes(ifofile->file, cmd_tbl->pre_cmds, pre_cmds_size))) {
-      free(cmd_tbl->pre_cmds);
-      return 0;
-    }
-  }
-
-  if(cmd_tbl->nr_of_post != 0) {
-    unsigned int post_cmds_size = cmd_tbl->nr_of_post * COMMAND_DATA_SIZE;
-    cmd_tbl->post_cmds = malloc(post_cmds_size);
-    if(!cmd_tbl->post_cmds) {
-      if(cmd_tbl->pre_cmds) 
-        free(cmd_tbl->pre_cmds);
-      return 0;
-    }
-    if(!(DVDReadBytes(ifofile->file, cmd_tbl->post_cmds, post_cmds_size))) {
-      if(cmd_tbl->pre_cmds) 
-        free(cmd_tbl->pre_cmds);
-      free(cmd_tbl->post_cmds);
-      return 0;
-    }
-  }
-
-  if(cmd_tbl->nr_of_cell != 0) {
-    unsigned int cell_cmds_size = cmd_tbl->nr_of_cell * COMMAND_DATA_SIZE;
-    cmd_tbl->cell_cmds = malloc(cell_cmds_size);
-    if(!cmd_tbl->cell_cmds) {
-      if(cmd_tbl->pre_cmds)
-        free(cmd_tbl->pre_cmds);
-      if(cmd_tbl->post_cmds)
-        free(cmd_tbl->post_cmds);
-      return 0;
-    }
-    if(!(DVDReadBytes(ifofile->file, cmd_tbl->cell_cmds, cell_cmds_size))) {
-      if(cmd_tbl->pre_cmds) 
-        free(cmd_tbl->pre_cmds);
-      if(cmd_tbl->post_cmds) 
-        free(cmd_tbl->post_cmds);
-      free(cmd_tbl->cell_cmds);
-      return 0;
-    }
-  }
-  
-  /* 
-   * Make a run over all the commands and see that we can interpret them all?
-   */
-  return 1;
-}
-
-
-static void ifoFree_PGC_COMMAND_TBL(pgc_command_tbl_t *cmd_tbl) {
-  if(cmd_tbl) {
-    if(cmd_tbl->nr_of_pre && cmd_tbl->pre_cmds)
-      free(cmd_tbl->pre_cmds);
-    if(cmd_tbl->nr_of_post && cmd_tbl->post_cmds)
-      free(cmd_tbl->post_cmds);
-    if(cmd_tbl->nr_of_cell && cmd_tbl->cell_cmds)
-      free(cmd_tbl->cell_cmds);
-    free(cmd_tbl);
-  }
-}
-
-static int ifoRead_PGC_PROGRAM_MAP(ifo_handle_t *ifofile, 
-                                   pgc_program_map_t *program_map, 
-                                   unsigned int nr, unsigned int offset) {
-  unsigned int size = nr * sizeof(pgc_program_map_t);
-
-  if(!DVDFileSeek_(ifofile->file, offset))
-    return 0;
- 
-  if(!(DVDReadBytes(ifofile->file, program_map, size)))
-    return 0;
-
-  return 1;
-}
-
-static int ifoRead_CELL_PLAYBACK_TBL(ifo_handle_t *ifofile, 
-                                     cell_playback_t *cell_playback,
-                                     unsigned int nr, unsigned int offset) {
-  unsigned int i;
-  unsigned int size = nr * sizeof(cell_playback_t);
-
-  if(!DVDFileSeek_(ifofile->file, offset))
-    return 0;
-
-  if(!(DVDReadBytes(ifofile->file, cell_playback, size)))
-    return 0;
-
-  for(i = 0; i < nr; i++) {
-    B2N_32(cell_playback[i].first_sector);
-    B2N_32(cell_playback[i].first_ilvu_end_sector);
-    B2N_32(cell_playback[i].last_vobu_start_sector);
-    B2N_32(cell_playback[i].last_sector);
-    
-    /* Changed < to <= because this was false in the movie 'Pi'. */
-    CHECK_VALUE(cell_playback[i].last_vobu_start_sector <= 
-                cell_playback[i].last_sector);
-    CHECK_VALUE(cell_playback[i].first_sector <= 
-                cell_playback[i].last_vobu_start_sector);
-  }
-
-  return 1;
-}
-
-
-static int ifoRead_CELL_POSITION_TBL(ifo_handle_t *ifofile, 
-                                     cell_position_t *cell_position, 
-                                     unsigned int nr, unsigned int offset) {
-  unsigned int i;
-  unsigned int size = nr * sizeof(cell_position_t);
-
-  if(!DVDFileSeek_(ifofile->file, offset))
-    return 0;
-
-  if(!(DVDReadBytes(ifofile->file, cell_position, size)))
-    return 0;
-
-  for(i = 0; i < nr; i++) {
-    B2N_16(cell_position[i].vob_id_nr);
-    CHECK_ZERO(cell_position[i].zero_1);
-  }
-
-  return 1;
-}
-
-static int ifoRead_PGC(ifo_handle_t *ifofile, pgc_t *pgc, unsigned int offset) {
-  unsigned int i;
-
-  if(!DVDFileSeek_(ifofile->file, offset))
-    return 0;
- 
-  if(!(DVDReadBytes(ifofile->file, pgc, PGC_SIZE)))
-    return 0;
-
-  B2N_16(pgc->next_pgc_nr);
-  B2N_16(pgc->prev_pgc_nr);
-  B2N_16(pgc->goup_pgc_nr);
-  B2N_16(pgc->command_tbl_offset);
-  B2N_16(pgc->program_map_offset);
-  B2N_16(pgc->cell_playback_offset);
-  B2N_16(pgc->cell_position_offset);
-
-  for(i = 0; i < 8; i++)
-    B2N_16(pgc->audio_control[i]);
-  for(i = 0; i < 32; i++)
-    B2N_32(pgc->subp_control[i]);
-  for(i = 0; i < 16; i++)
-    B2N_32(pgc->palette[i]);
-  
-  CHECK_ZERO(pgc->zero_1);
-  CHECK_VALUE(pgc->nr_of_programs <= pgc->nr_of_cells);
-
-  /* verify time (look at print_time) */
-  for(i = 0; i < 8; i++)
-    if(!pgc->audio_control[i] & 0x8000)
-      CHECK_ZERO(pgc->audio_control[i]);
-  for(i = 0; i < 32; i++)
-    if(!pgc->subp_control[i] & 0x80000000)
-      CHECK_ZERO(pgc->subp_control[i]);
-  
-  /* Check that time is 0:0:0:0 also if nr_of_programs == 0 */
-  if(pgc->nr_of_programs == 0) {
-    CHECK_ZERO(pgc->still_time);
-    CHECK_ZERO(pgc->pg_playback_mode); // ??
-    CHECK_VALUE(pgc->program_map_offset == 0);
-    CHECK_VALUE(pgc->cell_playback_offset == 0);
-    CHECK_VALUE(pgc->cell_position_offset == 0);
-  } else {
-    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) {
-    pgc->command_tbl = malloc(sizeof(pgc_command_tbl_t));
-    if(!pgc->command_tbl)
-      return 0;
-
-    if(!ifoRead_PGC_COMMAND_TBL(ifofile, pgc->command_tbl, 
-                                offset + pgc->command_tbl_offset)) {
-      free(pgc->command_tbl);
-      return 0;
-    }
-  } else {
-    pgc->command_tbl = NULL;
-  }
-  
-  if(pgc->program_map_offset != 0) {
-    if(pgc->nr_of_programs != 0) {
-
-    pgc->program_map = malloc(pgc->nr_of_programs * sizeof(pgc_program_map_t));
-    if(!pgc->program_map) {
-      ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
-      return 0;
-    }
-    if(!ifoRead_PGC_PROGRAM_MAP(ifofile, pgc->program_map,pgc->nr_of_programs,
-                                offset + pgc->program_map_offset)) {
-      ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
-      free(pgc->program_map);
-      return 0;
-    }
-    } else {
-      pgc->program_map = NULL;
-    }
-  } else {
-    pgc->program_map = NULL;
-  }
-  
-  if(pgc->cell_playback_offset != 0) {
-    if(pgc->nr_of_cells != 0) {
-
-    pgc->cell_playback = malloc(pgc->nr_of_cells * sizeof(cell_playback_t));
-    if(!pgc->cell_playback) {
-      ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
-      if(pgc->program_map)
-        free(pgc->program_map);
-      return 0;
-    }
-    if(!ifoRead_CELL_PLAYBACK_TBL(ifofile, pgc->cell_playback, 
-                                  pgc->nr_of_cells,
-                                  offset + pgc->cell_playback_offset)) {
-      ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
-      if(pgc->program_map)
-        free(pgc->program_map);
-      free(pgc->cell_playback);
-      return 0;
-    }
-    } else {
-      pgc->cell_playback = NULL;
-    }
-  } else {
-    pgc->cell_playback = NULL;
-  }
-  
-  if(pgc->cell_position_offset != 0) {
-    if(pgc->nr_of_cells != 0) {
-
-    pgc->cell_position = malloc(pgc->nr_of_cells * sizeof(cell_position_t));
-    if(!pgc->cell_position) {
-      ifoFree_PGC(pgc);
-      return 0;
-    }
-    if(!ifoRead_CELL_POSITION_TBL(ifofile, pgc->cell_position, 
-                                  pgc->nr_of_cells,
-                                  offset + pgc->cell_position_offset)) {
-      ifoFree_PGC(pgc);
-      return 0;
-    }
-    } else {
-      pgc->cell_position = NULL;
-    }
-  } else {
-    pgc->cell_position = NULL;
-  }
-
-  return 1;
-}
-
-int ifoRead_FP_PGC(ifo_handle_t *ifofile) {
-
-  if(!ifofile)
-    return 0;
-
-  if(!ifofile->vmgi_mat)
-    return 0;
-  
-  /* It seems that first_play_pgc is optional after all. */
-  ifofile->first_play_pgc = 0;
-  if(ifofile->vmgi_mat->first_play_pgc == 0)
-    return 1;
-  
-  ifofile->first_play_pgc = malloc(sizeof(pgc_t));
-  if(!ifofile->first_play_pgc)
-    return 0;
-  
-  if(!ifoRead_PGC(ifofile, ifofile->first_play_pgc, 
-                  ifofile->vmgi_mat->first_play_pgc)) {
-    free(ifofile->first_play_pgc);
-    ifofile->first_play_pgc = 0;
-    return 0;
-  }
-
-  return 1;
-}
-
-static void ifoFree_PGC(pgc_t *pgc) {
-  if(pgc) {
-    ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
-    if(pgc->program_map)
-      free(pgc->program_map);
-    if(pgc->cell_playback)
-      free(pgc->cell_playback);
-    if(pgc->cell_position)
-      free(pgc->cell_position);
-  }
-}
-
-void ifoFree_FP_PGC(ifo_handle_t *ifofile) {
-  if(!ifofile)
-    return;
-  
-  if(ifofile->first_play_pgc) {
-    ifoFree_PGC(ifofile->first_play_pgc);
-    free(ifofile->first_play_pgc);
-    ifofile->first_play_pgc = 0;
-  }
-}
-
-
-int ifoRead_TT_SRPT(ifo_handle_t *ifofile) {
-  tt_srpt_t *tt_srpt;
-  int i, info_length;
-
-  if(!ifofile)
-    return 0;
-
-  if(!ifofile->vmgi_mat)
-    return 0;
-
-  if(ifofile->vmgi_mat->tt_srpt == 0) /* mandatory */
-    return 0;
-
-  if(!DVDFileSeek_(ifofile->file, ifofile->vmgi_mat->tt_srpt * DVD_BLOCK_LEN))
-    return 0;
-
-  tt_srpt = malloc(sizeof(tt_srpt_t));
-  if(!tt_srpt)
-    return 0;
-
-  ifofile->tt_srpt = tt_srpt;
-  
-  if(!(DVDReadBytes(ifofile->file, tt_srpt, TT_SRPT_SIZE))) {
-    if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
-      fprintf(stderr, "libdvdread: Unable to read read TT_SRPT.\n");
-    }
-    free(tt_srpt);
-    return 0;
-  }
-
-  B2N_16(tt_srpt->nr_of_srpts);
-  B2N_32(tt_srpt->last_byte);
-  
-  info_length = tt_srpt->last_byte + 1 - TT_SRPT_SIZE;
-
-  tt_srpt->title = malloc(info_length);
-  if(!tt_srpt->title) {
-    free(tt_srpt);
-    ifofile->tt_srpt = 0;
-    return 0;
-  }
-  if(!(DVDReadBytes(ifofile->file, tt_srpt->title, info_length))) {
-    if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
-      fprintf(stderr, "libdvdread: Unable to read read TT_SRPT.\n");
-    }
-    ifoFree_TT_SRPT(ifofile);
-    return 0;
-  }
-
-  for(i =  0; i < tt_srpt->nr_of_srpts; i++) {
-    B2N_16(tt_srpt->title[i].nr_of_ptts);
-    B2N_16(tt_srpt->title[i].parental_id);
-    B2N_32(tt_srpt->title[i].title_set_sector);
-  }
-  
-
-  CHECK_ZERO(tt_srpt->zero_1);
-  CHECK_VALUE(tt_srpt->nr_of_srpts != 0);
-  CHECK_VALUE(tt_srpt->nr_of_srpts < 100); // ??
-  CHECK_VALUE(tt_srpt->nr_of_srpts * sizeof(title_info_t) <= info_length);
-  
-  for(i = 0; i < tt_srpt->nr_of_srpts; i++) {
-    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:
-    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
-#if 0
-  if(memcmp((uint8_t *)tt_srpt->title + 
-            tt_srpt->nr_of_srpts * sizeof(title_info_t), 
-            my_friendly_zeros, 
-            info_length - tt_srpt->nr_of_srpts * sizeof(title_info_t))) {
-    fprintf(stderr, "VMG_PTT_SRPT slack is != 0, ");
-    hexdump((uint8_t *)tt_srpt->title + 
-            tt_srpt->nr_of_srpts * sizeof(title_info_t), 
-            info_length - tt_srpt->nr_of_srpts * sizeof(title_info_t));
-  }
-#endif
-
-  return 1;
-}
-
-
-void ifoFree_TT_SRPT(ifo_handle_t *ifofile) {
-  if(!ifofile)
-    return;
-  
-  if(ifofile->tt_srpt) {
-    free(ifofile->tt_srpt->title);
-    free(ifofile->tt_srpt);
-    ifofile->tt_srpt = 0;
-  }
-}
-
-
-int ifoRead_VTS_PTT_SRPT(ifo_handle_t *ifofile) {
-  vts_ptt_srpt_t *vts_ptt_srpt;
-  int info_length, i, j;
-  uint32_t *data;
-
-  if(!ifofile)
-    return 0;
-  
-  if(!ifofile->vtsi_mat)
-    return 0;
-
-  if(ifofile->vtsi_mat->vts_ptt_srpt == 0) /* mandatory */
-    return 0;
-    
-  if(!DVDFileSeek_(ifofile->file,
-                   ifofile->vtsi_mat->vts_ptt_srpt * DVD_BLOCK_LEN))
-    return 0;
-
-  vts_ptt_srpt = malloc(sizeof(vts_ptt_srpt_t));
-  if(!vts_ptt_srpt)
-    return 0;
-
-  ifofile->vts_ptt_srpt = vts_ptt_srpt;
-
-  if(!(DVDReadBytes(ifofile->file, vts_ptt_srpt, VTS_PTT_SRPT_SIZE))) {
-    if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
-      fprintf(stderr, "libdvdread: Unable to read PTT search table.\n");
-    }
-    free(vts_ptt_srpt);
-    return 0;
-  }
-
-  B2N_16(vts_ptt_srpt->nr_of_srpts);
-  B2N_32(vts_ptt_srpt->last_byte);
-
-  CHECK_ZERO(vts_ptt_srpt->zero_1);
-  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;
-  
-  data = malloc(info_length);
-  if(!data) {
-    free(vts_ptt_srpt);
-    ifofile->vts_ptt_srpt = 0;
-    return 0;
-  }
-  if(!(DVDReadBytes(ifofile->file, data, info_length))) {
-    if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
-      fprintf(stderr, "libdvdread: Unable to read PTT search table.\n");
-    }
-    free(vts_ptt_srpt);
-    free(data);
-    ifofile->vts_ptt_srpt = 0;
-    return 0;
-  }
-
-  for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
-    B2N_32(data[i]);
-    /* assert(data[i] + sizeof(ptt_info_t) <= vts_ptt_srpt->last_byte + 1);
-       Magic Knight Rayearth Daybreak is mastered very strange and has 
-       Titles with 0 PTTs. They all have a data[i] offsets beyond the end of
-       of the vts_ptt_srpt structure. */
-    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) {
-    free(vts_ptt_srpt);
-    free(data);
-    ifofile->vts_ptt_srpt = 0;
-    return 0;
-  }
-  for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
-    int n;
-    if(i < vts_ptt_srpt->nr_of_srpts - 1)
-      n = (data[i+1] - data[i]);
-    else
-      n = (vts_ptt_srpt->last_byte + 1 - data[i]);
-    /* assert(n > 0 && (n % 4) == 0);
-       Magic Knight Rayearth Daybreak is mastered very strange and has 
-       Titles with 0 PTTs. */
-    if(n < 0) n = 0;
-    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));
-    if(!vts_ptt_srpt->title[i].ptt) {
-      for(n = 0; n < i; n++)
-        free(vts_ptt_srpt->title[n].ptt);
-      free(vts_ptt_srpt);
-      free(data);
-      ifofile->vts_ptt_srpt = 0;
-      return 0;
-    }
-    for(j = 0; j < vts_ptt_srpt->title[i].nr_of_ptts; j++) {
-      /* The assert placed here because of Magic Knight Rayearth Daybreak */
-      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);
-    }
-  }
-  
-  for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
-    for(j = 0; j < vts_ptt_srpt->title[i].nr_of_ptts; j++) {
-      B2N_16(vts_ptt_srpt->title[i].ptt[j].pgcn);
-      B2N_16(vts_ptt_srpt->title[i].ptt[j].pgn);
-    }
-  }
-  
-  for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
-    CHECK_VALUE(vts_ptt_srpt->title[i].nr_of_ptts < 1000); // ??
-    for(j = 0; j < vts_ptt_srpt->title[i].nr_of_ptts; j++) {
-      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); // ??
-    }
-  }
-
-  return 1;
-}
-
-
-void ifoFree_VTS_PTT_SRPT(ifo_handle_t *ifofile) {
-  if(!ifofile)
-    return;
-  
-  if(ifofile->vts_ptt_srpt) {
-    int i;
-    for(i = 0; i < ifofile->vts_ptt_srpt->nr_of_srpts; i++)
-      free(ifofile->vts_ptt_srpt->title[i].ptt);
-    free(ifofile->vts_ptt_srpt->ttu_offset);
-    free(ifofile->vts_ptt_srpt->title);
-    free(ifofile->vts_ptt_srpt);
-    ifofile->vts_ptt_srpt = 0;
-  }
-}
-
-
-int ifoRead_PTL_MAIT(ifo_handle_t *ifofile) {
-  ptl_mait_t *ptl_mait;
-  int info_length;
-  unsigned int i, j;
-
-  if(!ifofile)
-    return 0;
-  
-  if(!ifofile->vmgi_mat)
-    return 0;
-  
-  if(ifofile->vmgi_mat->ptl_mait == 0)
-    return 1;
-
-  if(!DVDFileSeek_(ifofile->file, ifofile->vmgi_mat->ptl_mait * DVD_BLOCK_LEN))
-    return 0;
-
-  ptl_mait = malloc(sizeof(ptl_mait_t));
-  if(!ptl_mait)
-    return 0;
-
-  ifofile->ptl_mait = ptl_mait;
-
-  if(!(DVDReadBytes(ifofile->file, ptl_mait, PTL_MAIT_SIZE))) {
-    free(ptl_mait);
-    ifofile->ptl_mait = 0;
-    return 0;
-  }
-
-  B2N_16(ptl_mait->nr_of_countries);
-  B2N_16(ptl_mait->nr_of_vtss);
-  B2N_32(ptl_mait->last_byte);
-  
-  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);
-  
-  info_length = ptl_mait->nr_of_countries * sizeof(ptl_mait_country_t);
-  ptl_mait->countries = malloc(info_length);
-  if(!ptl_mait->countries) {
-    free(ptl_mait);
-    ifofile->ptl_mait = 0;
-    return 0;
-  }
-  
-  for(i = 0; i < ptl_mait->nr_of_countries; i++) {
-    if(!(DVDReadBytes(ifofile->file, &ptl_mait->countries[i], PTL_MAIT_COUNTRY_SIZE))) {
-      if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
-        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++) {
-    B2N_16(ptl_mait->countries[i].country_code);
-    B2N_16(ptl_mait->countries[i].pf_ptl_mai_start_byte);
-  }
-  
-  for(i = 0; i < ptl_mait->nr_of_countries; i++) {
-    CHECK_ZERO(ptl_mait->countries[i].zero_1);
-    CHECK_ZERO(ptl_mait->countries[i].zero_2);    
-    CHECK_VALUE(ptl_mait->countries[i].pf_ptl_mai_start_byte +
-                16U * (ptl_mait->nr_of_vtss + 1) <= ptl_mait->last_byte + 1U);
-  }
-
-  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)) {
-      if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
-        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 = 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))) {
-      if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
-        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 = 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;
-
-  /* Seems to be optional, at least when there are no OneSequencial Titles */
-  if(ifofile->vtsi_mat->vts_tmapt == 0) {
-    ifofile->vts_tmapt = NULL;
-    return 1;
-  }
-  
-  offset = ifofile->vtsi_mat->vts_tmapt * DVD_BLOCK_LEN;
-  
-  if(!DVDFileSeek_(ifofile->file, offset)) 
-    return 0;
-  
-  vts_tmapt = malloc(sizeof(vts_tmapt_t));
-  if(!vts_tmapt)
-    return 0;
-  
-  ifofile->vts_tmapt = vts_tmapt;
-  
-  if(!(DVDReadBytes(ifofile->file, vts_tmapt, VTS_TMAPT_SIZE))) {
-    if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
-      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 = 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))) {
-    if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
-      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 = 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))) {
-      if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
-        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 = 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))) {
-      if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
-        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)
-    return 0;
-
-  if(!ifofile->vtsi_mat)
-    return 0;
-
-  if(ifofile->vtsi_mat->vts_c_adt == 0) /* mandatory */
-    return 0;
-
-  ifofile->vts_c_adt = malloc(sizeof(c_adt_t));
-  if(!ifofile->vts_c_adt)
-    return 0;
-
-  if(!ifoRead_C_ADT_internal(ifofile, ifofile->vts_c_adt, 
-                             ifofile->vtsi_mat->vts_c_adt)) {
-    free(ifofile->vts_c_adt);
-    ifofile->vts_c_adt = 0;
-    return 0;
-  }
-
-  return 1;
-}
-
-int ifoRead_C_ADT(ifo_handle_t *ifofile) {
-  unsigned int sector;
-
-  if(!ifofile)
-    return 0;
-  
-  if(ifofile->vmgi_mat) {
-    if(ifofile->vmgi_mat->vmgm_c_adt == 0)
-      return 1;
-    sector = ifofile->vmgi_mat->vmgm_c_adt;
-  } else if(ifofile->vtsi_mat) {
-    if(ifofile->vtsi_mat->vtsm_c_adt == 0)
-      return 1;
-    sector = ifofile->vtsi_mat->vtsm_c_adt;
-  } else {
-    return 0;
-  }
-  
-  ifofile->menu_c_adt = malloc(sizeof(c_adt_t));
-  if(!ifofile->menu_c_adt)
-    return 0;
-
-  if(!ifoRead_C_ADT_internal(ifofile, ifofile->menu_c_adt, sector)) {
-    free(ifofile->menu_c_adt);
-    ifofile->menu_c_adt = 0;
-    return 0;
-  }
-
-  return 1;
-}
-
-static int ifoRead_C_ADT_internal(ifo_handle_t *ifofile, 
-                                  c_adt_t *c_adt, unsigned int sector) {
-  int i, info_length;
-
-  if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN))
-    return 0;
-
-  if(!(DVDReadBytes(ifofile->file, c_adt, C_ADT_SIZE)))
-    return 0;
-
-  B2N_16(c_adt->nr_of_vobs);
-  B2N_32(c_adt->last_byte);
-  
-  info_length = c_adt->last_byte + 1 - C_ADT_SIZE;
-  
-  CHECK_ZERO(c_adt->zero_1);
-  /* assert(c_adt->nr_of_vobs > 0);  
-     Magic Knight Rayearth Daybreak is mastered very strange and has 
-     Titles with a VOBS that has no cells. */
-  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
-     is to high, they high ones are never referenced though. */
-  if(info_length / sizeof(cell_adr_t) < c_adt->nr_of_vobs) {
-    if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
-      fprintf(stderr, "libdvdread: *C_ADT nr_of_vobs > avaiable info entries\n");
-    }
-    c_adt->nr_of_vobs = info_length / sizeof(cell_adr_t);
-  }
-  
-  c_adt->cell_adr_table = malloc(info_length);
-  if(!c_adt->cell_adr_table)
-    return 0;
-
-  if(info_length && 
-     !(DVDReadBytes(ifofile->file, c_adt->cell_adr_table, info_length))) {
-    free(c_adt->cell_adr_table);
-    return 0;
-  }
-
-  for(i = 0; i < info_length/sizeof(cell_adr_t); i++) {
-    B2N_16(c_adt->cell_adr_table[i].vob_id);
-    B2N_32(c_adt->cell_adr_table[i].start_sector);
-    B2N_32(c_adt->cell_adr_table[i].last_sector);
-
-    CHECK_ZERO(c_adt->cell_adr_table[i].zero_1);
-    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);
-  }
-
-  return 1;
-}
-
-
-static void ifoFree_C_ADT_internal(c_adt_t *c_adt) {
-  if(c_adt) {
-    free(c_adt->cell_adr_table);
-    free(c_adt);
-  }
-}
-
-void ifoFree_C_ADT(ifo_handle_t *ifofile) {
-  if(!ifofile)
-    return;
-  
-  ifoFree_C_ADT_internal(ifofile->menu_c_adt);
-  ifofile->menu_c_adt = 0;
-}
-
-void ifoFree_TITLE_C_ADT(ifo_handle_t *ifofile) {
-  if(!ifofile)
-    return;
-  
-  ifoFree_C_ADT_internal(ifofile->vts_c_adt);
-  ifofile->vts_c_adt = 0;
-}
-
-int ifoRead_TITLE_VOBU_ADMAP(ifo_handle_t *ifofile) {
-  if(!ifofile)
-    return 0;
-
-  if(!ifofile->vtsi_mat)
-    return 0;
-  
-  if(ifofile->vtsi_mat->vts_vobu_admap == 0) /* mandatory */
-    return 0;
-  
-  ifofile->vts_vobu_admap = malloc(sizeof(vobu_admap_t));
-  if(!ifofile->vts_vobu_admap)
-    return 0;
-
-  if(!ifoRead_VOBU_ADMAP_internal(ifofile, ifofile->vts_vobu_admap,
-                                  ifofile->vtsi_mat->vts_vobu_admap)) {
-    free(ifofile->vts_vobu_admap);
-    ifofile->vts_vobu_admap = 0;
-    return 0;
-  }
-
-  return 1;
-}
-
-int ifoRead_VOBU_ADMAP(ifo_handle_t *ifofile) {
-  unsigned int sector;
-
-  if(!ifofile)
-    return 0;
-     
-  if(ifofile->vmgi_mat) {
-    if(ifofile->vmgi_mat->vmgm_vobu_admap == 0)
-      return 1;
-    sector = ifofile->vmgi_mat->vmgm_vobu_admap;
-  } else if(ifofile->vtsi_mat) {
-    if(ifofile->vtsi_mat->vtsm_vobu_admap == 0)
-      return 1;
-    sector = ifofile->vtsi_mat->vtsm_vobu_admap;
-  } else {
-    return 0;
-  }
-  
-  ifofile->menu_vobu_admap = malloc(sizeof(vobu_admap_t));
-  if(!ifofile->menu_vobu_admap)
-    return 0;
-  
-  if(!ifoRead_VOBU_ADMAP_internal(ifofile, ifofile->menu_vobu_admap, sector)) {
-    free(ifofile->menu_vobu_admap);
-    ifofile->menu_vobu_admap = 0;
-    return 0;
-  }
-
-  return 1;
-}
-
-static int ifoRead_VOBU_ADMAP_internal(ifo_handle_t *ifofile, 
-                                       vobu_admap_t *vobu_admap, 
-                                       unsigned int sector) {
-  unsigned int i;
-  int info_length;
-
-  if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN))
-    return 0;
-
-  if(!(DVDReadBytes(ifofile->file, vobu_admap, VOBU_ADMAP_SIZE)))
-    return 0;
-
-  B2N_32(vobu_admap->last_byte);
-  
-  info_length = vobu_admap->last_byte + 1 - VOBU_ADMAP_SIZE;
-  /* assert(info_length > 0);
-     Magic Knight Rayearth Daybreak is mastered very strange and has 
-     Titles with a VOBS that has no VOBUs. */
-  CHECK_VALUE(info_length % sizeof(uint32_t) == 0);
-  
-  vobu_admap->vobu_start_sectors = malloc(info_length);
-  if(!vobu_admap->vobu_start_sectors) {
-    return 0;
-  }
-  if(info_length && 
-     !(DVDReadBytes(ifofile->file, 
-                    vobu_admap->vobu_start_sectors, info_length))) {
-    free(vobu_admap->vobu_start_sectors);
-    return 0;
-  }
-
-  for(i = 0; i < info_length/sizeof(uint32_t); i++)
-    B2N_32(vobu_admap->vobu_start_sectors[i]);
-
-  return 1;
-}
-
-
-static void ifoFree_VOBU_ADMAP_internal(vobu_admap_t *vobu_admap) {
-  if(vobu_admap) {
-    free(vobu_admap->vobu_start_sectors);
-    free(vobu_admap);
-  }
-}
-
-void ifoFree_VOBU_ADMAP(ifo_handle_t *ifofile) {
-  if(!ifofile)
-    return;
-  
-  ifoFree_VOBU_ADMAP_internal(ifofile->menu_vobu_admap);
-  ifofile->menu_vobu_admap = 0;
-}
-
-void ifoFree_TITLE_VOBU_ADMAP(ifo_handle_t *ifofile) {
-  if(!ifofile)
-    return;
-  
-  ifoFree_VOBU_ADMAP_internal(ifofile->vts_vobu_admap);
-  ifofile->vts_vobu_admap = 0;
-}
-
-int ifoRead_PGCIT(ifo_handle_t *ifofile) {
-
-  if(!ifofile)
-    return 0;
-  
-  if(!ifofile->vtsi_mat)
-    return 0;
-  
-  if(ifofile->vtsi_mat->vts_pgcit == 0) /* mandatory */
-    return 0;
-  
-  ifofile->vts_pgcit = malloc(sizeof(pgcit_t));
-  if(!ifofile->vts_pgcit)
-    return 0;
-
-  if(!ifoRead_PGCIT_internal(ifofile, ifofile->vts_pgcit, 
-                             ifofile->vtsi_mat->vts_pgcit * DVD_BLOCK_LEN)) {
-    free(ifofile->vts_pgcit);
-    ifofile->vts_pgcit = 0;
-    return 0;
-  }
-
-  return 1;
-}
-
-static int ifoRead_PGCIT_internal(ifo_handle_t *ifofile, pgcit_t *pgcit, 
-                                  unsigned int offset) {
-  int i, info_length;
-  uint8_t *data, *ptr;
-  
-  if(!DVDFileSeek_(ifofile->file, offset))
-    return 0;
-
-  if(!(DVDReadBytes(ifofile->file, pgcit, PGCIT_SIZE)))
-    return 0;
-
-  B2N_16(pgcit->nr_of_pgci_srp);
-  B2N_32(pgcit->last_byte);
-  
-  CHECK_ZERO(pgcit->zero_1);
-  /* assert(pgcit->nr_of_pgci_srp != 0);
-     Magic Knight Rayearth Daybreak is mastered very strange and has 
-     Titles with 0 PTTs. */
-  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);
-  if(!data)
-    return 0;
-
-  if(info_length && !(DVDReadBytes(ifofile->file, data, info_length))) {
-    free(data);
-    return 0;
-  }
-
-  pgcit->pgci_srp = malloc(pgcit->nr_of_pgci_srp * sizeof(pgci_srp_t));
-  if(!pgcit->pgci_srp) {
-    free(data);
-    return 0;
-  }
-  ptr = data;
-  for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
-    memcpy(&pgcit->pgci_srp[i], ptr, PGCI_SRP_SIZE);
-    ptr += PGCI_SRP_SIZE;
-    B2N_16(pgcit->pgci_srp[i].ptl_id_mask);
-    B2N_32(pgcit->pgci_srp[i].pgc_start_byte);
-    CHECK_VALUE(pgcit->pgci_srp[i].unknown1 == 0);
-  }
-  free(data);
-  
-  for(i = 0; i < pgcit->nr_of_pgci_srp; i++)
-    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));
-    if(!pgcit->pgci_srp[i].pgc) {
-      int j;
-      for(j = 0; j < i; j++) {
-        ifoFree_PGC(pgcit->pgci_srp[j].pgc);
-        free(pgcit->pgci_srp[j].pgc);
-      }
-      free(pgcit->pgci_srp);
-      pgcit->pgci_srp = NULL;
-      return 0;
-    }
-    if(!ifoRead_PGC(ifofile, pgcit->pgci_srp[i].pgc, 
-                    offset + pgcit->pgci_srp[i].pgc_start_byte)) {
-      int j;
-      for(j = 0; j < i; j++) {
-        ifoFree_PGC(pgcit->pgci_srp[j].pgc);
-        free(pgcit->pgci_srp[j].pgc);
-      }
-      free(pgcit->pgci_srp);
-      pgcit->pgci_srp = NULL;
-      return 0;
-    }
-  }
-
-  return 1;
-}
-
-static void ifoFree_PGCIT_internal(pgcit_t *pgcit) {
-  if(pgcit) {
-    int i;
-    for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
-      ifoFree_PGC(pgcit->pgci_srp[i].pgc);
-      free(pgcit->pgci_srp[i].pgc);
-    }
-    free(pgcit->pgci_srp);
-  }
-}
-
-void ifoFree_PGCIT(ifo_handle_t *ifofile) {
-  if(!ifofile)
-    return;
-  
-  if(ifofile->vts_pgcit) {
-    ifoFree_PGCIT_internal(ifofile->vts_pgcit);
-    free(ifofile->vts_pgcit);
-    ifofile->vts_pgcit = 0;
-  }
-}
-
-
-int ifoRead_PGCI_UT(ifo_handle_t *ifofile) {
-  pgci_ut_t *pgci_ut;
-  unsigned int sector;
-  unsigned int i;  
-  int info_length;
-  uint8_t *data, *ptr;
-
-  if(!ifofile)
-    return 0;
-  
-  if(ifofile->vmgi_mat) {
-    if(ifofile->vmgi_mat->vmgm_pgci_ut == 0)
-      return 1;
-    sector = ifofile->vmgi_mat->vmgm_pgci_ut;
-  } else if(ifofile->vtsi_mat) {
-    if(ifofile->vtsi_mat->vtsm_pgci_ut == 0)
-      return 1;
-    sector = ifofile->vtsi_mat->vtsm_pgci_ut;
-  } else {
-    return 0;
-  }
-  
-  ifofile->pgci_ut = malloc(sizeof(pgci_ut_t));
-  if(!ifofile->pgci_ut)
-    return 0;
-  
-  if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN)) {
-    free(ifofile->pgci_ut);
-    ifofile->pgci_ut = 0;
-    return 0;
-  }
-  
-  if(!(DVDReadBytes(ifofile->file, ifofile->pgci_ut, PGCI_UT_SIZE))) {
-    free(ifofile->pgci_ut);
-    ifofile->pgci_ut = 0;
-    return 0;
-  }
-  
-  pgci_ut = ifofile->pgci_ut;
-  
-  B2N_16(pgci_ut->nr_of_lus);
-  B2N_32(pgci_ut->last_byte);
-  
-  CHECK_ZERO(pgci_ut->zero_1);
-  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);
-  if(!data) {
-    free(pgci_ut);
-    ifofile->pgci_ut = 0;
-    return 0;
-  }
-  if(!(DVDReadBytes(ifofile->file, data, info_length))) {
-    free(data);
-    free(pgci_ut);
-    ifofile->pgci_ut = 0;
-    return 0;
-  }
-
-  pgci_ut->lu = malloc(pgci_ut->nr_of_lus * sizeof(pgci_lu_t));
-  if(!pgci_ut->lu) {
-    free(data);
-    free(pgci_ut);
-    ifofile->pgci_ut = 0;
-    return 0;
-  }
-  ptr = data;
-  for(i = 0; i < pgci_ut->nr_of_lus; i++) {
-    memcpy(&pgci_ut->lu[i], ptr, PGCI_LU_SIZE);
-    ptr += PGCI_LU_SIZE;
-    B2N_16(pgci_ut->lu[i].lang_code); 
-    B2N_32(pgci_ut->lu[i].lang_start_byte); 
-  }
-  free(data);
-  
-  for(i = 0; i < pgci_ut->nr_of_lus; i++) {
-    // Maybe this is only defined for v1.1 and later titles?
-    /* If the bits in 'lu[i].exists' are enumerated abcd efgh then:
-       VTS_x_yy.IFO        VIDEO_TS.IFO
-       a == 0x83 "Root"         0x82 "Title"
-       b == 0x84 "Subpicture"
-       c == 0x85 "Audio"
-       d == 0x86 "Angle"
-       e == 0x87 "PTT"
-    */
-    CHECK_VALUE((pgci_ut->lu[i].exists & 0x07) == 0);
-  }
-
-  for(i = 0; i < pgci_ut->nr_of_lus; i++) {
-    pgci_ut->lu[i].pgcit = malloc(sizeof(pgcit_t));
-    if(!pgci_ut->lu[i].pgcit) {
-      unsigned int j;
-      for(j = 0; j < i; j++) {
-        ifoFree_PGCIT_internal(pgci_ut->lu[j].pgcit);
-        free(pgci_ut->lu[j].pgcit);
-      }
-      free(pgci_ut->lu);
-      free(pgci_ut);
-      ifofile->pgci_ut = 0;
-      return 0;
-    }
-    if(!ifoRead_PGCIT_internal(ifofile, pgci_ut->lu[i].pgcit, 
-                               sector * DVD_BLOCK_LEN 
-                               + pgci_ut->lu[i].lang_start_byte)) {
-      unsigned int j;
-      for(j = 0; j < i; j++) {
-        ifoFree_PGCIT_internal(pgci_ut->lu[j].pgcit);
-        free(pgci_ut->lu[j].pgcit);
-      }
-      free(pgci_ut->lu[i].pgcit);
-      free(pgci_ut->lu);
-      free(pgci_ut);
-      ifofile->pgci_ut = 0;
-      return 0;
-    }
-    // FIXME: Iterate and verify that all menus that should exists accordingly
-    //        to pgci_ut->lu[i].exists really do?
-  }
-
-  return 1;
-}
-
-
-void ifoFree_PGCI_UT(ifo_handle_t *ifofile) {
-  unsigned int i;
-
-  if(!ifofile)
-    return;
-  
-  if(ifofile->pgci_ut) {
-    for(i = 0; i < ifofile->pgci_ut->nr_of_lus; i++) {
-      ifoFree_PGCIT_internal(ifofile->pgci_ut->lu[i].pgcit);
-      free(ifofile->pgci_ut->lu[i].pgcit);
-    }
-    free(ifofile->pgci_ut->lu);
-    free(ifofile->pgci_ut);
-    ifofile->pgci_ut = 0;
-  }
-}
-
-static int ifoRead_VTS_ATTRIBUTES(ifo_handle_t *ifofile, 
-                                  vts_attributes_t *vts_attributes, 
-                                  unsigned int offset) {
-  unsigned int i;
-
-  if(!DVDFileSeek_(ifofile->file, offset))
-    return 0;
-
-  if(!(DVDReadBytes(ifofile->file, vts_attributes, sizeof(vts_attributes_t))))
-    return 0;
-
-  B2N_32(vts_attributes->last_byte);
-  B2N_32(vts_attributes->vts_cat);
-  B2N_16(vts_attributes->vtsm_audio_attr.lang_code);
-  B2N_16(vts_attributes->vtsm_subp_attr.lang_code);
-  for(i = 0; i < 8; i++)
-    B2N_16(vts_attributes->vtstt_audio_attr[i].lang_code);
-  for(i = 0; i < 32; i++)
-    B2N_16(vts_attributes->vtstt_subp_attr[i].lang_code);
-  
-  CHECK_ZERO(vts_attributes->zero_1);
-  CHECK_ZERO(vts_attributes->zero_2);
-  CHECK_ZERO(vts_attributes->zero_3);
-  CHECK_ZERO(vts_attributes->zero_4);
-  CHECK_ZERO(vts_attributes->zero_5);
-  CHECK_ZERO(vts_attributes->zero_6);
-  CHECK_ZERO(vts_attributes->zero_7);
-  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]);
-  CHECK_VALUE(vts_attributes->nr_of_vtstt_subp_streams <= 32);
-  {
-    unsigned int nr_coded;
-    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;
-    }
-    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]);
-  }
-
-  return 1;
-}
-
-
-
-int ifoRead_VTS_ATRT(ifo_handle_t *ifofile) {
-  vts_atrt_t *vts_atrt;
-  unsigned int i, info_length, sector;
-  uint32_t *data;
-
-  if(!ifofile)
-    return 0;
-  
-  if(!ifofile->vmgi_mat)
-    return 0;
-  
-  if(ifofile->vmgi_mat->vts_atrt == 0) /* mandatory */
-    return 0;
-  
-  sector = ifofile->vmgi_mat->vts_atrt;
-  if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN))
-    return 0;
-
-  vts_atrt = malloc(sizeof(vts_atrt_t));
-  if(!vts_atrt)
-    return 0;
-
-  ifofile->vts_atrt = vts_atrt;
-  
-  if(!(DVDReadBytes(ifofile->file, vts_atrt, VTS_ATRT_SIZE))) {
-    free(vts_atrt);
-    ifofile->vts_atrt = 0;
-    return 0;
-  }
-
-  B2N_16(vts_atrt->nr_of_vtss);
-  B2N_32(vts_atrt->last_byte);
-
-  CHECK_ZERO(vts_atrt->zero_1);
-  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);
-  data = malloc(info_length);
-  if(!data) {
-    free(vts_atrt);
-    ifofile->vts_atrt = 0;
-    return 0;
-  }
-
-  vts_atrt->vts_atrt_offsets = data;   
-
-  if(!(DVDReadBytes(ifofile->file, data, info_length))) {
-    free(data);
-    free(vts_atrt);
-    ifofile->vts_atrt = 0;
-    return 0;
-  }
-  
-  for(i = 0; i < vts_atrt->nr_of_vtss; i++) {
-    B2N_32(data[i]);
-    CHECK_VALUE(data[i] + VTS_ATTRIBUTES_MIN_SIZE < vts_atrt->last_byte + 1);
-  }
-  
-  info_length = vts_atrt->nr_of_vtss * sizeof(vts_attributes_t);
-  vts_atrt->vts = malloc(info_length);
-  if(!vts_atrt->vts) {
-    free(data);
-    free(vts_atrt);
-    ifofile->vts_atrt = 0;
-    return 0;
-  }
-  for(i = 0; i < vts_atrt->nr_of_vtss; i++) {
-    unsigned int offset = data[i];
-    if(!ifoRead_VTS_ATTRIBUTES(ifofile, &(vts_atrt->vts[i]),
-                               (sector * DVD_BLOCK_LEN) + offset)) {
-      free(data);
-      free(vts_atrt);
-      ifofile->vts_atrt = 0;
-      return 0;
-    }
-
-    // This assert cant be in ifoRead_VTS_ATTRIBUTES
-    CHECK_VALUE(offset + vts_atrt->vts[i].last_byte <= vts_atrt->last_byte + 1);
-    // Is this check correct?
-  }
-
-  return 1;
-}
-
-
-void ifoFree_VTS_ATRT(ifo_handle_t *ifofile) {
-  if(!ifofile)
-    return;
-  
-  if(ifofile->vts_atrt) {
-    free(ifofile->vts_atrt->vts);
-    free(ifofile->vts_atrt->vts_atrt_offsets);
-    free(ifofile->vts_atrt);
-    ifofile->vts_atrt = 0;
-  }
-}
-
-
-int ifoRead_TXTDT_MGI(ifo_handle_t *ifofile) {
-  txtdt_mgi_t *txtdt_mgi;
-
-  if(!ifofile)
-    return 0;
-  
-  if(!ifofile->vmgi_mat)
-    return 0;
- 
-  /* Return successfully if there is nothing to read. */ 
-  if(ifofile->vmgi_mat->txtdt_mgi == 0)
-    return 1;
-
-  if(!DVDFileSeek_(ifofile->file, 
-                   ifofile->vmgi_mat->txtdt_mgi * DVD_BLOCK_LEN))
-    return 0;
-  
-  txtdt_mgi = malloc(sizeof(txtdt_mgi_t));
-  if(!txtdt_mgi) {
-    return 0;
-  }
-  ifofile->txtdt_mgi = txtdt_mgi;
-
-  if(!(DVDReadBytes(ifofile->file, txtdt_mgi, TXTDT_MGI_SIZE))) {
-    if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
-      fprintf(stderr, "libdvdread: Unable to read TXTDT_MGI.\n");
-    }
-    free(txtdt_mgi);
-    ifofile->txtdt_mgi = 0;
-    return 0;
-  }
-
-  // fprintf(stderr, "-- Not done yet --\n");
-  return 1;
-}
-
-void ifoFree_TXTDT_MGI(ifo_handle_t *ifofile) {
-  if(!ifofile)
-    return;
-  
-  if(ifofile->txtdt_mgi) {
-    free(ifofile->txtdt_mgi);
-    ifofile->txtdt_mgi = 0;
-  }
-}
-
--- a/dvdread/ifo_read.h	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,228 +0,0 @@
-/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
-#ifndef IFO_READ_H_INCLUDED
-#define IFO_READ_H_INCLUDED
-
-/*
- * Copyright (C) 2000, 2001, 2002 Björn Englund <d4bjorn@dtek.chalmers.se>,
- *                                Håkan Hjort <d95hjort@dtek.chalmers.se>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include <dvdread/ifo_types.h>
-#include <dvdread/dvd_reader.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * handle = ifoOpen(dvd, title);
- *
- * Opens an IFO and reads in all the data for the IFO file corresponding to the
- * given title.  If title 0 is given, the video manager IFO file is read.
- * Returns a handle to a completely parsed structure.
- */
-ifo_handle_t *ifoOpen(dvd_reader_t *, int );
-
-/**
- * handle = ifoOpenVMGI(dvd);
- *
- * Opens an IFO and reads in _only_ the vmgi_mat data.  This call can be used
- * together with the calls below to read in each segment of the IFO file on
- * demand.
- */
-ifo_handle_t *ifoOpenVMGI(dvd_reader_t *);
-
-/**
- * handle = ifoOpenVTSI(dvd, title);
- *
- * Opens an IFO and reads in _only_ the vtsi_mat data.  This call can be used
- * together with the calls below to read in each segment of the IFO file on
- * demand.
- */
-ifo_handle_t *ifoOpenVTSI(dvd_reader_t *, int);
-
-/**
- * ifoClose(ifofile);
- * Cleans up the IFO information.  This will free all data allocated for the
- * substructures.
- */
-void ifoClose(ifo_handle_t *);
-
-/**
- * The following functions are for reading only part of the VMGI/VTSI files.
- * Returns 1 if the data was successfully read and 0 on error.
- */
-
-/**
- * okay = ifoRead_PLT_MAIT(ifofile);
- *
- * Read in the Parental Management Information table, filling the
- * ifofile->ptl_mait structure and its substructures.  This data is only
- * located in the video manager information file.  This fills the
- * ifofile->ptl_mait structure and all its substructures.
- */
-int ifoRead_PTL_MAIT(ifo_handle_t *);
-
-/**
- * okay = ifoRead_VTS_ATRT(ifofile);
- *
- * Read in the attribute table for the main menu vob, filling the
- * ifofile->vts_atrt structure and its substructures.  Only located in the
- * video manager information file.  This fills in the ifofile->vts_atrt
- * structure and all its substructures.
- */
-int ifoRead_VTS_ATRT(ifo_handle_t *);
-
-/**
- * okay = ifoRead_TT_SRPT(ifofile);
- *
- * Reads the title info for the main menu, filling the ifofile->tt_srpt
- * structure and its substructures.  This data is only located in the video
- * manager information file.  This structure is mandatory in the IFO file.
- */
-int ifoRead_TT_SRPT(ifo_handle_t *);
-
-/**
- * okay = ifoRead_VTS_PTT_SRPT(ifofile);
- *
- * Reads in the part of title search pointer table, filling the
- * ifofile->vts_ptt_srpt structure and its substructures.  This data is only
- * located in the video title set information file.  This structure is
- * mandatory, and must be included in the VTSI file.
- */
-int ifoRead_VTS_PTT_SRPT(ifo_handle_t *);
-
-/**
- * okay = ifoRead_FP_PGC(ifofile);
- *
- * Reads in the first play program chain data, filling the
- * ifofile->first_play_pgc structure.  This data is only located in the video
- * manager information file (VMGI).  This structure is optional.
- */
-int ifoRead_FP_PGC(ifo_handle_t *);
-
-/**
- * okay = ifoRead_PGCIT(ifofile);
- *
- * Reads in the program chain information table for the video title set.  Fills
- * in the ifofile->vts_pgcit structure and its substructures, which includes
- * the data for each program chain in the set.  This data is only located in
- * the video title set information file.  This structure is mandatory, and must
- * be included in the VTSI file.
- */
-int ifoRead_PGCIT(ifo_handle_t *);
-
-/**
- * okay = ifoRead_PGCI_UT(ifofile);
- *
- * Reads in the menu PGCI unit table for the menu VOB.  For the video manager,
- * this corresponds to the VIDEO_TS.VOB file, and for each title set, this
- * corresponds to the VTS_XX_0.VOB file.  This data is located in both the
- * video manager and video title set information files.  For VMGI files, this
- * fills the ifofile->vmgi_pgci_ut structure and all its substructures.  For
- * VTSI files, this fills the ifofile->vtsm_pgci_ut structure.
- */
-int ifoRead_PGCI_UT(ifo_handle_t *);
-  
-/**
- * okay = ifoRead_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);
- *
- * Reads in the cell address table for the menu VOB.  For the video manager,
- * this corresponds to the VIDEO_TS.VOB file, and for each title set, this
- * corresponds to the VTS_XX_0.VOB file.  This data is located in both the
- * video manager and video title set information files.  For VMGI files, this
- * fills the ifofile->vmgm_c_adt structure and all its substructures.  For VTSI
- * files, this fills the ifofile->vtsm_c_adt structure.
- */
-int ifoRead_C_ADT(ifo_handle_t *);
-
-/**
- * okay = ifoRead_TITLE_C_ADT(ifofile);
- *
- * Reads in the cell address table for the video title set corresponding to
- * this IFO file.  This data is only located in the video title set information
- * file.  This structure is mandatory, and must be included in the VTSI file.
- * This call fills the ifofile->vts_c_adt structure and its substructures.
- */
-int ifoRead_TITLE_C_ADT(ifo_handle_t *);
-
-/**
- * okay = ifoRead_VOBU_ADMAP(ifofile);
- *
- * Reads in the VOBU address map for the menu VOB.  For the video manager, this
- * corresponds to the VIDEO_TS.VOB file, and for each title set, this
- * corresponds to the VTS_XX_0.VOB file.  This data is located in both the
- * video manager and video title set information files.  For VMGI files, this
- * fills the ifofile->vmgm_vobu_admap structure and all its substructures.  For
- * VTSI files, this fills the ifofile->vtsm_vobu_admap structure.
- */
-int ifoRead_VOBU_ADMAP(ifo_handle_t *);
-
-/**
- * okay = ifoRead_TITLE_VOBU_ADMAP(ifofile);
- *
- * Reads in the VOBU address map for the associated video title set.  This data
- * is only located in the video title set information file.  This structure is
- * mandatory, and must be included in the VTSI file.  Fills the
- * ifofile->vts_vobu_admap structure and its substructures.
- */
-int ifoRead_TITLE_VOBU_ADMAP(ifo_handle_t *);
-
-/**
- * okay = ifoRead_TXTDT_MGI(ifofile);
- *
- * Reads in the text data strings for the DVD.  Fills the ifofile->txtdt_mgi
- * structure and all its substructures.  This data is only located in the video
- * manager information file.  This structure is mandatory, and must be included
- * in the VMGI file.
- */
-int ifoRead_TXTDT_MGI(ifo_handle_t *);
-
-/**
- * The following functions are used for freeing parsed sections of the
- * ifo_handle_t structure and the allocated substructures.  The free calls
- * below are safe:  they will not mind if you attempt to free part of an IFO
- * file which was not read in or which does not exist.
- */
-void ifoFree_PTL_MAIT(ifo_handle_t *);
-void ifoFree_VTS_ATRT(ifo_handle_t *);
-void ifoFree_TT_SRPT(ifo_handle_t *);
-void ifoFree_VTS_PTT_SRPT(ifo_handle_t *);
-void ifoFree_FP_PGC(ifo_handle_t *);
-void ifoFree_PGCIT(ifo_handle_t *);
-void ifoFree_PGCI_UT(ifo_handle_t *);
-void ifoFree_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 *);
-void ifoFree_TITLE_VOBU_ADMAP(ifo_handle_t *);
-void ifoFree_TXTDT_MGI(ifo_handle_t *);
-
-#ifdef __cplusplus
-};
-#endif
-#endif /* IFO_READ_H_INCLUDED */
--- a/dvdread/ifo_types.h	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,897 +0,0 @@
-/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
-#ifndef IFO_TYPES_H_INCLUDED
-#define IFO_TYPES_H_INCLUDED
-
-/*
- * Copyright (C) 2000, 2001 Björn Englund <d4bjorn@dtek.chalmers.se>,
- *                          Håkan Hjort <d95hjort@dtek.chalmers.se>
- *
- * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
- * detailed changelog at http://svn.mplayerhq.hu/mplayer/trunk/
- * $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
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include <dvdread/dvd_reader.h>
-
-#if defined(__BEOS__)
-#if !defined(_INTTYPES_H_) && !defined(_INTTYPES_H) && !defined(_STDINT_H_) && !defined(_STDINT_H)
-#error "Must include <inttypes.h> or <stdint.h> before any libdvdread header."
-#endif
-#else
-#if !defined(UINT8_MAX) || !defined(UINT16_MAX) || !defined(INT32_MAX)
-#error "Must include <inttypes.h> or <stdint.h> before any libdvdread header."
-#endif
-#endif
-
-#undef ATTRIBUTE_PACKED
-#undef PRAGMA_PACK_BEGIN 
-#undef PRAGMA_PACK_END
-
-#if defined(__GNUC__)
-#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
-#define ATTRIBUTE_PACKED __attribute__ ((packed))
-#define PRAGMA_PACK 0
-#endif
-#endif
-
-#if !defined(ATTRIBUTE_PACKED)
-#define ATTRIBUTE_PACKED
-#define PRAGMA_PACK 1
-#endif
-
-#if PRAGMA_PACK
-#pragma pack(1)
-#endif
-
-
-/**
- * Common
- *
- * The following structures are used in both the VMGI and VTSI.
- */
-
-
-/**
- * DVD Time Information.
- */
-typedef struct {
-  uint8_t hour;
-  uint8_t minute;
-  uint8_t second;
-  uint8_t frame_u; /* The two high bits are the frame rate. */
-} ATTRIBUTE_PACKED dvd_time_t;
-
-/**
- * Type to store per-command data.
- */
-typedef struct {
-  uint8_t bytes[8];
-} ATTRIBUTE_PACKED vm_cmd_t;
-#define COMMAND_DATA_SIZE 8U
-
-
-/**
- * Video Attributes.
- */
-typedef struct {
-#ifdef WORDS_BIGENDIAN
-  unsigned int mpeg_version         : 2;
-  unsigned int video_format         : 2;
-  unsigned int display_aspect_ratio : 2;
-  unsigned int permitted_df         : 2;
-  
-  unsigned int line21_cc_1          : 1;
-  unsigned int line21_cc_2          : 1;
-  unsigned int unknown1             : 1;
-  unsigned int bit_rate             : 1;
-  
-  unsigned int picture_size         : 2;
-  unsigned int letterboxed          : 1;
-  unsigned int film_mode            : 1;
-#else
-  unsigned int permitted_df         : 2;
-  unsigned int display_aspect_ratio : 2;
-  unsigned int video_format         : 2;
-  unsigned int mpeg_version         : 2;
-  
-  unsigned int film_mode            : 1;
-  unsigned int letterboxed          : 1;
-  unsigned int picture_size         : 2;
-  
-  unsigned int 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.
- */
-typedef struct {
-#ifdef WORDS_BIGENDIAN
-  unsigned int audio_format           : 3;
-  unsigned int multichannel_extension : 1;
-  unsigned int lang_type              : 2;
-  unsigned int application_mode       : 2;
-  
-  unsigned int quantization           : 2;
-  unsigned int sample_frequency       : 2;
-  unsigned int unknown1               : 1;
-  unsigned int channels               : 3;
-#else
-  unsigned int application_mode       : 2;
-  unsigned int lang_type              : 2;
-  unsigned int multichannel_extension : 1;
-  unsigned int audio_format           : 3;
-  
-  unsigned int channels               : 3;
-  unsigned int unknown1               : 1;
-  unsigned int sample_frequency       : 2;
-  unsigned int quantization           : 2;
-#endif
-  uint16_t lang_code;
-  uint8_t  lang_extension;
-  uint8_t  code_extension;
-  uint8_t unknown3;
-  union {
-    struct {
-#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
-    } ATTRIBUTE_PACKED karaoke;
-    struct {
-#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
-    } ATTRIBUTE_PACKED surround;
-  } ATTRIBUTE_PACKED app_info;
-} ATTRIBUTE_PACKED audio_attr_t;
-
-
-/**
- * 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 {
-  /*
-   * type: 0 not specified
-   *       1 language
-   *       2 other
-   * coding mode: 0 run length
-   *              1 extended
-   *              2 other
-   * language: indicates language if type == 1
-   * lang extension: if type == 1 contains the lang extension
-   */
-#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  code_extension;
-} ATTRIBUTE_PACKED subp_attr_t;
-
-
-
-/**
- * PGC Command Table.
- */ 
-typedef struct {
-  uint16_t nr_of_pre;
-  uint16_t nr_of_post;
-  uint16_t nr_of_cell;
-  uint16_t last_byte;
-  vm_cmd_t *pre_cmds;
-  vm_cmd_t *post_cmds;
-  vm_cmd_t *cell_cmds;
-} ATTRIBUTE_PACKED pgc_command_tbl_t;
-#define PGC_COMMAND_TBL_SIZE 8U
-
-/**
- * PGC Program Map
- */
-typedef uint8_t pgc_program_map_t; 
-
-/**
- * Cell Playback Information.
- */
-typedef struct {
-#ifdef WORDS_BIGENDIAN
-  unsigned int block_mode       : 2;
-  unsigned int block_type       : 2;
-  unsigned int seamless_play    : 1;
-  unsigned int interleaved      : 1;
-  unsigned int stc_discontinuity: 1;
-  unsigned int seamless_angle   : 1;
-  
-  unsigned int 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;
-  unsigned int stc_discontinuity: 1;
-  unsigned int interleaved      : 1;
-  unsigned int seamless_play    : 1;
-  unsigned int block_type       : 2;
-  unsigned int block_mode       : 2;
-  
-  unsigned int unknown2         : 6;
-  unsigned int restricted       : 1;
-  unsigned int playback_mode    : 1;
-#endif
-  uint8_t still_time;
-  uint8_t cell_cmd_nr;
-  dvd_time_t playback_time;
-  uint32_t first_sector;
-  uint32_t first_ilvu_end_sector;
-  uint32_t last_vobu_start_sector;
-  uint32_t last_sector;
-} ATTRIBUTE_PACKED cell_playback_t;
-
-#define BLOCK_TYPE_NONE         0x0
-#define BLOCK_TYPE_ANGLE_BLOCK  0x1
-
-#define BLOCK_MODE_NOT_IN_BLOCK 0x0
-#define BLOCK_MODE_FIRST_CELL   0x1
-#define BLOCK_MODE_IN_BLOCK     0x2
-#define BLOCK_MODE_LAST_CELL    0x3
-
-/**
- * Cell Position Information.
- */
-typedef struct {
-  uint16_t vob_id_nr;
-  uint8_t  zero_1;
-  uint8_t  cell_nr;
-} ATTRIBUTE_PACKED cell_position_t;
-
-/**
- * User Operations.
- */
-typedef struct {
-#ifdef WORDS_BIGENDIAN
-  unsigned int zero                           : 7; /* 25-31 */
-  unsigned int video_pres_mode_change         : 1; /* 24 */
-  
-  unsigned int karaoke_audio_pres_mode_change : 1; /* 23 */
-  unsigned int angle_change                   : 1;
-  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;
-  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;
-  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 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;
-  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;
-  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;
-
-/**
- * Program Chain Information.
- */
-typedef struct {
-  uint16_t zero_1;
-  uint8_t  nr_of_programs;
-  uint8_t  nr_of_cells;
-  dvd_time_t playback_time;
-  user_ops_t prohibited_ops;
-  uint16_t audio_control[8];
-  uint32_t subp_control[32];
-  uint16_t next_pgc_nr;
-  uint16_t prev_pgc_nr;
-  uint16_t goup_pgc_nr;
-  uint8_t  pg_playback_mode;
-  uint8_t  still_time;
-  uint32_t palette[16]; /* New type struct {zero_1, Y, Cr, Cb} ? */
-  uint16_t command_tbl_offset;
-  uint16_t program_map_offset;
-  uint16_t cell_playback_offset;
-  uint16_t cell_position_offset;
-  pgc_command_tbl_t *command_tbl;
-  pgc_program_map_t  *program_map;
-  cell_playback_t *cell_playback;
-  cell_position_t *cell_position;
-} ATTRIBUTE_PACKED pgc_t;
-#define PGC_SIZE 236U
-
-/**
- * Program Chain Information Search Pointer.
- */
-typedef struct {
-  uint8_t  entry_id;
-#ifdef WORDS_BIGENDIAN
-  unsigned int block_mode : 2;
-  unsigned int block_type : 2;
-  unsigned int unknown1   : 4;
-#else
-  unsigned int unknown1   : 4;
-  unsigned int block_type : 2;
-  unsigned int block_mode : 2;
-#endif  
-  uint16_t ptl_id_mask;
-  uint32_t pgc_start_byte;
-  pgc_t *pgc;
-} ATTRIBUTE_PACKED pgci_srp_t;
-#define PGCI_SRP_SIZE 8U
-
-/**
- * Program Chain Information Table.
- */
-typedef struct {
-  uint16_t nr_of_pgci_srp;
-  uint16_t zero_1;
-  uint32_t last_byte;
-  pgci_srp_t *pgci_srp;
-} ATTRIBUTE_PACKED pgcit_t;
-#define PGCIT_SIZE 8U
-
-/**
- * Menu PGCI Language Unit.
- */
-typedef struct {
-  uint16_t lang_code;
-  uint8_t  lang_extension;
-  uint8_t  exists;
-  uint32_t lang_start_byte;
-  pgcit_t *pgcit;
-} ATTRIBUTE_PACKED pgci_lu_t;
-#define PGCI_LU_SIZE 8U
-
-/**
- * Menu PGCI Unit Table.
- */
-typedef struct {
-  uint16_t nr_of_lus;
-  uint16_t zero_1;
-  uint32_t last_byte;
-  pgci_lu_t *lu;
-} ATTRIBUTE_PACKED pgci_ut_t;
-#define PGCI_UT_SIZE 8U
-
-/**
- * Cell Address Information.
- */
-typedef struct {
-  uint16_t vob_id;
-  uint8_t  cell_id;
-  uint8_t  zero_1;
-  uint32_t start_sector;
-  uint32_t last_sector;
-} ATTRIBUTE_PACKED cell_adr_t;
-
-/**
- * Cell Address Table.
- */
-typedef struct {
-  uint16_t nr_of_vobs; /* VOBs */
-  uint16_t zero_1;
-  uint32_t last_byte;
-  cell_adr_t *cell_adr_table;  /* No explicit size given. */
-} ATTRIBUTE_PACKED c_adt_t;
-#define C_ADT_SIZE 8U
-
-/**
- * VOBU Address Map.
- */
-typedef struct {
-  uint32_t last_byte;
-  uint32_t *vobu_start_sectors;
-} ATTRIBUTE_PACKED vobu_admap_t;
-#define VOBU_ADMAP_SIZE 4U
-
-
-
-
-/**
- * VMGI
- *
- * The following structures relate to the Video Manager.
- */
-
-/**
- * Video Manager Information Management Table.
- */
-typedef struct {
-  char     vmg_identifier[12];
-  uint32_t vmg_last_sector;
-  uint8_t  zero_1[12];
-  uint32_t vmgi_last_sector;
-  uint8_t  zero_2;
-  uint8_t  specification_version;
-  uint32_t vmg_category;
-  uint16_t vmg_nr_of_volumes;
-  uint16_t vmg_this_volume_nr;
-  uint8_t  disc_side;
-  uint8_t  zero_3[19];
-  uint16_t vmg_nr_of_title_sets;  /* Number of VTSs. */
-  char     provider_identifier[32];
-  uint64_t vmg_pos_code;
-  uint8_t  zero_4[24];
-  uint32_t vmgi_last_byte;
-  uint32_t first_play_pgc;
-  uint8_t  zero_5[56];
-  uint32_t vmgm_vobs;             /* sector */
-  uint32_t tt_srpt;               /* sector */
-  uint32_t vmgm_pgci_ut;          /* sector */
-  uint32_t ptl_mait;              /* sector */
-  uint32_t vts_atrt;              /* sector */
-  uint32_t txtdt_mgi;             /* sector */
-  uint32_t vmgm_c_adt;            /* sector */
-  uint32_t vmgm_vobu_admap;       /* sector */
-  uint8_t  zero_6[32];
-  
-  video_attr_t vmgm_video_attr;
-  uint8_t  zero_7;
-  uint8_t  nr_of_vmgm_audio_streams; /* should be 0 or 1 */
-  audio_attr_t vmgm_audio_attr;
-  audio_attr_t zero_8[7];
-  uint8_t  zero_9[17];
-  uint8_t  nr_of_vmgm_subp_streams; /* should be 0 or 1 */
-  subp_attr_t  vmgm_subp_attr;
-  subp_attr_t  zero_10[27];  /* XXX: how much 'padding' here? */
-} ATTRIBUTE_PACKED vmgi_mat_t;
-
-typedef struct {
-#ifdef WORDS_BIGENDIAN
-  unsigned int zero_1                    : 1;
-  unsigned int multi_or_random_pgc_title : 1; /* 0: one sequential pgc title */
-  unsigned int jlc_exists_in_cell_cmd    : 1;
-  unsigned int jlc_exists_in_prepost_cmd : 1;
-  unsigned int jlc_exists_in_button_cmd  : 1;
-  unsigned int jlc_exists_in_tt_dom      : 1;
-  unsigned int chapter_search_or_play    : 1; /* UOP 1 */
-  unsigned int title_or_time_play        : 1; /* UOP 0 */
-#else
-  unsigned int title_or_time_play        : 1;
-  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;
-  unsigned int zero_1                    : 1;
-#endif
-} ATTRIBUTE_PACKED playback_type_t;
-
-/**
- * Title Information.
- */
-typedef struct {
-  playback_type_t pb_ty;
-  uint8_t  nr_of_angles;
-  uint16_t nr_of_ptts;
-  uint16_t parental_id;
-  uint8_t  title_set_nr;
-  uint8_t  vts_ttn;
-  uint32_t title_set_sector;
-} ATTRIBUTE_PACKED title_info_t;
-
-/**
- * PartOfTitle Search Pointer Table.
- */
-typedef struct {
-  uint16_t nr_of_srpts;
-  uint16_t zero_1;
-  uint32_t last_byte;
-  title_info_t *title;
-} ATTRIBUTE_PACKED tt_srpt_t;
-#define TT_SRPT_SIZE 8U
-
-
-/**
- * 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.
- */
-typedef struct {
-  uint16_t country_code;
-  uint16_t zero_1;
-  uint16_t pf_ptl_mai_start_byte;
-  uint16_t zero_2;
-  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 8U
-
-/**
- * Parental Management Information Table.
- */
-typedef struct {
-  uint16_t nr_of_countries;
-  uint16_t nr_of_vtss;
-  uint32_t last_byte;
-  ptl_mait_country_t *countries;
-} ATTRIBUTE_PACKED ptl_mait_t;
-#define PTL_MAIT_SIZE 8U
-
-/**
- * Video Title Set Attributes.
- */
-typedef struct {
-  uint32_t last_byte;
-  uint32_t vts_cat;
-  
-  video_attr_t vtsm_vobs_attr;
-  uint8_t  zero_1;
-  uint8_t  nr_of_vtsm_audio_streams; /* should be 0 or 1 */
-  audio_attr_t vtsm_audio_attr;
-  audio_attr_t zero_2[7];  
-  uint8_t  zero_3[16];
-  uint8_t  zero_4;
-  uint8_t  nr_of_vtsm_subp_streams; /* should be 0 or 1 */
-  subp_attr_t vtsm_subp_attr;
-  subp_attr_t zero_5[27];
-  
-  uint8_t  zero_6[2];
-  
-  video_attr_t vtstt_vobs_video_attr;
-  uint8_t  zero_7;
-  uint8_t  nr_of_vtstt_audio_streams;
-  audio_attr_t vtstt_audio_attr[8];
-  uint8_t  zero_8[16];
-  uint8_t  zero_9;
-  uint8_t  nr_of_vtstt_subp_streams;
-  subp_attr_t vtstt_subp_attr[32];
-} ATTRIBUTE_PACKED vts_attributes_t;
-#define VTS_ATTRIBUTES_SIZE 542U
-#define VTS_ATTRIBUTES_MIN_SIZE 356U
-
-/**
- * Video Title Set Attribute Table.
- */
-typedef struct {
-  uint16_t nr_of_vtss;
-  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 8U
-
-/**
- * Text Data. (Incomplete)
- */
-typedef struct {
-  uint32_t last_byte;    /* offsets are relative here */
-  uint16_t offsets[100]; /* == nr_of_srpts + 1 (first is disc title) */
-#if 0  
-  uint16_t unknown; /* 0x48 ?? 0x48 words (16bit) info following */
-  uint16_t zero_1;
-  
-  uint8_t type_of_info; /* ?? 01 == disc, 02 == Title, 04 == Title part */
-  uint8_t unknown1;
-  uint8_t unknown2;
-  uint8_t unknown3;
-  uint8_t unknown4; /* ?? allways 0x30 language?, text format? */
-  uint8_t unknown5;
-  uint16_t offset; /* from first */
-  
-  char text[12]; /* ended by 0x09 */
-#endif
-} ATTRIBUTE_PACKED txtdt_t;
-
-/**
- * Text Data Language Unit. (Incomplete)
- */ 
-typedef struct {
-  uint16_t lang_code;
-  uint16_t unknown;      /* 0x0001, title 1? disc 1? side 1? */
-  uint32_t txtdt_start_byte;  /* prt, rel start of vmg_txtdt_mgi  */
-  txtdt_t  *txtdt;
-} ATTRIBUTE_PACKED txtdt_lu_t;
-#define TXTDT_LU_SIZE 8U
-
-/**
- * Text Data Manager Information. (Incomplete)
- */
-typedef struct {
-  char disc_name[14];            /* how many bytes?? */
-  uint16_t nr_of_language_units; /* 32bit??          */
-  uint32_t last_byte;
-  txtdt_lu_t *lu;
-} ATTRIBUTE_PACKED txtdt_mgi_t;
-#define TXTDT_MGI_SIZE 20U
-
-
-/**
- * VTS
- *
- * Structures relating to the Video Title Set (VTS).
- */
-
-/**
- * Video Title Set Information Management Table.
- */
-typedef struct {
-  char vts_identifier[12];
-  uint32_t vts_last_sector;
-  uint8_t  zero_1[12];
-  uint32_t vtsi_last_sector;
-  uint8_t  zero_2;
-  uint8_t  specification_version;
-  uint32_t vts_category;
-  uint16_t zero_3;
-  uint16_t zero_4;
-  uint8_t  zero_5;
-  uint8_t  zero_6[19];
-  uint16_t zero_7;
-  uint8_t  zero_8[32];
-  uint64_t zero_9;
-  uint8_t  zero_10[24];
-  uint32_t vtsi_last_byte;
-  uint32_t zero_11;
-  uint8_t  zero_12[56];
-  uint32_t vtsm_vobs;       /* sector */
-  uint32_t vtstt_vobs;      /* sector */
-  uint32_t vts_ptt_srpt;    /* sector */
-  uint32_t vts_pgcit;       /* sector */
-  uint32_t vtsm_pgci_ut;    /* sector */
-  uint32_t vts_tmapt;       /* sector */
-  uint32_t vtsm_c_adt;      /* sector */
-  uint32_t vtsm_vobu_admap; /* sector */
-  uint32_t vts_c_adt;       /* sector */
-  uint32_t vts_vobu_admap;  /* sector */
-  uint8_t  zero_13[24];
-  
-  video_attr_t vtsm_video_attr;
-  uint8_t  zero_14;
-  uint8_t  nr_of_vtsm_audio_streams; /* should be 0 or 1 */
-  audio_attr_t vtsm_audio_attr;
-  audio_attr_t zero_15[7];
-  uint8_t  zero_16[17];
-  uint8_t  nr_of_vtsm_subp_streams; /* should be 0 or 1 */
-  subp_attr_t vtsm_subp_attr;
-  subp_attr_t zero_17[27];
-  uint8_t  zero_18[2];
-  
-  video_attr_t vts_video_attr;
-  uint8_t  zero_19;
-  uint8_t  nr_of_vts_audio_streams;
-  audio_attr_t vts_audio_attr[8];
-  uint8_t  zero_20[17];
-  uint8_t  nr_of_vts_subp_streams;
-  subp_attr_t vts_subp_attr[32];
-  uint16_t zero_21;
-  multichannel_ext_t vts_mu_audio_attr[8];
-  /* XXX: how much 'padding' here, if any? */
-} ATTRIBUTE_PACKED vtsi_mat_t;
-
-/**
- * PartOfTitle Unit Information.
- */
-typedef struct {
-  uint16_t pgcn;
-  uint16_t pgn;
-} ATTRIBUTE_PACKED ptt_info_t;
-
-/**
- * PartOfTitle Information.
- */
-typedef struct {
-  uint16_t nr_of_ptts;
-  ptt_info_t *ptt;
-} ATTRIBUTE_PACKED ttu_t;
-
-/**
- * PartOfTitle Search Pointer Table.
- */
-typedef struct {
-  uint16_t nr_of_srpts;
-  uint16_t zero_1;
-  uint32_t last_byte;
-  ttu_t  *title;
-  uint32_t *ttu_offset; /* offset table for each ttu */
-} ATTRIBUTE_PACKED vts_ptt_srpt_t;
-#define VTS_PTT_SRPT_SIZE 8U
-
-
-/**
- * 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 4U
-
-/**
- * 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 8U
-
-
-#if PRAGMA_PACK
-#pragma pack()
-#endif
-
-
-/**
- * The following structure defines an IFO file.  The structure is divided into
- * two parts, the VMGI, or Video Manager Information, which is read from the
- * VIDEO_TS.[IFO,BUP] file, and the VTSI, or Video Title Set Information, which
- * is read in from the VTS_XX_0.[IFO,BUP] files.
- */
-typedef struct {
-  dvd_file_t *file;
-  
-  /* VMGI */
-  vmgi_mat_t     *vmgi_mat;
-  tt_srpt_t      *tt_srpt;
-  pgc_t          *first_play_pgc;    
-  ptl_mait_t     *ptl_mait;
-  vts_atrt_t     *vts_atrt;
-  txtdt_mgi_t    *txtdt_mgi;
-  
-  /* Common */
-  pgci_ut_t      *pgci_ut;
-  c_adt_t        *menu_c_adt;
-  vobu_admap_t   *menu_vobu_admap;
-  
-  /* VTSI */
-  vtsi_mat_t     *vtsi_mat;
-  vts_ptt_srpt_t *vts_ptt_srpt;
-  pgcit_t        *vts_pgcit;
-  vts_tmapt_t    *vts_tmapt;
-  c_adt_t        *vts_c_adt;
-  vobu_admap_t   *vts_vobu_admap;
-} ifo_handle_t;
-
-#endif /* IFO_TYPES_H_INCLUDED */
--- a/dvdread/libdvdread_changes.diff	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,514 +0,0 @@
---- dvdread.orig/dvd_reader.c	2007-08-06 13:34:37.000000000 +0200
-+++ dvdread/dvd_reader.c	2007-08-06 13:35:19.000000000 +0200
-@@ -39,9 +43,11 @@
- 
- #if defined(__sun)
- #include <sys/mnttab.h>
-+#elif defined(hpux)
-+#include </usr/conf/h/mnttab.h>
- #elif defined(SYS_BSD)
- #include <fstab.h>
--#elif defined(__linux__)
-+#elif defined(__linux__) || defined(__CYGWIN__)
- #include <mntent.h>
- #endif
- 
-@@ -52,7 +58,7 @@
- 
- #include "dvdread_internal.h"
- 
--#define DEFAULT_UDF_CACHE_LEVEL 1
-+#define DEFAULT_UDF_CACHE_LEVEL 0
- 
- struct dvd_reader_s {
-   /* Basic information. */
-@@ -183,19 +189,7 @@
-   dev->align = align;
- }
- 
--#ifdef WIN32 /* replacement gettimeofday implementation */
--#include <sys/timeb.h>
--static int gettimeofday( struct timeval *tv, void *tz )
--{
--  struct timeb t;
--  ftime( &t );
--  tv->tv_sec = t.time;
--  tv->tv_usec = t.millitm * 1000;
--  return 0;
--}
--#endif
- 
--
- /* Loop over all titles and call dvdcss_title to crack the keys. */
- static int initAllCSSKeys( dvd_reader_t *dvd )
- {
-@@ -494,7 +488,7 @@
-     char *path_copy;
- #if defined(SYS_BSD)
-     struct fstab* fe;
--#elif defined(__sun) || defined(__linux__)
-+#elif defined(__sun) || defined(__linux__) || defined(__CYGWIN__)
-     FILE *mntfile;
- #endif
- 
-@@ -598,7 +592,7 @@
-       }
-       fclose( mntfile );
-     }
--#elif defined(__linux__)
-+#elif defined(__linux__) || defined(__CYGWIN__)
-     mntfile = fopen( MOUNTED, "r" );
-     if( mntfile ) {
-       struct mntent *me;
-@@ -623,6 +617,9 @@
-       }
-       fclose( mntfile );
-     }
-+#elif defined(__MINGW32__)
-+    dev_name = strdup(path);
-+    auth_drive = DVDOpenImageFile( path, have_css );
- #endif
-     if( !dev_name ) {
-       if(verbose >= 1) {
-@@ -841,8 +838,8 @@
-   }
-     
-   if( dvd->css_state == 1 /* Need key init */ ) {
--    initAllCSSKeys( dvd );
--    dvd->css_state = 2;
-+//    initAllCSSKeys( dvd );
-+//    dvd->css_state = 2;
-   }
-   /*    
-         if( dvdinput_title( dvd_file->dvd->dev, (int)start ) < 0 ) {
-
---- dvdread.orig/dvdread_internal.h	2005-09-12 21:42:12.000000000 +0200
-+++ dvdread/dvdread_internal.h	2007-08-06 12:57:08.000000000 +0200
-@@ -3,12 +3,7 @@
- #define DVDREAD_INTERNAL_H
- 
- 
--#define CHECK_VALUE(arg)                                                \
--  if(!(arg)) {                                                          \
--    fprintf(stderr, "\n*** libdvdread: CHECK_VALUE failed in %s:%i ***" \
--            "\n*** for %s ***\n\n",                                     \
--            __FILE__, __LINE__, # arg );                                \
--  }
-+#define CHECK_VALUE(arg)
- 
- 
- int get_verbose(void);
-
---- dvdread.orig/ifo_print.c	2005-09-15 18:54:29.000000000 +0200
-+++ dvdread/ifo_print.c	2007-08-06 12:52:32.000000000 +0200
-@@ -793,14 +797,14 @@
-   ifoPrint_USER_OPS(&pgc->prohibited_ops);
-   
-   for(i = 0; i < 8; i++) {
--    if(pgc->audio_control[i] & 0x8000) { /* The 'is present' bit */
-+    if(pgc->audio_control[i].present) { /* The 'is present' bit */
-       printf("Audio stream %i control: %04x\n", 
-              i, pgc->audio_control[i]);
-     }
-   }
-   
-   for(i = 0; i < 32; i++) {
--    if(pgc->subp_control[i] & 0x80000000) { /* The 'is present' bit */
-+    if(pgc->subp_control[i].present) { /* The 'is present' bit */
-       printf("Subpicture stream %2i control: %08x\n", 
-              i, pgc->subp_control[i]);
-     }
-
---- dvdread.orig/ifo_read.c	2006-01-22 13:19:19.000000000 +0100
-+++ dvdread/ifo_read.c	2007-08-06 13:15:55.000000000 +0200
-@@ -110,7 +114,7 @@
- ifo_handle_t *ifoOpen(dvd_reader_t *dvd, int title) {
-   ifo_handle_t *ifofile;
- 
--  ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
-+  ifofile = malloc(sizeof(ifo_handle_t));
-   if(!ifofile)
-     return NULL;
- 
-@@ -240,7 +244,7 @@
- ifo_handle_t *ifoOpenVMGI(dvd_reader_t *dvd) {
-   ifo_handle_t *ifofile;
- 
--  ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
-+  ifofile = malloc(sizeof(ifo_handle_t));
-   if(!ifofile)
-     return NULL;
- 
-@@ -292,7 +296,7 @@
- ifo_handle_t *ifoOpenVTSI(dvd_reader_t *dvd, int title) {
-   ifo_handle_t *ifofile;
-   
--  ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
-+  ifofile = malloc(sizeof(ifo_handle_t));
-   if(!ifofile)
-     return NULL;
- 
-@@ -382,7 +386,7 @@
- static int ifoRead_VMG(ifo_handle_t *ifofile) {
-   vmgi_mat_t *vmgi_mat;
- 
--  vmgi_mat = (vmgi_mat_t *)malloc(sizeof(vmgi_mat_t));
-+  vmgi_mat = malloc(sizeof(vmgi_mat_t));
-   if(!vmgi_mat)
-     return 0;
- 
-@@ -473,7 +477,7 @@
-   vtsi_mat_t *vtsi_mat;
-   int i;
- 
--  vtsi_mat = (vtsi_mat_t *)malloc(sizeof(vtsi_mat_t));
-+  vtsi_mat = malloc(sizeof(vtsi_mat_t));
-   if(!vtsi_mat)
-     return 0;
-   
-@@ -606,7 +610,7 @@
- 
-   if(cmd_tbl->nr_of_pre != 0) {
-     unsigned int pre_cmds_size  = cmd_tbl->nr_of_pre * COMMAND_DATA_SIZE;
--    cmd_tbl->pre_cmds = (vm_cmd_t *)malloc(pre_cmds_size);
-+    cmd_tbl->pre_cmds = malloc(pre_cmds_size);
-     if(!cmd_tbl->pre_cmds)
-       return 0;
- 
-@@ -618,7 +622,7 @@
- 
-   if(cmd_tbl->nr_of_post != 0) {
-     unsigned int post_cmds_size = cmd_tbl->nr_of_post * COMMAND_DATA_SIZE;
--    cmd_tbl->post_cmds = (vm_cmd_t *)malloc(post_cmds_size);
-+    cmd_tbl->post_cmds = malloc(post_cmds_size);
-     if(!cmd_tbl->post_cmds) {
-       if(cmd_tbl->pre_cmds) 
-         free(cmd_tbl->pre_cmds);
-@@ -634,7 +638,7 @@
- 
-   if(cmd_tbl->nr_of_cell != 0) {
-     unsigned int cell_cmds_size = cmd_tbl->nr_of_cell * COMMAND_DATA_SIZE;
--    cmd_tbl->cell_cmds = (vm_cmd_t *)malloc(cell_cmds_size);
-+    cmd_tbl->cell_cmds = malloc(cell_cmds_size);
-     if(!cmd_tbl->cell_cmds) {
-       if(cmd_tbl->pre_cmds)
-         free(cmd_tbl->pre_cmds);
-@@ -751,10 +755,6 @@
-   B2N_16(pgc->cell_playback_offset);
-   B2N_16(pgc->cell_position_offset);
- 
--  for(i = 0; i < 8; i++)
--    B2N_16(pgc->audio_control[i]);
--  for(i = 0; i < 32; i++)
--    B2N_32(pgc->subp_control[i]);
-   for(i = 0; i < 16; i++)
-     B2N_32(pgc->palette[i]);
-   
-@@ -763,10 +763,10 @@
- 
-   /* verify time (look at print_time) */
-   for(i = 0; i < 8; i++)
--    if(!pgc->audio_control[i] & 0x8000) /* The 'is present' bit */
-+    if(!pgc->audio_control[i].present)
-       CHECK_ZERO(pgc->audio_control[i]);
-   for(i = 0; i < 32; i++)
--    if(!pgc->subp_control[i] & 0x80000000) /* The 'is present' bit */
-+    if(!pgc->subp_control[i].present)
-       CHECK_ZERO(pgc->subp_control[i]);
-   
-   /* Check that time is 0:0:0:0 also if nr_of_programs == 0 */
-@@ -880,7 +880,7 @@
-   if(ifofile->vmgi_mat->first_play_pgc == 0)
-     return 1;
-   
--  ifofile->first_play_pgc = (pgc_t *)malloc(sizeof(pgc_t));
-+  ifofile->first_play_pgc = malloc(sizeof(pgc_t));
-   if(!ifofile->first_play_pgc)
-     return 0;
-   
-@@ -934,7 +934,7 @@
-   if(!DVDFileSeek_(ifofile->file, ifofile->vmgi_mat->tt_srpt * DVD_BLOCK_LEN))
-     return 0;
- 
--  tt_srpt = (tt_srpt_t *)malloc(sizeof(tt_srpt_t));
-+  tt_srpt = malloc(sizeof(tt_srpt_t));
-   if(!tt_srpt)
-     return 0;
- 
-@@ -953,7 +953,7 @@
-   
-   info_length = tt_srpt->last_byte + 1 - TT_SRPT_SIZE;
- 
--  tt_srpt->title = (title_info_t *)malloc(info_length); 
-+  tt_srpt->title = malloc(info_length);
-   if(!tt_srpt->title) {
-     free(tt_srpt);
-     ifofile->tt_srpt = 0;
-@@ -1040,7 +1040,7 @@
-                    ifofile->vtsi_mat->vts_ptt_srpt * DVD_BLOCK_LEN))
-     return 0;
- 
--  vts_ptt_srpt = (vts_ptt_srpt_t *)malloc(sizeof(vts_ptt_srpt_t));
-+  vts_ptt_srpt = malloc(sizeof(vts_ptt_srpt_t));
-   if(!vts_ptt_srpt)
-     return 0;
- 
-@@ -1063,7 +1063,7 @@
-   
-   info_length = vts_ptt_srpt->last_byte + 1 - VTS_PTT_SRPT_SIZE;
-   
--  data = (uint32_t *)malloc(info_length); 
-+  data = malloc(info_length);
-   if(!data) {
-     free(vts_ptt_srpt);
-     ifofile->vts_ptt_srpt = 0;
-@@ -1183,7 +1183,7 @@
-   if(!DVDFileSeek_(ifofile->file, ifofile->vmgi_mat->ptl_mait * DVD_BLOCK_LEN))
-     return 0;
- 
--  ptl_mait = (ptl_mait_t *)malloc(sizeof(ptl_mait_t));
-+  ptl_mait = malloc(sizeof(ptl_mait_t));
-   if(!ptl_mait)
-     return 0;
- 
-@@ -1207,7 +1207,7 @@
-               <= ptl_mait->last_byte + 1 - PTL_MAIT_SIZE);
-   
-   info_length = ptl_mait->nr_of_countries * sizeof(ptl_mait_country_t);
--  ptl_mait->countries = (ptl_mait_country_t *)malloc(info_length);
-+  ptl_mait->countries = malloc(info_length);
-   if(!ptl_mait->countries) {
-     free(ptl_mait);
-     ifofile->ptl_mait = 0;
-@@ -1252,7 +1252,7 @@
-       return 0;
-     }
-     info_length = (ptl_mait->nr_of_vtss + 1) * sizeof(pf_level_t);
--    pf_temp = (uint16_t *)malloc(info_length);
-+    pf_temp = malloc(info_length);
-     if(!pf_temp) {
-       for(j = 0; j < i ; j++) {
-         free(ptl_mait->countries[j].pf_ptl_mai);
-@@ -1276,7 +1276,7 @@
-     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);
-+    ptl_mait->countries[i].pf_ptl_mai = malloc(info_length);
-     if(!ptl_mait->countries[i].pf_ptl_mai) {
-       free(pf_temp);
-       for(j = 0; j < i ; j++) {
-@@ -1340,7 +1340,7 @@
-   if(!DVDFileSeek_(ifofile->file, offset)) 
-     return 0;
-   
--  vts_tmapt = (vts_tmapt_t *)malloc(sizeof(vts_tmapt_t));
-+  vts_tmapt = malloc(sizeof(vts_tmapt_t));
-   if(!vts_tmapt)
-     return 0;
-   
-@@ -1362,7 +1362,7 @@
-   
-   info_length = vts_tmapt->nr_of_tmaps * 4;
-   
--  vts_tmap_srp = (uint32_t *)malloc(info_length);
-+  vts_tmap_srp = malloc(info_length);
-   if(!vts_tmap_srp) {
-     free(vts_tmapt);
-     ifofile->vts_tmapt = NULL;
-@@ -1388,7 +1388,7 @@
-   
-   info_length = vts_tmapt->nr_of_tmaps * sizeof(vts_tmap_t);
-   
--  vts_tmapt->tmap = (vts_tmap_t *)malloc(info_length);
-+  vts_tmapt->tmap = malloc(info_length);
-   if(!vts_tmapt->tmap) {
-     free(vts_tmap_srp);
-     free(vts_tmapt);
-@@ -1422,7 +1422,7 @@
-     
-     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);
-+    vts_tmapt->tmap[i].map_ent = malloc(info_length);
-     if(!vts_tmapt->tmap[i].map_ent) {
-       ifoFree_VTS_TMAPT(ifofile);
-       return 0;
-@@ -1472,7 +1472,7 @@
-   if(ifofile->vtsi_mat->vts_c_adt == 0) /* mandatory */
-     return 0;
- 
--  ifofile->vts_c_adt = (c_adt_t *)malloc(sizeof(c_adt_t));
-+  ifofile->vts_c_adt = malloc(sizeof(c_adt_t));
-   if(!ifofile->vts_c_adt)
-     return 0;
- 
-@@ -1504,7 +1504,7 @@
-     return 0;
-   }
-   
--  ifofile->menu_c_adt = (c_adt_t *)malloc(sizeof(c_adt_t));
-+  ifofile->menu_c_adt = malloc(sizeof(c_adt_t));
-   if(!ifofile->menu_c_adt)
-     return 0;
- 
-@@ -1548,7 +1548,7 @@
-     c_adt->nr_of_vobs = info_length / sizeof(cell_adr_t);
-   }
-   
--  c_adt->cell_adr_table = (cell_adr_t *)malloc(info_length);
-+  c_adt->cell_adr_table = malloc(info_length);
-   if(!c_adt->cell_adr_table)
-     return 0;
- 
-@@ -1608,7 +1608,7 @@
-   if(ifofile->vtsi_mat->vts_vobu_admap == 0) /* mandatory */
-     return 0;
-   
--  ifofile->vts_vobu_admap = (vobu_admap_t *)malloc(sizeof(vobu_admap_t));
-+  ifofile->vts_vobu_admap = malloc(sizeof(vobu_admap_t));
-   if(!ifofile->vts_vobu_admap)
-     return 0;
- 
-@@ -1640,7 +1640,7 @@
-     return 0;
-   }
-   
--  ifofile->menu_vobu_admap = (vobu_admap_t *)malloc(sizeof(vobu_admap_t));
-+  ifofile->menu_vobu_admap = malloc(sizeof(vobu_admap_t));
-   if(!ifofile->menu_vobu_admap)
-     return 0;
-   
-@@ -1673,7 +1673,7 @@
-      Titles with a VOBS that has no VOBUs. */
-   CHECK_VALUE(info_length % sizeof(uint32_t) == 0);
-   
--  vobu_admap->vobu_start_sectors = (uint32_t *)malloc(info_length); 
-+  vobu_admap->vobu_start_sectors = malloc(info_length);
-   if(!vobu_admap->vobu_start_sectors) {
-     return 0;
-   }
-@@ -1725,7 +1725,7 @@
-   if(ifofile->vtsi_mat->vts_pgcit == 0) /* mandatory */
-     return 0;
-   
--  ifofile->vts_pgcit = (pgcit_t *)malloc(sizeof(pgcit_t));
-+  ifofile->vts_pgcit = malloc(sizeof(pgcit_t));
-   if(!ifofile->vts_pgcit)
-     return 0;
- 
-@@ -1860,7 +1860,7 @@
-     return 0;
-   }
-   
--  ifofile->pgci_ut = (pgci_ut_t *)malloc(sizeof(pgci_ut_t));
-+  ifofile->pgci_ut = malloc(sizeof(pgci_ut_t));
-   if(!ifofile->pgci_ut)
-     return 0;
-   
-@@ -2050,7 +2050,7 @@
-   if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN))
-     return 0;
- 
--  vts_atrt = (vts_atrt_t *)malloc(sizeof(vts_atrt_t));
-+  vts_atrt = malloc(sizeof(vts_atrt_t));
-   if(!vts_atrt)
-     return 0;
- 
-@@ -2072,7 +2072,7 @@
-               VTS_ATRT_SIZE < vts_atrt->last_byte + 1);
- 
-   info_length = vts_atrt->nr_of_vtss * sizeof(uint32_t);
--  data = (uint32_t *)malloc(info_length);
-+  data = malloc(info_length);
-   if(!data) {
-     free(vts_atrt);
-     ifofile->vts_atrt = 0;
-@@ -2094,7 +2094,7 @@
-   }
-   
-   info_length = vts_atrt->nr_of_vtss * sizeof(vts_attributes_t);
--  vts_atrt->vts = (vts_attributes_t *)malloc(info_length);
-+  vts_atrt->vts = malloc(info_length);
-   if(!vts_atrt->vts) {
-     free(data);
-     free(vts_atrt);
-@@ -2150,7 +2150,7 @@
-                    ifofile->vmgi_mat->txtdt_mgi * DVD_BLOCK_LEN))
-     return 0;
-   
--  txtdt_mgi = (txtdt_mgi_t *)malloc(sizeof(txtdt_mgi_t));
-+  txtdt_mgi = malloc(sizeof(txtdt_mgi_t));
-   if(!txtdt_mgi) {
-     return 0;
-   }
-
---- dvdread.orig/ifo_types.h	2005-06-23 00:18:54.000000000 +0200
-+++ dvdread/ifo_types.h	2005-06-23 00:19:10.000000000 +0200
-@@ -403,6 +407,55 @@
- } ATTRIBUTE_PACKED user_ops_t;
- 
- /**
-+ * Subpicture stream mapping for a subtitle
-+ */
-+typedef struct {
-+#ifdef WORDS_BIGENDIAN
-+  unsigned int present   : 1;
-+  unsigned int zero1     : 2;
-+  unsigned int s_4p3     : 5; /* stream for 4:3 on any display */
-+
-+  unsigned int zero2     : 3;
-+  unsigned int s_wide    : 5; /* stream for 16:9 on widescreen display */
-+
-+  unsigned int zero3     : 3;
-+  unsigned int s_lbox    : 5; /* stream for 16:9 on letterboxed 4:3 display */
-+
-+  unsigned int zero4     : 3;
-+  unsigned int s_panscan : 5; /* stream for 16:9 with pan&scan data on 4:3 display */
-+#else
-+  unsigned int s_4p3     : 5; /* stream for 4:3 on any display */
-+  unsigned int zero1     : 2;
-+  unsigned int present   : 1;
-+
-+  unsigned int s_wide    : 5; /* stream for 16:9 on widescreen display */
-+  unsigned int zero2     : 3;
-+
-+  unsigned int s_lbox    : 5; /* stream for 16:9 on letterboxed 4:3 display */
-+  unsigned int zero3     : 3;
-+
-+  unsigned int s_panscan : 5; /* stream for 16:9 with pan&scan data on 4:3 display */
-+  unsigned int zero4     : 3;
-+#endif
-+} ATTRIBUTE_PACKED subp_mapping_t;
-+
-+/**
-+ * Audio stream mapping for a soundtrack
-+ */
-+typedef struct {
-+#ifdef WORDS_BIGENDIAN
-+  unsigned int present : 1;
-+  unsigned int zero1   : 4;
-+  unsigned int s_audio : 3;
-+#else
-+  unsigned int s_audio : 3;
-+  unsigned int zero1   : 4;
-+  unsigned int present : 1;
-+#endif
-+  uint8_t zero2;
-+} ATTRIBUTE_PACKED audio_mapping_t;
-+
-+/**
-  * Program Chain Information.
-  */
- typedef struct {
-@@ -411,8 +464,8 @@
-   uint8_t  nr_of_cells;
-   dvd_time_t playback_time;
-   user_ops_t prohibited_ops;
--  uint16_t audio_control[8]; /* New type? */
--  uint32_t subp_control[32]; /* New type? */
-+  audio_mapping_t audio_control[8];
-+  subp_mapping_t subp_control[32];
-   uint16_t next_pgc_nr;
-   uint16_t prev_pgc_nr;
-   uint16_t goup_pgc_nr;
--- a/dvdread/md5.c	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,418 +0,0 @@
-/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
-/* md5.c - Functions to compute MD5 message digest of files or memory blocks
-   according to the definition of MD5 in RFC 1321 from April 1992.
-   Copyright (C) 1995, 1996, 2001 Free Software Foundation, Inc.
-   NOTE: The canonical source of this file is maintained with the GNU C
-   Library.  Bugs can be reported to bug-glibc@prep.ai.mit.edu.
-
-   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, or (at your option) any
-   later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software Foundation,
-   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
-
-/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.  */
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include <sys/types.h>
-
-#if STDC_HEADERS || defined _LIBC
-# include <stdlib.h>
-# include <string.h>
-#else
-# ifndef HAVE_MEMCPY
-#  define memcpy(d, s, n) bcopy ((s), (d), (n))
-# endif
-#endif
-
-#include "md5.h"
-//#include "unlocked-io.h"
-
-#ifdef _LIBC
-# include <endian.h>
-# if __BYTE_ORDER == __BIG_ENDIAN
-#  define WORDS_BIGENDIAN 1
-# endif
-#endif
-
-#ifdef WORDS_BIGENDIAN
-# define SWAP(n)                                                        \
-  (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
-#else
-# define SWAP(n) (n)
-#endif
-
-
-/* This array contains the bytes used to pad the buffer to the next
-   64-byte boundary.  (RFC 1321, 3.1: Step 1)  */
-static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ...  */ };
-
-
-/* Initialize structure containing state of computation.
-   (RFC 1321, 3.3: Step 3)  */
-void
-md5_init_ctx (ctx)
-     struct md5_ctx *ctx;
-{
-  ctx->A = 0x67452301;
-  ctx->B = 0xefcdab89;
-  ctx->C = 0x98badcfe;
-  ctx->D = 0x10325476;
-
-  ctx->total[0] = ctx->total[1] = 0;
-  ctx->buflen = 0;
-}
-
-/* Put result from CTX in first 16 bytes following RESBUF.  The result
-   must be in little endian byte order.
-
-   IMPORTANT: On some systems it is required that RESBUF is correctly
-   aligned for a 32 bits value.  */
-void *
-md5_read_ctx (ctx, resbuf)
-     const struct md5_ctx *ctx;
-     void *resbuf;
-{
-  ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A);
-  ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B);
-  ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C);
-  ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D);
-
-  return resbuf;
-}
-
-/* Process the remaining bytes in the internal buffer and the usual
-   prolog according to the standard and write the result to RESBUF.
-
-   IMPORTANT: On some systems it is required that RESBUF is correctly
-   aligned for a 32 bits value.  */
-void *
-md5_finish_ctx (ctx, resbuf)
-     struct md5_ctx *ctx;
-     void *resbuf;
-{
-  /* Take yet unprocessed bytes into account.  */
-  md5_uint32 bytes = ctx->buflen;
-  size_t pad;
-
-  /* Now count remaining bytes.  */
-  ctx->total[0] += bytes;
-  if (ctx->total[0] < bytes)
-    ++ctx->total[1];
-
-  pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
-  memcpy (&ctx->buffer[bytes], fillbuf, pad);
-
-  /* Put the 64-bit file length in *bits* at the end of the buffer.  */
-  *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3);
-  *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) |
-                                                        (ctx->total[0] >> 29));
-
-  /* Process last bytes.  */
-  md5_process_block (ctx->buffer, bytes + pad + 8, ctx);
-
-  return md5_read_ctx (ctx, resbuf);
-}
-
-/* Compute MD5 message digest for bytes read from STREAM.  The
-   resulting message digest number will be written into the 16 bytes
-   beginning at RESBLOCK.  */
-int
-md5_stream (stream, resblock)
-     FILE *stream;
-     void *resblock;
-{
-  /* Important: BLOCKSIZE must be a multiple of 64.  */
-#define BLOCKSIZE 4096
-  struct md5_ctx ctx;
-  char buffer[BLOCKSIZE + 72];
-  size_t sum;
-
-  /* Initialize the computation context.  */
-  md5_init_ctx (&ctx);
-
-  /* Iterate over full file contents.  */
-  while (1)
-    {
-      /* We read the file in blocks of BLOCKSIZE bytes.  One call of the
-         computation function processes the whole buffer so that with the
-         next round of the loop another block can be read.  */
-      size_t n;
-      sum = 0;
-
-      /* Read block.  Take care for partial reads.  */
-      do
-        {
-          n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);
-
-          sum += n;
-        }
-      while (sum < BLOCKSIZE && n != 0);
-      if (n == 0 && ferror (stream))
-        return 1;
-
-      /* If end of file is reached, end the loop.  */
-      if (n == 0)
-        break;
-
-      /* Process buffer with BLOCKSIZE bytes.  Note that
-         BLOCKSIZE % 64 == 0
-      */
-      md5_process_block (buffer, BLOCKSIZE, &ctx);
-    }
-
-  /* Add the last bytes if necessary.  */
-  if (sum > 0)
-    md5_process_bytes (buffer, sum, &ctx);
-
-  /* Construct result in desired memory.  */
-  md5_finish_ctx (&ctx, resblock);
-  return 0;
-}
-
-/* Compute MD5 message digest for LEN bytes beginning at BUFFER.  The
-   result is always in little endian byte order, so that a byte-wise
-   output yields to the wanted ASCII representation of the message
-   digest.  */
-void *
-md5_buffer (buffer, len, resblock)
-     const char *buffer;
-     size_t len;
-     void *resblock;
-{
-  struct md5_ctx ctx;
-
-  /* Initialize the computation context.  */
-  md5_init_ctx (&ctx);
-
-  /* Process whole buffer but last len % 64 bytes.  */
-  md5_process_bytes (buffer, len, &ctx);
-
-  /* Put result in desired memory area.  */
-  return md5_finish_ctx (&ctx, resblock);
-}
-
-
-void
-md5_process_bytes (buffer, len, ctx)
-     const void *buffer;
-     size_t len;
-     struct md5_ctx *ctx;
-{
-  /* When we already have some bits in our internal buffer concatenate
-     both inputs first.  */
-  if (ctx->buflen != 0)
-    {
-      size_t left_over = ctx->buflen;
-      size_t add = 128 - left_over > len ? len : 128 - left_over;
-
-      memcpy (&ctx->buffer[left_over], buffer, add);
-      ctx->buflen += add;
-
-      if (left_over + add > 64)
-        {
-          md5_process_block (ctx->buffer, (left_over + add) & ~63, ctx);
-          /* The regions in the following copy operation cannot overlap.  */
-          memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
-                  (left_over + add) & 63);
-          ctx->buflen = (left_over + add) & 63;
-        }
-
-      buffer = (const char *) buffer + add;
-      len -= add;
-    }
-
-  /* Process available complete blocks.  */
-  if (len > 64)
-    {
-      md5_process_block (buffer, len & ~63, ctx);
-      buffer = (const char *) buffer + (len & ~63);
-      len &= 63;
-    }
-
-  /* Move remaining bytes in internal buffer.  */
-  if (len > 0)
-    {
-      memcpy (ctx->buffer, buffer, len);
-      ctx->buflen = len;
-    }
-}
-
-
-/* These are the four functions used in the four steps of the MD5 algorithm
-   and defined in the RFC 1321.  The first function is a little bit optimized
-   (as found in Colin Plumbs public domain implementation).  */
-/* #define FF(b, c, d) ((b & c) | (~b & d)) */
-#define FF(b, c, d) (d ^ (b & (c ^ d)))
-#define FG(b, c, d) FF (d, b, c)
-#define FH(b, c, d) (b ^ c ^ d)
-#define FI(b, c, d) (c ^ (b | ~d))
-
-/* Process LEN bytes of BUFFER, accumulating context into CTX.
-   It is assumed that LEN % 64 == 0.  */
-
-void
-md5_process_block (buffer, len, ctx)
-     const void *buffer;
-     size_t len;
-     struct md5_ctx *ctx;
-{
-  md5_uint32 correct_words[16];
-  const md5_uint32 *words = buffer;
-  size_t nwords = len / sizeof (md5_uint32);
-  const md5_uint32 *endp = words + nwords;
-  md5_uint32 A = ctx->A;
-  md5_uint32 B = ctx->B;
-  md5_uint32 C = ctx->C;
-  md5_uint32 D = ctx->D;
-
-  /* First increment the byte count.  RFC 1321 specifies the possible
-     length of the file up to 2^64 bits.  Here we only compute the
-     number of bytes.  Do a double word increment.  */
-  ctx->total[0] += len;
-  if (ctx->total[0] < len)
-    ++ctx->total[1];
-
-  /* Process all bytes in the buffer with 64 bytes in each round of
-     the loop.  */
-  while (words < endp)
-    {
-      md5_uint32 *cwp = correct_words;
-      md5_uint32 A_save = A;
-      md5_uint32 B_save = B;
-      md5_uint32 C_save = C;
-      md5_uint32 D_save = D;
-
-      /* First round: using the given function, the context and a constant
-         the next context is computed.  Because the algorithms processing
-         unit is a 32-bit word and it is determined to work on words in
-         little endian byte order we perhaps have to change the byte order
-         before the computation.  To reduce the work for the next steps
-         we store the swapped words in the array CORRECT_WORDS.  */
-
-#define OP(a, b, c, d, s, T)                                    \
-      do                                                        \
-        {                                                       \
-          a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T;     \
-          ++words;                                              \
-          a = rol (a, s);                                       \
-          a += b;                                               \
-        }                                                       \
-      while (0)
-
-      /* Before we start, one word to the strange constants.
-         They are defined in RFC 1321 as
-
-         T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64, or
-         perl -e 'foreach(1..64){printf "0x%08x\n", int (4294967296 * abs (sin $_))}'
-      */
-
-      /* Round 1.  */
-      OP (A, B, C, D,  7, 0xd76aa478);
-      OP (D, A, B, C, 12, 0xe8c7b756);
-      OP (C, D, A, B, 17, 0x242070db);
-      OP (B, C, D, A, 22, 0xc1bdceee);
-      OP (A, B, C, D,  7, 0xf57c0faf);
-      OP (D, A, B, C, 12, 0x4787c62a);
-      OP (C, D, A, B, 17, 0xa8304613);
-      OP (B, C, D, A, 22, 0xfd469501);
-      OP (A, B, C, D,  7, 0x698098d8);
-      OP (D, A, B, C, 12, 0x8b44f7af);
-      OP (C, D, A, B, 17, 0xffff5bb1);
-      OP (B, C, D, A, 22, 0x895cd7be);
-      OP (A, B, C, D,  7, 0x6b901122);
-      OP (D, A, B, C, 12, 0xfd987193);
-      OP (C, D, A, B, 17, 0xa679438e);
-      OP (B, C, D, A, 22, 0x49b40821);
-
-      /* For the second to fourth round we have the possibly swapped words
-         in CORRECT_WORDS.  Redefine the macro to take an additional first
-         argument specifying the function to use.  */
-#undef OP
-#define OP(f, a, b, c, d, k, s, T)                      \
-      do                                                \
-        {                                               \
-          a += f (b, c, d) + correct_words[k] + T;      \
-          a = rol (a, s);                               \
-          a += b;                                       \
-        }                                               \
-      while (0)
-
-      /* Round 2.  */
-      OP (FG, A, B, C, D,  1,  5, 0xf61e2562);
-      OP (FG, D, A, B, C,  6,  9, 0xc040b340);
-      OP (FG, C, D, A, B, 11, 14, 0x265e5a51);
-      OP (FG, B, C, D, A,  0, 20, 0xe9b6c7aa);
-      OP (FG, A, B, C, D,  5,  5, 0xd62f105d);
-      OP (FG, D, A, B, C, 10,  9, 0x02441453);
-      OP (FG, C, D, A, B, 15, 14, 0xd8a1e681);
-      OP (FG, B, C, D, A,  4, 20, 0xe7d3fbc8);
-      OP (FG, A, B, C, D,  9,  5, 0x21e1cde6);
-      OP (FG, D, A, B, C, 14,  9, 0xc33707d6);
-      OP (FG, C, D, A, B,  3, 14, 0xf4d50d87);
-      OP (FG, B, C, D, A,  8, 20, 0x455a14ed);
-      OP (FG, A, B, C, D, 13,  5, 0xa9e3e905);
-      OP (FG, D, A, B, C,  2,  9, 0xfcefa3f8);
-      OP (FG, C, D, A, B,  7, 14, 0x676f02d9);
-      OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
-
-      /* Round 3.  */
-      OP (FH, A, B, C, D,  5,  4, 0xfffa3942);
-      OP (FH, D, A, B, C,  8, 11, 0x8771f681);
-      OP (FH, C, D, A, B, 11, 16, 0x6d9d6122);
-      OP (FH, B, C, D, A, 14, 23, 0xfde5380c);
-      OP (FH, A, B, C, D,  1,  4, 0xa4beea44);
-      OP (FH, D, A, B, C,  4, 11, 0x4bdecfa9);
-      OP (FH, C, D, A, B,  7, 16, 0xf6bb4b60);
-      OP (FH, B, C, D, A, 10, 23, 0xbebfbc70);
-      OP (FH, A, B, C, D, 13,  4, 0x289b7ec6);
-      OP (FH, D, A, B, C,  0, 11, 0xeaa127fa);
-      OP (FH, C, D, A, B,  3, 16, 0xd4ef3085);
-      OP (FH, B, C, D, A,  6, 23, 0x04881d05);
-      OP (FH, A, B, C, D,  9,  4, 0xd9d4d039);
-      OP (FH, D, A, B, C, 12, 11, 0xe6db99e5);
-      OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8);
-      OP (FH, B, C, D, A,  2, 23, 0xc4ac5665);
-
-      /* Round 4.  */
-      OP (FI, A, B, C, D,  0,  6, 0xf4292244);
-      OP (FI, D, A, B, C,  7, 10, 0x432aff97);
-      OP (FI, C, D, A, B, 14, 15, 0xab9423a7);
-      OP (FI, B, C, D, A,  5, 21, 0xfc93a039);
-      OP (FI, A, B, C, D, 12,  6, 0x655b59c3);
-      OP (FI, D, A, B, C,  3, 10, 0x8f0ccc92);
-      OP (FI, C, D, A, B, 10, 15, 0xffeff47d);
-      OP (FI, B, C, D, A,  1, 21, 0x85845dd1);
-      OP (FI, A, B, C, D,  8,  6, 0x6fa87e4f);
-      OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
-      OP (FI, C, D, A, B,  6, 15, 0xa3014314);
-      OP (FI, B, C, D, A, 13, 21, 0x4e0811a1);
-      OP (FI, A, B, C, D,  4,  6, 0xf7537e82);
-      OP (FI, D, A, B, C, 11, 10, 0xbd3af235);
-      OP (FI, C, D, A, B,  2, 15, 0x2ad7d2bb);
-      OP (FI, B, C, D, A,  9, 21, 0xeb86d391);
-
-      /* Add the starting values of the context.  */
-      A += A_save;
-      B += B_save;
-      C += C_save;
-      D += D_save;
-    }
-
-  /* Put checksum in context given as argument.  */
-  ctx->A = A;
-  ctx->B = B;
-  ctx->C = C;
-  ctx->D = D;
-}
--- a/dvdread/md5.h	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,162 +0,0 @@
-/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
-/* md5.h - Declaration of functions and data types used for MD5 sum
-   computing library functions.
-   Copyright (C) 1995, 1996, 1999 Free Software Foundation, Inc.
-   NOTE: The canonical source of this file is maintained with the GNU C
-   Library.  Bugs can be reported to bug-glibc@prep.ai.mit.edu.
-
-   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, or (at your option) any
-   later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software Foundation,
-   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
-
-#ifndef _MD5_H
-#define _MD5_H 1
-
-#include <stdio.h>
-
-#if defined HAVE_LIMITS_H || _LIBC
-# include <limits.h>
-#endif
-
-/* The following contortions are an attempt to use the C preprocessor
-   to determine an unsigned integral type that is 32 bits wide.  An
-   alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but
-   doing that would require that the configure script compile and *run*
-   the resulting executable.  Locally running cross-compiled executables
-   is usually not possible.  */
-
-#ifdef _LIBC
-# include <sys/types.h>
-typedef u_int32_t md5_uint32;
-#else
-# if defined __STDC__ && __STDC__
-#  define UINT_MAX_32_BITS 4294967295U
-# else
-#  define UINT_MAX_32_BITS 0xFFFFFFFF
-# endif
-
-/* If UINT_MAX isn't defined, assume it's a 32-bit type.
-   This should be valid for all systems GNU cares about because
-   that doesn't include 16-bit systems, and only modern systems
-   (that certainly have <limits.h>) have 64+-bit integral types.  */
-
-# ifndef UINT_MAX
-#  define UINT_MAX UINT_MAX_32_BITS
-# endif
-
-# if UINT_MAX == UINT_MAX_32_BITS
-typedef unsigned int md5_uint32;
-# else
-#  if USHRT_MAX == UINT_MAX_32_BITS
-typedef unsigned short md5_uint32;
-#  else
-#   if ULONG_MAX == UINT_MAX_32_BITS
-typedef unsigned long md5_uint32;
-#   else
-/* The following line is intended to evoke an error.
-   Using #error is not portable enough.  */
-"Cannot determine unsigned 32-bit data type."
-#   endif
-#  endif
-# endif
-#endif
-
-#undef __P
-#if defined (__STDC__) && __STDC__
-#define __P(x) x
-#else
-#define __P(x) ()
-#endif
-
-/* Structure to save state of computation between the single steps.  */
-struct md5_ctx
-{
-  md5_uint32 A;
-  md5_uint32 B;
-  md5_uint32 C;
-  md5_uint32 D;
-
-  md5_uint32 total[2];
-  md5_uint32 buflen;
-  char buffer[128];
-};
-
-/*
- * The following three functions are build up the low level used in
- * the functions `md5_stream' and `md5_buffer'.
- */
-
-/* Initialize structure containing state of computation.
-   (RFC 1321, 3.3: Step 3)  */
-extern void md5_init_ctx __P ((struct md5_ctx *ctx));
-
-/* Starting with the result of former calls of this function (or the
-   initialization function update the context for the next LEN bytes
-   starting at BUFFER.
-   It is necessary that LEN is a multiple of 64!!! */
-extern void md5_process_block __P ((const void *buffer, size_t len,
-                                    struct md5_ctx *ctx));
-
-/* Starting with the result of former calls of this function (or the
-   initialization function update the context for the next LEN bytes
-   starting at BUFFER.
-   It is NOT required that LEN is a multiple of 64.  */
-extern void md5_process_bytes __P ((const void *buffer, size_t len,
-                                    struct md5_ctx *ctx));
-
-/* Process the remaining bytes in the buffer and put result from CTX
-   in first 16 bytes following RESBUF.  The result is always in little
-   endian byte order, so that a byte-wise output yields to the wanted
-   ASCII representation of the message digest.
-
-   IMPORTANT: On some systems it is required that RESBUF be correctly
-   aligned for a 32 bits value.  */
-extern void *md5_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf));
-
-
-/* Put result from CTX in first 16 bytes following RESBUF.  The result is
-   always in little endian byte order, so that a byte-wise output yields
-   to the wanted ASCII representation of the message digest.
-
-   IMPORTANT: On some systems it is required that RESBUF is correctly
-   aligned for a 32 bits value.  */
-extern void *md5_read_ctx __P ((const struct md5_ctx *ctx, void *resbuf));
-
-
-/* Compute MD5 message digest for bytes read from STREAM.  The
-   resulting message digest number will be written into the 16 bytes
-   beginning at RESBLOCK.  */
-extern int md5_stream __P ((FILE *stream, void *resblock));
-
-/* Compute MD5 message digest for LEN bytes beginning at BUFFER.  The
-   result is always in little endian byte order, so that a byte-wise
-   output yields to the wanted ASCII representation of the message
-   digest.  */
-extern void *md5_buffer __P ((const char *buffer, size_t len, void *resblock));
-
-/* The following is from gnupg-1.0.2's cipher/bithelp.h.  */
-/* Rotate a 32 bit integer by n bytes */
-#if defined __GNUC__ && defined __i386__
-static inline md5_uint32
-rol(md5_uint32 x, int n)
-{
-  __asm__("roll %%cl,%0"
-          :"=r" (x)
-          :"0" (x),"c" (n));
-  return x;
-}
-#else
-# define rol(x,n) ( ((x) << (n)) | ((x) >> (32-(n))) )
-#endif
-
-#endif
--- a/dvdread/nav_print.c	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,286 +0,0 @@
-/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
-/*
- * Copyright (C) 2000, 2001, 2002, 2003 Håkan Hjort <d95hjort@dtek.chalmers.se>
- *
- * Much of the contents in this file is based on VOBDUMP.
- *
- * VOBDUMP: a program for examining DVD .VOB filse
- *
- * Copyright 1998, 1999 Eric Smith <eric@brouhaha.com>
- *
- * VOBDUMP is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.  Note that I am not
- * granting permission to redistribute or modify VOBDUMP under the
- * terms of any later version of the General Public License.
- *
- * This program is distributed in the hope that it will be useful (or
- * at least amusing), but WITHOUT ANY WARRANTY; without even the
- * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "config.h"
-
-#include <stdio.h>
-
-#if defined(HAVE_INTTYPES_H)
-#include <inttypes.h>
-#elif defined(HAVE_STDINT_H)
-#include <stdint.h>
-#endif
-
-#include "nav_types.h"
-#include "nav_print.h"
-#include "cmd_print.h"
-#include "dvdread_internal.h"
-
-static void print_time(dvd_time_t *dtime) {
-  const char *rate;
-  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,
-         dtime->minute,
-         dtime->second,
-         dtime->frame_u & 0x3f);
-  switch((dtime->frame_u & 0xc0) >> 6) {
-  case 1:
-    rate = "25.00";
-    break;
-  case 3:
-    rate = "29.97";
-    break;
-  default:
-    rate = "(please send a bug report)";
-    break;
-  } 
-  printf(" @ %s fps", rate);
-}
-
-
-static void navPrint_PCI_GI(pci_gi_t *pci_gi) {
-  int i;
-
-  printf("pci_gi:\n");
-  printf("nv_pck_lbn    0x%08x\n", pci_gi->nv_pck_lbn);
-  printf("vobu_cat      0x%04x\n", pci_gi->vobu_cat);
-  printf("vobu_uop_ctl  0x%08x\n", *(uint32_t*)&pci_gi->vobu_uop_ctl);
-  printf("vobu_s_ptm    0x%08x\n", pci_gi->vobu_s_ptm);
-  printf("vobu_e_ptm    0x%08x\n", pci_gi->vobu_e_ptm);
-  printf("vobu_se_e_ptm 0x%08x\n", pci_gi->vobu_se_e_ptm);
-  printf("e_eltm        ");
-  print_time(&pci_gi->e_eltm);
-  printf("\n");
-  
-  printf("vobu_isrc     \"");
-  for(i = 0; i < 32; i++) {
-    char c = pci_gi->vobu_isrc[i];
-    if((c >= ' ') && (c <= '~'))
-      printf("%c", c);
-    else
-      printf(".");
-  }
-  printf("\"\n");
-}
-
-static void navPrint_NSML_AGLI(nsml_agli_t *nsml_agli) {
-  int i, j = 0;
-  
-  for(i = 0; i < 9; i++)
-    j |= nsml_agli->nsml_agl_dsta[i];
-  if(j == 0)
-    return;
-  
-  printf("nsml_agli:\n");
-  for(i = 0; i < 9; i++)
-    if(nsml_agli->nsml_agl_dsta[i])
-      printf("nsml_agl_c%d_dsta  0x%08x\n", i + 1, 
-             nsml_agli->nsml_agl_dsta[i]);
-}
-
-static void navPrint_HL_GI(hl_gi_t *hl_gi, int *btngr_ns, int *btn_ns) {
-  
-  if((hl_gi->hli_ss & 0x03) == 0)
-    return;
-  
-  printf("hl_gi:\n");
-  printf("hli_ss        0x%01x\n", hl_gi->hli_ss & 0x03);
-  printf("hli_s_ptm     0x%08x\n", hl_gi->hli_s_ptm);
-  printf("hli_e_ptm     0x%08x\n", hl_gi->hli_e_ptm);
-  printf("btn_se_e_ptm  0x%08x\n", hl_gi->btn_se_e_ptm);
-
-  *btngr_ns = hl_gi->btngr_ns;
-  printf("btngr_ns      %d\n",  hl_gi->btngr_ns);
-  printf("btngr%d_dsp_ty    0x%02x\n", 1, hl_gi->btngr1_dsp_ty);
-  printf("btngr%d_dsp_ty    0x%02x\n", 2, hl_gi->btngr2_dsp_ty);
-  printf("btngr%d_dsp_ty    0x%02x\n", 3, hl_gi->btngr3_dsp_ty);
-  
-  printf("btn_ofn       %d\n", hl_gi->btn_ofn);
-  *btn_ns = hl_gi->btn_ns;
-  printf("btn_ns        %d\n", hl_gi->btn_ns);
-  printf("nsl_btn_ns    %d\n", hl_gi->nsl_btn_ns);
-  printf("fosl_btnn     %d\n", hl_gi->fosl_btnn);
-  printf("foac_btnn     %d\n", hl_gi->foac_btnn);
-}
-
-static void navPrint_BTN_COLIT(btn_colit_t *btn_colit) {
-  int i, j;
-  
-  j = 0;
-  for(i = 0; i < 6; i++)
-    j |= btn_colit->btn_coli[i/2][i&1];
-  if(j == 0)
-    return;
-  
-  printf("btn_colit:\n");
-  for(i = 0; i < 3; i++)
-    for(j = 0; j < 2; j++)
-      printf("btn_cqoli %d  %s_coli:  %08x\n",
-             i, (j == 0) ? "sl" : "ac",
-             btn_colit->btn_coli[i][j]);
-}
-
-static void navPrint_BTNIT(btni_t *btni_table, int btngr_ns, int btn_ns) {
-  int i, j;
-  
-  printf("btnit:\n");
-  printf("btngr_ns: %i\n", btngr_ns);
-  printf("btn_ns: %i\n", btn_ns);
-  
-  if(btngr_ns == 0)
-    return;
-  
-  for(i = 0; i < btngr_ns; i++) {
-    for(j = 0; j < (36 / btngr_ns); j++) {
-      if(j < btn_ns) {
-        btni_t *btni = &btni_table[(36 / btngr_ns) * i + j];
-        
-        printf("group %d btni %d:  ", i+1, j+1);
-        printf("btn_coln %d, auto_action_mode %d\n",
-               btni->btn_coln, btni->auto_action_mode);
-        printf("coords   (%d, %d) .. (%d, %d)\n",
-               btni->x_start, btni->y_start, btni->x_end, btni->y_end);
-        
-        printf("up %d, ", btni->up);
-        printf("down %d, ", btni->down);
-        printf("left %d, ", btni->left);
-        printf("right %d\n", btni->right);
-        
-        cmdPrint_CMD(0, &btni->cmd);
-        printf("\n");
-      }
-    }
-  }
-}
-
-static void navPrint_HLI(hli_t *hli) {
-  int btngr_ns = 0, btn_ns = 0;
-  
-  printf("hli:\n");
-  navPrint_HL_GI(&hli->hl_gi, & btngr_ns, & btn_ns);
-  navPrint_BTN_COLIT(&hli->btn_colit);
-  navPrint_BTNIT(hli->btnit, btngr_ns, btn_ns);
-}
-
-void navPrint_PCI(pci_t *pci) {
-  printf("pci packet:\n");
-  navPrint_PCI_GI(&pci->pci_gi);
-  navPrint_NSML_AGLI(&pci->nsml_agli);
-  navPrint_HLI(&pci->hli);
-}
-
-static void navPrint_DSI_GI(dsi_gi_t *dsi_gi) {
-  printf("dsi_gi:\n");
-  printf("nv_pck_scr     0x%08x\n", dsi_gi->nv_pck_scr);
-  printf("nv_pck_lbn     0x%08x\n", dsi_gi->nv_pck_lbn );
-  printf("vobu_ea        0x%08x\n", dsi_gi->vobu_ea);
-  printf("vobu_1stref_ea 0x%08x\n", dsi_gi->vobu_1stref_ea);
-  printf("vobu_2ndref_ea 0x%08x\n", dsi_gi->vobu_2ndref_ea);
-  printf("vobu_3rdref_ea 0x%08x\n", dsi_gi->vobu_3rdref_ea);
-  printf("vobu_vob_idn   0x%04x\n", dsi_gi->vobu_vob_idn);
-  printf("vobu_c_idn     0x%02x\n", dsi_gi->vobu_c_idn);
-  printf("c_eltm         ");
-  print_time(&dsi_gi->c_eltm);
-  printf("\n");
-}
-
-static void navPrint_SML_PBI(sml_pbi_t *sml_pbi) {
-  printf("sml_pbi:\n");
-  printf("category 0x%04x\n", sml_pbi->category);
-  if(sml_pbi->category & 0x8000)
-    printf("VOBU is in preunit\n");
-  if(sml_pbi->category & 0x4000)
-    printf("VOBU is in ILVU\n");
-  if(sml_pbi->category & 0x2000)
-    printf("VOBU at the beginning of ILVU\n");
-  if(sml_pbi->category & 0x1000)
-    printf("VOBU at end of PREU of ILVU\n");
-  
-  printf("ilvu_ea       0x%08x\n", sml_pbi->ilvu_ea);
-  printf("nxt_ilvu_sa   0x%08x\n", sml_pbi->ilvu_sa);
-  printf("nxt_ilvu_size 0x%04x\n", sml_pbi->size);
-  
-  printf("vob_v_s_s_ptm 0x%08x\n", sml_pbi->vob_v_s_s_ptm);
-  printf("vob_v_e_e_ptm 0x%08x\n", sml_pbi->vob_v_e_e_ptm);
-  
-  /* $$$ more code needed here */
-}
-
-static void navPrint_SML_AGLI(sml_agli_t *sml_agli) {
-  int i;
-  printf("sml_agli:\n");
-  for(i = 0; i < 9; i++) {
-    printf("agl_c%d address: 0x%08x size 0x%04x\n", i,
-           sml_agli->data[i].address, sml_agli->data[i].size);
-  }
-}
-
-static void navPrint_VOBU_SRI(vobu_sri_t *vobu_sri) {
-  int i;
-  int stime[19] = { 240, 120, 60, 20, 15, 14, 13, 12, 11, 
-                    10,   9,  8,  7,  6,  5,  4,  3,  2, 1};
-  printf("vobu_sri:\n");
-  printf("Next VOBU with Video %08x\n", vobu_sri->next_video);
-  for(i = 0; i < 19; i++) {
-    printf("%3.1f %08x ", stime[i]/2.0, vobu_sri->fwda[i]);
-  }
-  printf("\n");
-  printf("Next VOBU %08x\n", vobu_sri->next_vobu);
-  printf("--\n");
-  printf("Prev VOBU %08x\n", vobu_sri->prev_vobu);
-  for(i = 0; i < 19; i++) {
-    printf("%3.1f %08x ", stime[18 - i]/2.0, vobu_sri->bwda[i]);
-  }
-  printf("\n");
-  printf("Prev VOBU with Video %08x\n", vobu_sri->prev_video);
-}
-
-static void navPrint_SYNCI(synci_t *synci) {
-  int i;
-  
-  printf("synci:\n");
-  /* $$$ more code needed here */
-  for(i = 0; i < 8; i++)
-    printf("%04x ", synci->a_synca[i]);
-  for(i = 0; i < 32; i++)
-    printf("%08x ", synci->sp_synca[i]);
-}
-
-void navPrint_DSI(dsi_t *dsi) {
-  printf("dsi packet:\n");
-  navPrint_DSI_GI(&dsi->dsi_gi);
-  navPrint_SML_PBI(&dsi->sml_pbi);
-  navPrint_SML_AGLI(&dsi->sml_agli);
-  navPrint_VOBU_SRI(&dsi->vobu_sri);
-  navPrint_SYNCI(&dsi->synci);
-}
-
-
--- a/dvdread/nav_print.h	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
-#ifndef NAV_PRINT_H_INCLUDED
-#define NAV_PRINT_H_INCLUDED
-
-/*
- * Copyright (C) 2001, 2002 Billy Biggs <vektor@dumbterm.net>,
- *                          Håkan Hjort <d95hjort@dtek.chalmers.se>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include <dvdread/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 *);
-
-#ifdef __cplusplus
-};
-#endif
-#endif /* NAV_PRINT_H_INCLUDED */
--- a/dvdread/nav_read.c	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,331 +0,0 @@
-/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
-/*
- * 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
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "config.h"
-
-#include <stdio.h>
-#include <string.h>
-#if defined(HAVE_INTTYPES_H)
-#include <inttypes.h>
-#elif defined(HAVE_STDINT_H)
-#include <stdint.h>
-#endif
-
-#include "bswap.h"
-#include "nav_types.h"
-#include "nav_read.h"
-#include "dvdread_internal.h"
-
-typedef struct {
-  uint8_t *start;
-  uint32_t byte_position;
-  uint32_t bit_position;
-  uint8_t byte;
-} getbits_state_t;
-
-static int getbits_init(getbits_state_t *state, uint8_t *start) {
-  if ((state == NULL) || (start == NULL)) return 0;
-  state->start = start;
-  state->bit_position = 0;
-  state->byte_position = 0;
-  state->byte = start[0];
-  return 1;
-}
-
-/* Non-optimized getbits. */
-/* This can easily be optimized for particular platforms. */
-static uint32_t getbits(getbits_state_t *state, uint32_t number_of_bits) {
-  uint32_t result=0;
-  uint8_t byte=0;
-  if (number_of_bits > 32) {
-    printf("Number of bits > 32 in getbits\n");
-    abort();
-  }
-
-  if ((state->bit_position) > 0) {  /* Last getbits left us in the middle of a byte. */
-    if (number_of_bits > (8-state->bit_position)) { /* this getbits will span 2 or more bytes. */
-      byte = state->byte;
-      byte = byte >> (state->bit_position);
-      result = byte;
-      number_of_bits -= (8-state->bit_position);
-      state->bit_position = 0;
-      state->byte_position++;
-      state->byte = state->start[state->byte_position];
-    } else {
-      byte=state->byte;
-      state->byte = state->byte << number_of_bits;
-      byte = byte >> (8 - number_of_bits);
-      result = byte;
-      state->bit_position += number_of_bits; /* Here it is impossible for bit_position > 8 */
-      if (state->bit_position == 8) {
-        state->bit_position = 0;
-        state->byte_position++;
-        state->byte = state->start[state->byte_position];
-      }
-      number_of_bits = 0;
-    }
-  }
-  if ((state->bit_position) == 0) {
-    while (number_of_bits > 7) {
-      result = (result << 8) + state->byte;
-      state->byte_position++;
-      state->byte = state->start[state->byte_position];
-      number_of_bits -= 8;
-    }
-    if (number_of_bits > 0) { /* number_of_bits < 8 */
-      byte = state->byte;
-      state->byte = state->byte << number_of_bits;
-      state->bit_position += number_of_bits; /* Here it is impossible for bit_position > 7 */
-      byte = byte >> (8 - number_of_bits);
-      result = (result << number_of_bits) + byte;
-      number_of_bits = 0;
-    }
-  }
-
-  return result;
-}
-
-
-void navRead_PCI(pci_t *pci, unsigned char *buffer) {
-  int i, j;
-  getbits_state_t state;
-  if (!getbits_init(&state, buffer)) abort(); /* Passed NULL pointers */
-
-  /* pci pci_gi */
-  pci->pci_gi.nv_pck_lbn = getbits(&state, 32 );
-  pci->pci_gi.vobu_cat = getbits(&state, 16 );
-  pci->pci_gi.zero1 = getbits(&state, 16 );
-  pci->pci_gi.vobu_uop_ctl.zero = getbits(&state, 7 );
-  pci->pci_gi.vobu_uop_ctl.video_pres_mode_change         = getbits(&state, 1 );
-
-  pci->pci_gi.vobu_uop_ctl.karaoke_audio_pres_mode_change = getbits(&state, 1 ); 
-  pci->pci_gi.vobu_uop_ctl.angle_change                   = getbits(&state, 1 );
-  pci->pci_gi.vobu_uop_ctl.subpic_stream_change           = getbits(&state, 1 );
-  pci->pci_gi.vobu_uop_ctl.audio_stream_change            = getbits(&state, 1 );
-  pci->pci_gi.vobu_uop_ctl.pause_on                       = getbits(&state, 1 );
-  pci->pci_gi.vobu_uop_ctl.still_off                      = getbits(&state, 1 );
-  pci->pci_gi.vobu_uop_ctl.button_select_or_activate      = getbits(&state, 1 );
-  pci->pci_gi.vobu_uop_ctl.resume                         = getbits(&state, 1 );
-
-  pci->pci_gi.vobu_uop_ctl.chapter_menu_call              = getbits(&state, 1 ); 
-  pci->pci_gi.vobu_uop_ctl.angle_menu_call                = getbits(&state, 1 );
-  pci->pci_gi.vobu_uop_ctl.audio_menu_call                = getbits(&state, 1 );
-  pci->pci_gi.vobu_uop_ctl.subpic_menu_call               = getbits(&state, 1 );
-  pci->pci_gi.vobu_uop_ctl.root_menu_call                 = getbits(&state, 1 );
-  pci->pci_gi.vobu_uop_ctl.title_menu_call                = getbits(&state, 1 );
-  pci->pci_gi.vobu_uop_ctl.backward_scan                  = getbits(&state, 1 );
-  pci->pci_gi.vobu_uop_ctl.forward_scan                   = getbits(&state, 1 );
-
-  pci->pci_gi.vobu_uop_ctl.next_pg_search                 = getbits(&state, 1 ); 
-  pci->pci_gi.vobu_uop_ctl.prev_or_top_pg_search          = getbits(&state, 1 );
-  pci->pci_gi.vobu_uop_ctl.time_or_chapter_search         = getbits(&state, 1 );
-  pci->pci_gi.vobu_uop_ctl.go_up                          = getbits(&state, 1 );
-  pci->pci_gi.vobu_uop_ctl.stop                           = getbits(&state, 1 );
-  pci->pci_gi.vobu_uop_ctl.title_play                     = getbits(&state, 1 );
-  pci->pci_gi.vobu_uop_ctl.chapter_search_or_play         = getbits(&state, 1 );
-  pci->pci_gi.vobu_uop_ctl.title_or_time_play             = getbits(&state, 1 );
-  pci->pci_gi.vobu_s_ptm = getbits(&state, 32 ); 
-  pci->pci_gi.vobu_e_ptm = getbits(&state, 32 ); 
-  pci->pci_gi.vobu_se_e_ptm = getbits(&state, 32 ); 
-  pci->pci_gi.e_eltm.hour   = getbits(&state, 8 );
-  pci->pci_gi.e_eltm.minute = getbits(&state, 8 );
-  pci->pci_gi.e_eltm.second = getbits(&state, 8 );
-  pci->pci_gi.e_eltm.frame_u = getbits(&state, 8 );
-  for(i = 0; i < 32; i++)
-    pci->pci_gi.vobu_isrc[i] = getbits(&state, 8 );
-
-  /* pci nsml_agli */
-  for(i = 0; i < 9; i++)
-    pci->nsml_agli.nsml_agl_dsta[i] = getbits(&state, 32 );
-
-  /* pci hli hli_gi */
-  pci->hli.hl_gi.hli_ss = getbits(&state, 16 );
-  pci->hli.hl_gi.hli_s_ptm = getbits(&state, 32 ); 
-  pci->hli.hl_gi.hli_e_ptm = getbits(&state, 32 );
-  pci->hli.hl_gi.btn_se_e_ptm = getbits(&state, 32 );
-  pci->hli.hl_gi.zero1 = getbits(&state, 2 );
-  pci->hli.hl_gi.btngr_ns = getbits(&state, 2 );
-  pci->hli.hl_gi.zero2 = getbits(&state, 1 );
-  pci->hli.hl_gi.btngr1_dsp_ty = getbits(&state, 3 );
-  pci->hli.hl_gi.zero3 = getbits(&state, 1 );
-  pci->hli.hl_gi.btngr2_dsp_ty = getbits(&state, 3 );
-  pci->hli.hl_gi.zero4 = getbits(&state, 1 );
-  pci->hli.hl_gi.btngr3_dsp_ty = getbits(&state, 3 );
-  pci->hli.hl_gi.btn_ofn = getbits(&state, 8 );
-  pci->hli.hl_gi.btn_ns = getbits(&state, 8 );
-  pci->hli.hl_gi.nsl_btn_ns = getbits(&state, 8 ); 
-  pci->hli.hl_gi.zero5 = getbits(&state, 8 );
-  pci->hli.hl_gi.fosl_btnn = getbits(&state, 8 );
-  pci->hli.hl_gi.foac_btnn = getbits(&state, 8 );
-
-  /* pci hli btn_colit */
-  for(i = 0; i < 3; i++)
-    for(j = 0; j < 2; j++)
-      pci->hli.btn_colit.btn_coli[i][j] = getbits(&state, 32 ); 
-
-  /* pci hli btni */
-  for(i = 0; i < 36; i++) {
-    pci->hli.btnit[i].btn_coln = getbits(&state, 2 );
-    pci->hli.btnit[i].x_start = getbits(&state, 10 );
-    pci->hli.btnit[i].zero1 = getbits(&state, 2 );
-    pci->hli.btnit[i].x_end = getbits(&state, 10 );
-
-    pci->hli.btnit[i].auto_action_mode = getbits(&state, 2 );
-    pci->hli.btnit[i].y_start = getbits(&state, 10 );
-    pci->hli.btnit[i].zero2 = getbits(&state, 2 );
-    pci->hli.btnit[i].y_end = getbits(&state, 10 );
-
-    pci->hli.btnit[i].zero3 = getbits(&state, 2 );
-    pci->hli.btnit[i].up = getbits(&state, 6 );
-    pci->hli.btnit[i].zero4 = getbits(&state, 2 );
-    pci->hli.btnit[i].down = getbits(&state, 6 );
-    pci->hli.btnit[i].zero5 = getbits(&state, 2 );
-    pci->hli.btnit[i].left = getbits(&state, 6 );
-    pci->hli.btnit[i].zero6 = getbits(&state, 2 );
-    pci->hli.btnit[i].right = getbits(&state, 6 );
-    /* pci vm_cmd */
-    for(j = 0; j < 8; j++)
-      pci->hli.btnit[i].cmd.bytes[j] = getbits(&state, 8 );
-  }
-
-
-#ifndef NDEBUG
-  /* Asserts */
-
-  /* pci pci gi */ 
-  CHECK_VALUE(pci->pci_gi.zero1 == 0);
-
-  /* pci hli hli_gi */
-  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) {
-    CHECK_VALUE(pci->hli.hl_gi.btn_ns != 0); 
-    CHECK_VALUE(pci->hli.hl_gi.btngr_ns != 0); 
-  } else {
-    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++) {
-      int n = (36 / pci->hli.hl_gi.btngr_ns) * i + j;
-      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) {  
-        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 {
-        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++)
-          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;
-  getbits_state_t state;
-  if (!getbits_init(&state, buffer)) abort(); /* Passed NULL pointers */
-
-  /* dsi dsi gi */
-  dsi->dsi_gi.nv_pck_scr = getbits(&state, 32 );
-  dsi->dsi_gi.nv_pck_lbn = getbits(&state, 32 );
-  dsi->dsi_gi.vobu_ea = getbits(&state, 32 );
-  dsi->dsi_gi.vobu_1stref_ea = getbits(&state, 32 );
-  dsi->dsi_gi.vobu_2ndref_ea = getbits(&state, 32 );
-  dsi->dsi_gi.vobu_3rdref_ea = getbits(&state, 32 );
-  dsi->dsi_gi.vobu_vob_idn = getbits(&state, 16 );
-  dsi->dsi_gi.zero1 = getbits(&state, 8 );
-  dsi->dsi_gi.vobu_c_idn = getbits(&state, 8 );
-  dsi->dsi_gi.c_eltm.hour   = getbits(&state, 8 );
-  dsi->dsi_gi.c_eltm.minute = getbits(&state, 8 );
-  dsi->dsi_gi.c_eltm.second = getbits(&state, 8 );
-  dsi->dsi_gi.c_eltm.frame_u = getbits(&state, 8 );
-
-  /* dsi sml pbi */
-  dsi->sml_pbi.category = getbits(&state, 16 );
-  dsi->sml_pbi.ilvu_ea = getbits(&state, 32 );
-  dsi->sml_pbi.ilvu_sa = getbits(&state, 32 );
-  dsi->sml_pbi.size = getbits(&state, 16 );
-  dsi->sml_pbi.vob_v_s_s_ptm = getbits(&state, 32 );
-  dsi->sml_pbi.vob_v_e_e_ptm = getbits(&state, 32 );
-  for(i = 0; i < 8; i++) {
-    dsi->sml_pbi.vob_a[i].stp_ptm1 = getbits(&state, 32 );
-    dsi->sml_pbi.vob_a[i].stp_ptm2 = getbits(&state, 32 );
-    dsi->sml_pbi.vob_a[i].gap_len1 = getbits(&state, 32 );
-    dsi->sml_pbi.vob_a[i].gap_len2 = getbits(&state, 32 );
-  }
-
-  /* dsi sml agli */
-  for(i = 0; i < 9; i++) {
-    dsi->sml_agli.data[ i ].address = getbits(&state, 32 );
-    dsi->sml_agli.data[ i ].size = getbits(&state, 16 );
-  }
-
-  /* dsi vobu sri */
-  dsi->vobu_sri.next_video = getbits(&state, 32 );
-  for(i = 0; i < 19; i++)
-    dsi->vobu_sri.fwda[i] = getbits(&state, 32 );
-  dsi->vobu_sri.next_vobu = getbits(&state, 32 );
-  dsi->vobu_sri.prev_vobu = getbits(&state, 32 );
-  for(i = 0; i < 19; i++)
-    dsi->vobu_sri.bwda[i] = getbits(&state, 32 );
-  dsi->vobu_sri.prev_video = getbits(&state, 32 );
-
-  /* dsi synci */
-  for(i = 0; i < 8; i++)
-    dsi->synci.a_synca[i] = getbits(&state, 16 );
-  for(i = 0; i < 32; i++)
-    dsi->synci.sp_synca[i] = getbits(&state, 32 );
-
-  
-  /* Asserts */
-
-  /* dsi dsi gi */
-  CHECK_VALUE(dsi->dsi_gi.zero1 == 0);
-}
-
--- a/dvdread/nav_read.h	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
-#ifndef NAV_READ_H_INCLUDED
-#define NAV_READ_H_INCLUDED
-
-/*
- * Copyright (C) 2000, 2001, 2002 Håkan Hjort <d95hjort@dtek.chalmers.se>.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include <dvdread/nav_types.h>
-
-/**
- * Parsing of NAV data, PCI and DSI parts.
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * 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 *);
-
-#ifdef __cplusplus
-};
-#endif
-#endif /* NAV_READ_H_INCLUDED */
--- a/dvdread/nav_types.h	Sat Aug 30 11:21:11 2008 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,273 +0,0 @@
-/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
-#ifndef NAV_TYPES_H_INCLUDED
-#define NAV_TYPES_H_INCLUDED
-
-/*
- * Copyright (C) 2000, 2001, 2002 Håkan Hjort <d95hjort@dtek.chalmers.se>
- *
- * The data structures in this file should represent the layout of the
- * pci and dsi packets as they are stored in the stream.  Information
- * found by reading the source to VOBDUMP is the base for the structure
- * and names of these data types.
- *
- * VOBDUMP: a program for examining DVD .VOB files.
- * Copyright 1998, 1999 Eric Smith <eric@brouhaha.com>
- *
- * VOBDUMP is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.  Note that I am not
- * granting permission to redistribute or modify VOBDUMP under the terms
- * of any later version of the General Public License.
- *
- * This program is distributed in the hope that it will be useful (or at
- * least amusing), but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
- * the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
- * USA
- */
-
-#include <dvdread/ifo_types.h> /* only dvd_time_t, vm_cmd_t and user_ops_t */
-/* If it's ever removed add a uintX_t test. */
-
-#undef ATTRIBUTE_PACKED
-#undef PRAGMA_PACK_BEGIN 
-#undef PRAGMA_PACK_END
-
-#if defined(__GNUC__)
-#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
-#define ATTRIBUTE_PACKED __attribute__ ((packed))
-#define PRAGMA_PACK 0
-#endif
-#endif
-
-#if !defined(ATTRIBUTE_PACKED)
-#define ATTRIBUTE_PACKED
-#define PRAGMA_PACK 1
-#endif
-
-
-/* The length including the substream id byte. */
-#define PCI_BYTES 0x3d4
-#define DSI_BYTES 0x3fa
-
-#define PS2_PCI_SUBSTREAM_ID 0x00
-#define PS2_DSI_SUBSTREAM_ID 0x01
-
-/* Remove this */
-#define DSI_START_BYTE 1031
-
-
-#if PRAGMA_PACK
-#pragma pack(1)
-#endif
-
-
-/**
- * PCI General Information 
- */
-typedef struct {
-  uint32_t nv_pck_lbn;      /**< 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;
-
-/**
- * Non Seamless Angle Information
- */
-typedef struct {
-  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; /**< 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 */
-  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 */
-  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];  /**< [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 {
-  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 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 zero3            : 2;  /**< reserved */
-  unsigned int up               : 6;  /**< button index when pressing up */
-  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 */
-  vm_cmd_t cmd;
-} ATTRIBUTE_PACKED btni_t;
-
-/**
- * Highlight Information 
- */
-typedef struct {
-  hl_gi_t     hl_gi;
-  btn_colit_t btn_colit;
-  btni_t      btnit[36];
-} ATTRIBUTE_PACKED hli_t;
-
-/**
- * PCI packet
- */
-typedef struct {
-  pci_gi_t    pci_gi;
-  nsml_agli_t nsml_agli;
-  hli_t       hli;
-  uint8_t     zero1[189];
-} ATTRIBUTE_PACKED pci_t;
-
-
-
-
-/**
- * DSI General Information 
- */
-typedef struct {
-  uint32_t nv_pck_scr;
-  uint32_t nv_pck_lbn;      /**< 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 */
-  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;
-    uint32_t gap_len1;
-    uint32_t gap_len2;      
-  } vob_a[8];
-} ATTRIBUTE_PACKED sml_pbi_t;
-
-/**
- * Seamless Angle Infromation for one angle
- */
-typedef struct {
-  uint32_t address; /**< 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;
-
-/**
- * Seamless Angle Infromation
- */
-typedef struct {
-  sml_agl_data_t data[9];
-} ATTRIBUTE_PACKED sml_agli_t;
-
-/**
- * VOBU Search Information 
- */
-typedef struct {
-  uint32_t next_video; /**< Next vobu that contains video */
-  uint32_t fwda[19];   /**< Forwards, time */
-  uint32_t next_vobu;
-  uint32_t prev_vobu;
-  uint32_t bwda[19];   /**< Backwards, time */
-  uint32_t prev_video;
-} ATTRIBUTE_PACKED vobu_sri_t;
-
-#define SRI_END_OF_CELL 0x3fffffff
-
-/**
- * Synchronous Information
- */ 
-typedef struct {
-  uint16_t a_synca[8];   /**< offset to first audio packet for this VOBU */
-  uint32_t sp_synca[32]; /**< offset to first subpicture packet */
-} ATTRIBUTE_PACKED synci_t;
-
-/**
- * DSI packet
- */
-typedef struct {
-  dsi_gi_t   dsi_gi;
-  sml_pbi_t  sml_pbi;
-  sml_agli_t sml_agli;
-  vobu_sri_t vobu_sri;
-  synci_t    synci;
-  uint8_t    zero1[471];
-} ATTRIBUTE_PACKED dsi_t;
-
-
-#if PRAGMA_PACK
-#pragma pack()
-#endif
-
-#endif /* NAV_TYPES_H_INCLUDED */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/bswap.h	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,16 @@
+#ifndef DVDREAD_BSWAP_H
+#define DVDREAD_BSWAP_H
+
+#include "libavutil/bswap.h"
+
+#ifdef WORDS_BIGENDIAN
+#define B2N_16(x)
+#define B2N_32(x)
+#define B2N_64(x)
+#else
+#define B2N_16(x) x = bswap_16(x)
+#define B2N_32(x) x = bswap_32(x)
+#define B2N_64(x) x = bswap_64(x)
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/cmd_print.c	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,550 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2000, 2001, 2002, 2003 Martin Norbäck, Håkan Hjort
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, 
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#if defined(HAVE_INTTYPES_H)
+#include <inttypes.h>
+#elif defined(HAVE_STDINT_H)
+#include <stdint.h>
+#endif
+
+#include "cmd_print.h"
+
+
+typedef struct
+{
+  uint8_t bits[8];
+  uint8_t examined[8];
+} cmd_t;
+
+
+static const char *cmp_op_table[] = {
+  NULL, "&", "==", "!=", ">=", ">", "<=", "<"
+};
+static const char *set_op_table[] = {
+  NULL, "=", "<->", "+=", "-=", "*=", "/=", "%=", "rnd", "&=", "|=", "^="
+};
+
+static const char *link_table[] = {
+  "LinkNoLink",  "LinkTopC",    "LinkNextC",   "LinkPrevC",
+  NULL,          "LinkTopPG",   "LinkNextPG",  "LinkPrevPG",
+  NULL,          "LinkTopPGC",  "LinkNextPGC", "LinkPrevPGC",
+  "LinkGoUpPGC", "LinkTailPGC", NULL,          NULL,
+  "RSM"
+};
+
+static const char *system_reg_table[] = {
+  "Menu Description Language Code",
+  "Audio Stream Number",
+  "Sub-picture Stream Number",
+  "Angle Number",
+  "Title Track Number",
+  "VTS Title Track Number",
+  "VTS PGC Number",
+  "PTT Number for One_Sequential_PGC_Title",
+  "Highlighted Button Number",
+  "Navigation Timer",
+  "Title PGC Number for Navigation Timer",
+  "Audio Mixing Mode for Karaoke",
+  "Country Code for Parental Management",
+  "Parental Level",
+  "Player Configurations for Video",
+  "Player Configurations for Audio",
+  "Initial Language Code for Audio",
+  "Initial Language Code Extension for Audio",
+  "Initial Language Code for Sub-picture",
+  "Initial Language Code Extension for Sub-picture",
+  "Player Regional Code",
+  "Reserved 21",
+  "Reserved 22",
+  "Reserved 23"
+};
+
+static const char *system_reg_abbr_table[] = {
+  NULL,
+  "ASTN",
+  "SPSTN",
+  "AGLN",
+  "TTN",
+  "VTS_TTN",
+  "TT_PGCN",
+  "PTTN",
+  "HL_BTNN",
+  "NVTMR",
+  "NV_PGCN",
+  NULL,
+  "CC_PLT",
+  "PLT",
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+};
+    
+
+
+static unsigned int bits(cmd_t *cmd, int byte, int bit, int count) {
+  unsigned int val = 0;
+  unsigned int bit_mask;
+  
+  while(count--) {
+    if(bit > 7) {
+      bit = 0;
+      byte++;
+    }
+    bit_mask = 0x01 << (7-bit);
+    val <<= 1;
+    if((cmd->bits[byte]) & bit_mask)
+      val |= 1;
+    cmd->examined[byte] |= bit_mask;
+    bit++;
+  }
+  return val;
+}
+
+
+static void print_system_reg(unsigned int reg) {
+  if(reg < sizeof(system_reg_abbr_table) / sizeof(char *))
+    fprintf(stdout, system_reg_table[reg]);
+  else
+    fprintf(stdout, " WARNING: Unknown system register ");
+}
+
+static void print_reg(unsigned int reg) {
+  if(reg & 0x80)
+    print_system_reg(reg & 0x7f);
+  else
+    if(reg < 16)
+      fprintf(stdout, "g[%u]", reg);
+    else
+      fprintf(stdout, " WARNING: Unknown general register ");
+}
+
+static void print_cmp_op(unsigned int op) {
+  if(op < sizeof(cmp_op_table) / sizeof(char *) && cmp_op_table[op] != NULL)
+    fprintf(stdout, " %s ", cmp_op_table[op]);
+  else
+    fprintf(stdout, " WARNING: Unknown compare op ");
+}
+
+static void print_set_op(unsigned int op) {
+  if(op < sizeof(set_op_table) / sizeof(char *) && set_op_table[op] != NULL)
+    fprintf(stdout, " %s ", set_op_table[op]);
+  else
+    fprintf(stdout, " WARNING: Unknown set op ");
+}
+
+static void print_reg_or_data(cmd_t *cmd, unsigned int immediate, int byte) {
+  if(immediate) {
+    int i = bits(cmd,byte,0,16);
+    
+    fprintf(stdout, "0x%x", i);
+    if(isprint(i & 0xff) && isprint((i>>8) & 0xff))
+      fprintf(stdout, " (\"%c%c\")", (char)((i>>8) & 0xff), (char)(i & 0xff));
+  } else {
+    print_reg(bits(cmd,byte + 1,0,8));
+  }
+}
+
+static void print_reg_or_data_2(cmd_t *cmd, unsigned int immediate, int byte) {
+  if(immediate)
+    fprintf(stdout, "0x%x", bits(cmd,byte,1,7));
+  else
+    fprintf(stdout, "g[%u]", bits(cmd,byte,4,4));
+}
+
+static void print_if_version_1(cmd_t *cmd) {
+  unsigned int op = bits(cmd,1,1,3);
+  
+  if(op) {
+    fprintf(stdout, "if (");
+    print_reg(bits(cmd,3,0,8));
+    print_cmp_op(op);
+    print_reg_or_data(cmd,bits(cmd,1,0,1), 4);
+    fprintf(stdout, ") ");
+  }
+}
+
+static void print_if_version_2(cmd_t *cmd) {
+  unsigned int op = bits(cmd,1,1,3);
+  
+  if(op) {
+    fprintf(stdout, "if (");
+    print_reg(bits(cmd,6,0,8));
+    print_cmp_op(op);
+    print_reg(bits(cmd,7,0,8));
+    fprintf(stdout, ") ");
+  }
+}
+
+static void print_if_version_3(cmd_t *cmd) {
+  unsigned int op = bits(cmd,1,1,3);
+  
+  if(op) {
+    fprintf(stdout, "if (");
+    print_reg(bits(cmd,2,0,8));
+    print_cmp_op(op);
+    print_reg_or_data(cmd,bits(cmd,1,0,1), 6);
+    fprintf(stdout, ") ");
+  }
+}
+
+static void print_if_version_4(cmd_t *cmd) {
+  unsigned int op = bits(cmd,1,1,3);
+  
+  if(op) {
+    fprintf(stdout, "if (");
+    print_reg(bits(cmd,1,4,4));
+    print_cmp_op(op);
+    print_reg_or_data(cmd,bits(cmd,1,0,1), 4);
+    fprintf(stdout, ") ");
+  }
+}
+
+static void print_if_version_5(cmd_t *cmd) {
+  unsigned int op = bits(cmd,1,1,3);
+  
+  if(op) {
+    fprintf(stdout, "if (");
+    print_reg(bits(cmd,4,0,8));
+    print_cmp_op(op);
+    print_reg(bits(cmd,5,0,8));
+    fprintf(stdout, ") ");
+  }
+}
+
+static void print_special_instruction(cmd_t *cmd) {
+  unsigned int op = bits(cmd,1,4,4);
+  
+  switch(op) {
+  case 0: // NOP
+    fprintf(stdout, "Nop");
+    break;
+  case 1: // Goto line
+    fprintf(stdout, "Goto %u", bits(cmd,7,0,8));
+    break;
+  case 2: // Break
+    fprintf(stdout, "Break");
+    break;
+  case 3: // Parental level
+    fprintf(stdout, "SetTmpPML %u, Goto %u", 
+            bits(cmd,6,4,4), bits(cmd,7,0,8));
+    break;
+  default:
+    fprintf(stdout, "WARNING: Unknown special instruction (%u)", 
+            bits(cmd,1,4,4));
+  }
+}
+
+static void print_linksub_instruction(cmd_t *cmd) {
+  unsigned int linkop = bits(cmd,7,3,5);
+  unsigned int button = bits(cmd,6,0,6);
+  
+  if(linkop < sizeof(link_table)/sizeof(char *) && link_table[linkop] != NULL)
+    fprintf(stdout, "%s (button %u)", link_table[linkop], button);
+  else
+    fprintf(stdout, "WARNING: Unknown linksub instruction (%u)", linkop);
+}
+
+static void print_link_instruction(cmd_t *cmd, int optional) {
+  unsigned int op = bits(cmd,1,4,4);
+  
+  if(optional && op)
+    fprintf(stdout, ", ");
+  
+  switch(op) {
+  case 0:
+    if(!optional)
+      fprintf(stdout, "WARNING: NOP (link)!");
+    break;
+  case 1:
+    print_linksub_instruction(cmd);
+    break;
+  case 4:
+    fprintf(stdout, "LinkPGCN %u", bits(cmd,6,1,15));
+    break;
+  case 5:
+    fprintf(stdout, "LinkPTT %u (button %u)", 
+            bits(cmd,6,6,10), bits(cmd,6,0,6));
+    break;
+  case 6:
+    fprintf(stdout, "LinkPGN %u (button %u)", 
+            bits(cmd,7,1,7), bits(cmd,6,0,6));
+    break;
+  case 7:
+    fprintf(stdout, "LinkCN %u (button %u)", 
+            bits(cmd,7,0,8), bits(cmd,6,0,6));
+    break;
+  default:
+    fprintf(stdout, "WARNING: Unknown link instruction");
+  }
+}
+
+static void print_jump_instruction(cmd_t *cmd) {
+  switch(bits(cmd,1,4,4)) {
+  case 1:
+    fprintf(stdout, "Exit");
+    break;
+  case 2:
+    fprintf(stdout, "JumpTT %u", bits(cmd,5,1,7));
+    break;
+  case 3:
+    fprintf(stdout, "JumpVTS_TT %u", bits(cmd,5,1,7));
+    break;
+  case 5:
+    fprintf(stdout, "JumpVTS_PTT %u:%u", bits(cmd,5,1,7), bits(cmd,2,6,10));
+    break;
+  case 6:
+    switch(bits(cmd,5,0,2)) {
+    case 0:
+      fprintf(stdout, "JumpSS FP");
+      break;
+    case 1:
+      fprintf(stdout, "JumpSS VMGM (menu %u)", bits(cmd,5,4,4));
+      break;
+    case 2:
+      fprintf(stdout, "JumpSS VTSM (vts %u, title %u, menu %u)", 
+              bits(cmd,4,0,8), bits(cmd,3,0,8), bits(cmd,5,4,4));
+      break;
+    case 3:
+      fprintf(stdout, "JumpSS VMGM (pgc %u)", bits(cmd,2,1,15));
+      break;
+    }
+    break;
+  case 8:
+    switch(bits(cmd,5,0,2)) {
+    case 0:
+      fprintf(stdout, "CallSS FP (rsm_cell %u)",
+              bits(cmd,4,0,8));
+      break;
+    case 1:
+      fprintf(stdout, "CallSS VMGM (menu %u, rsm_cell %u)",
+              bits(cmd,5,4,4), bits(cmd,4,0,8));
+      break;
+    case 2:
+      fprintf(stdout, "CallSS VTSM (menu %u, rsm_cell %u)",
+              bits(cmd,5,4,4), bits(cmd,4,0,8));
+      break;
+    case 3:
+      fprintf(stdout, "CallSS VMGM (pgc %u, rsm_cell %u)", 
+              bits(cmd,2,1,15), bits(cmd,4,0,8));
+      break;
+    }
+    break;
+  default:
+    fprintf(stdout, "WARNING: Unknown Jump/Call instruction");
+  }
+}
+
+static void print_system_set(cmd_t *cmd) {
+  int i;
+  
+  switch(bits(cmd,0,4,4)) {
+  case 1: // Set system reg 1 &| 2 &| 3 (Audio, Subp. Angle)
+    for(i = 1; i <= 3; i++) {
+      if(bits(cmd,2+i,0,1)) {
+        print_system_reg((unsigned int)i);
+        fprintf(stdout, " = ");
+        print_reg_or_data_2(cmd,bits(cmd,0,3,1), 2 + i);
+        fprintf(stdout, " ");
+      }
+    }
+    break;
+  case 2: // Set system reg 9 & 10 (Navigation timer, Title PGC number)
+    print_system_reg(9);
+    fprintf(stdout, " = ");
+    print_reg_or_data(cmd,bits(cmd,0,3,1), 2);
+    fprintf(stdout, " ");
+    print_system_reg(10);
+    fprintf(stdout, " = %u", bits(cmd,5,0,8)); // ??
+    break;
+  case 3: // Mode: Counter / Register + Set
+    fprintf(stdout, "SetMode ");
+    if(bits(cmd,5,0,1))
+      fprintf(stdout, "Counter ");
+    else
+      fprintf(stdout, "Register ");
+    print_reg(bits(cmd,5,4,4));
+    print_set_op(0x1); // '='
+    print_reg_or_data(cmd,bits(cmd,0,3,1), 2);
+    break;
+  case 6: // Set system reg 8 (Highlighted button)
+    print_system_reg(8);
+    if(bits(cmd,0,3,1)) // immediate
+      fprintf(stdout, " = 0x%x (button no %u)", 
+              bits(cmd,4,0,16), bits(cmd,4,0,6));
+    else
+      fprintf(stdout, " = g[%u]", bits(cmd,5,4,4));
+    break;
+  default:
+    fprintf(stdout, "WARNING: Unknown system set instruction (%u)", 
+            bits(cmd,0,4,4));
+  }
+}
+
+static void print_set_version_1(cmd_t *cmd) {
+  unsigned int set_op = bits(cmd,0,4,4);
+  
+  if(set_op) {
+    print_reg(bits(cmd,3,0,8));
+    print_set_op(set_op);
+    print_reg_or_data(cmd,bits(cmd,0,3,1), 4);
+  } else {
+    fprintf(stdout, "NOP");
+  }
+}
+
+static void print_set_version_2(cmd_t *cmd) {
+  unsigned int set_op = bits(cmd,0,4,4);
+  
+  if(set_op) {
+    print_reg(bits(cmd,1,4,4));
+    print_set_op(set_op);
+    print_reg_or_data(cmd,bits(cmd,0,3,1), 2);
+  } else {
+    fprintf(stdout, "NOP");
+  }
+}
+
+static void print_set_version_3(cmd_t *cmd) {
+  unsigned int set_op = bits(cmd,0,4,4);
+  
+  if(set_op) {
+    print_reg(bits(cmd,1,4,4));
+    print_set_op(set_op);
+    if(bits(cmd,0,3,1)) { // print_reg_or_data
+      unsigned int i = bits(cmd,2,0,16);
+      
+      fprintf(stdout, "0x%x", i);
+      if(isprint(i & 0xff) && isprint((i>>8) & 0xff))
+        fprintf(stdout, " (\"%c%c\")", 
+                (char)((i>>8) & 0xff), (char)(i & 0xff));
+    } else {
+      print_reg(bits(cmd,2,0,8));
+    }
+  } else {
+    fprintf(stdout, "NOP");
+  }
+}
+
+static void print_command(cmd_t *cmd) {
+  switch(bits(cmd,0,0,3)) { /* three first bits */
+  case 0: // Special instructions
+    print_if_version_1(cmd);
+    print_special_instruction(cmd);
+    break;
+  case 1: // Jump/Call or Link instructions
+    if(bits(cmd,0,3,1)) {
+      print_if_version_2(cmd);
+      print_jump_instruction(cmd);
+    } else {
+      print_if_version_1(cmd);
+      print_link_instruction(cmd,0); // must be pressent
+    }
+    break;
+  case 2: // Set System Parameters instructions
+    print_if_version_2(cmd);
+    print_system_set(cmd);
+    print_link_instruction(cmd,1); // either 'if' or 'link'
+    break;
+  case 3: // Set General Parameters instructions
+    print_if_version_3(cmd);
+    print_set_version_1(cmd);
+    print_link_instruction(cmd,1); // either 'if' or 'link'
+    break;
+  case 4: // Set, Compare -> LinkSub instructions
+    print_set_version_2(cmd);
+    fprintf(stdout, ", ");
+    print_if_version_4(cmd);
+    print_linksub_instruction(cmd);
+    break;
+  case 5: // Compare -> (Set and LinkSub) instructions
+    if(bits(cmd,0,3,1))
+      print_if_version_5(cmd);
+    else
+      print_if_version_1(cmd);
+    fprintf(stdout, "{ ");
+    print_set_version_3(cmd);
+    fprintf(stdout, ", ");
+    print_linksub_instruction(cmd);
+    fprintf(stdout, " }");
+    break;
+  case 6: // Compare -> Set, always LinkSub instructions
+    if(bits(cmd,0,3,1))
+      print_if_version_5(cmd);
+    else
+      print_if_version_1(cmd);
+    fprintf(stdout, "{ ");
+    print_set_version_3(cmd);
+    fprintf(stdout, " } ");
+    print_linksub_instruction(cmd);
+    break;
+  default:
+    fprintf(stdout, "WARNING: Unknown instruction type (%i)", 
+            bits(cmd,0,0,3));
+  }
+}
+
+void cmdPrint_mnemonic(vm_cmd_t *command)  {
+  int i, extra_bits;
+  cmd_t cmd;
+  
+  for(i = 0; i < 8; i++) {
+    cmd.bits[i] = command->bytes[i];
+    cmd.examined[i] = 0;
+  }
+
+  print_command(&cmd);
+  
+  // Check if there still are bits set that were not examined
+  extra_bits = 0;
+  for(i = 0; i < 8; i++)
+    if(cmd.bits[i] & ~ cmd.examined[i]) {
+      extra_bits = 1;
+      break;
+    }
+  if(extra_bits) {
+    fprintf(stdout, " [WARNING, unknown bits:");
+    for(i = 0; i < 8; i++)
+      fprintf(stdout, " %02x", cmd.bits[i] & ~ cmd.examined[i]);
+    fprintf(stdout, "]");
+  }
+}
+
+void cmdPrint_CMD(int row, vm_cmd_t *command) {
+  int i;
+
+  fprintf(stdout, "(%03d) ", row + 1);
+  for(i = 0; i < 8; i++)
+    fprintf(stdout, "%02x ", command->bytes[i]);
+  fprintf(stdout, "| ");
+
+  cmdPrint_mnemonic(command);
+  fprintf(stdout, "\n");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/cmd_print.h	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,51 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+#ifndef CMD_PRINT_H_INCLUDED
+#define CMD_PRINT_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001, 2002, 2003 Martin Norbäck, Håkan Hjort
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, 
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <libdvdread/ifo_types.h>
+
+/**
+ * Pretty printing of the DVD commands (vm instructions).
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+  
+/**
+ * Prints a text representation of the commands to stdout.
+ *
+ * @param command Pointer to the DVD command to be printed.
+ */
+void cmdPrint_mnemonic(vm_cmd_t *command);
+  
+/**
+ * Prints row, then a hex dump of the command followed by the text
+ * representation of the commands, as given by cmdPrint_mnemonic to
+ * stdout.
+ *
+ * @param command Pointer to the DVD command to be printed.  */
+void cmdPrint_CMD(int row, vm_cmd_t *command);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* CMD_PRINT_H_INCLUDED */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/dvd_input.c	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,414 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2002 Samuel Hocevar <sam@zoy.org>,
+ *                    Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#define __USE_GNU /* to get O_DIRECT in linux */
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "dvd_reader.h"
+#include "dvd_input.h"
+
+#include "dvdread_internal.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); 
+/**
+ *  pointer must be aligned to 2048 bytes
+ *  if reading from a raw/O_DIRECT file
+ */
+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/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 {
+  /* libdvdcss handle */
+  dvdcss_handle dvdcss;
+  
+  /* dummy file input */
+  int fd;
+};
+
+
+/**
+ * initialize and open a DVD device or file.
+ */
+static dvd_input_t css_open(const char *target)
+{
+  dvd_input_t dev;
+    
+  /* Allocate the handle structure */
+  dev = (dvd_input_t) malloc(sizeof(struct dvd_input_s));
+  if(dev == NULL) {
+    /* malloc has set errno to ENOMEM */
+    return NULL;
+  }
+  
+  /* Really open it with libdvdcss */
+  dev->dvdcss = DVDcss_open(target);
+  if(dev->dvdcss == 0) {
+    free(dev);
+    dev = NULL;
+  }
+  
+  return dev;
+}
+
+/**
+ * return the last error message
+ */
+static char *css_error(dvd_input_t dev)
+{
+  return DVDcss_error(dev->dvdcss);
+}
+
+/**
+ * seek into the device.
+ */
+static int css_seek(dvd_input_t dev, int blocks)
+{
+  /* DVDINPUT_NOFLAGS should match the DVDCSS_NOFLAGS value. */
+  return DVDcss_seek(dev->dvdcss, blocks, DVDINPUT_NOFLAGS);
+}
+
+/**
+ * set the block for the begining of a new title (key).
+ */
+static int css_title(dvd_input_t dev, int block)
+{
+  return DVDcss_title(dev->dvdcss, block);
+}
+
+/**
+ * read data from the device.
+ */
+static int css_read(dvd_input_t dev, void *buffer, int blocks, int flags)
+{
+  return DVDcss_read(dev->dvdcss, buffer, blocks, flags);
+}
+
+/**
+ * close the DVD device and clean up the library.
+ */
+static int css_close(dvd_input_t dev)
+{
+  int ret;
+
+  ret = DVDcss_close(dev->dvdcss);
+
+  if(ret < 0)
+    return ret;
+
+  free(dev);
+
+  return 0;
+}
+
+/* Need to use O_BINARY for WIN32 */
+#ifndef O_BINARY
+#ifdef _O_BINARY
+#define O_BINARY _O_BINARY
+#else
+#define O_BINARY 0
+#endif
+#endif
+
+/**
+ * initialize and open a DVD device or file.
+ */
+static dvd_input_t file_open(const char *target)
+{
+  dvd_input_t dev;
+  char *use_odirect;
+  int oflags;
+  
+  oflags = O_RDONLY | O_BINARY;
+  use_odirect = getenv("DVDREAD_USE_DIRECT");
+  if(use_odirect) {
+#ifndef O_DIRECT
+#define O_DIRECT 0
+#endif
+    oflags |= O_DIRECT;
+  }
+  /* Allocate the library structure */
+  dev = (dvd_input_t) malloc(sizeof(struct dvd_input_s));
+  if(dev == NULL) {
+    return NULL;
+  }
+  
+  /* Open the device */
+  dev->fd = open(target, oflags);
+  if(dev->fd < 0) {
+    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 = (off_t)blocks * (off_t)DVD_VIDEO_LB_LEN;
+
+  pos = lseek(dev->fd, pos, 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;
+  unsigned char *buf = buffer;
+
+  len = (size_t)blocks * DVD_VIDEO_LB_LEN;
+  
+  while(len > 0) {
+    
+    ret = read(dev->fd, buf, 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);
+    }
+    
+    buf+=ret;
+    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;
+}
+
+
+static void *dvdcss_library = NULL;
+static int dvdcss_library_init = 0;
+
+/**
+ * Free any objects allocated by dvdinput_setup.
+ * Should only be called when libdvdread is not to be used any more.
+ * Closes dlopened libraries.
+ */
+void dvdinput_free(void)
+{
+#ifdef HAVE_DVDCSS_DVDCSS_H
+  /* linked statically, nothing to free */
+  return;
+#else
+  if(dvdcss_library) {
+    dlclose(dvdcss_library);
+    dvdcss_library = NULL;
+  }
+  dvdcss_library_init = 0;
+  return;
+#endif
+}
+
+
+/**
+ * Setup read functions with either libdvdcss or minimal DVD access.
+ */
+int dvdinput_setup(void)
+{
+  char **dvdcss_version = NULL;
+  int verbose;
+
+  /* dlopening libdvdcss */
+  if(dvdcss_library_init) {
+    /* libdvdcss is already dlopened, function ptrs set */
+    if(dvdcss_library) {
+      return 1; /* css available */
+    } else {
+      return 0; /* css not available */
+    }
+  }
+
+  verbose = get_verbose();
+  
+#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
+
+  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")) {
+      if(verbose >= 0) {
+        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) {
+      if(verbose >= 0) {
+        fprintf(stderr,  "libdvdread: Missing symbols in libdvdcss.so.2, "
+                "this shouldn't happen !\n");
+      }
+      dlclose(dvdcss_library);
+      dvdcss_library = NULL;
+    }
+  }
+#endif /* HAVE_DVDCSS_DVDCSS_H */
+
+  dvdcss_library_init = 1;
+  
+  if(dvdcss_library) {
+    /*
+      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);
+    */
+    if(verbose >= 1) {
+      fprintf(stderr, "libdvdread: Using libdvdcss version %s for DVD access\n",
+              *dvdcss_version);
+    }
+    /* 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 {
+    if(verbose >= 1) {
+      fprintf(stderr, "libdvdread: Encrypted DVD support unavailable.\n");
+    }
+    /* libdvdcss replacement functions */
+    dvdinput_open  = file_open;
+    dvdinput_close = file_close;
+    dvdinput_seek  = file_seek;
+    dvdinput_title = file_title;
+    dvdinput_read  = file_read;
+    dvdinput_error = file_error;
+    return 0;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/dvd_input.h	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,55 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+#ifndef DVD_INPUT_H_INCLUDED
+#define DVD_INPUT_H_INCLUDED
+
+/*
+ * Copyright (C) 2001, 2002 Samuel Hocevar <sam@zoy.org>,
+ *                          Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */
+
+/**
+ * Defines and flags.  Make sure they fit the libdvdcss API!
+ */
+#define DVDINPUT_NOFLAGS         0
+
+#define DVDINPUT_READ_DECRYPT    (1 << 0)
+
+typedef struct dvd_input_s *dvd_input_t;
+
+/**
+ * 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);
+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);
+
+/**
+ * Free any objects allocated by dvdinput_setup.
+ * Should only be called when libdvdread is not to be used any more.
+ * Closes dlopened libraries.
+ */
+void dvdinput_free(void);
+
+/**
+ * Setup function accessed by dvd_reader.c.  Returns 1 if there is CSS support.
+ */
+int dvdinput_setup(void);
+
+#endif /* DVD_INPUT_H_INCLUDED */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/dvd_reader.c	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,1611 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/*
+ * 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 changelog at http://svn.mplayerhq.hu/mplayer/trunk/
+ * $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 (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h> /* For the timing of dvdcss_title crack. */
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <dirent.h>
+ 
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__) || defined(__DARWIN__) || defined(__DragonFly__)
+#define SYS_BSD 1
+#endif
+
+#if defined(__sun)
+#include <sys/mnttab.h>
+#elif defined(hpux)
+#include </usr/conf/h/mnttab.h>
+#elif defined(SYS_BSD)
+#include <fstab.h>
+#elif defined(__linux__) || defined(__CYGWIN__)
+#include <mntent.h>
+#endif
+
+#include "dvd_reader.h"
+#include "dvd_input.h"
+#include "dvd_udf.h"
+#include "md5.h"
+
+#include "dvdread_internal.h"
+
+#define DEFAULT_UDF_CACHE_LEVEL 0
+
+struct dvd_reader_s {
+  /* Basic information. */
+  int isImageFile;
+  
+  /* Hack for keeping track of the css status. 
+   * 0: no css, 1: perhaps (need init of keys), 2: have done init */
+  int css_state;
+  int css_title; /* Last title that we have called dvdinpute_title for. */
+
+  /* Information required for an image file. */
+  dvd_input_t dev;
+
+  /* Information required for a directory path drive. */
+  char *path_root;
+  
+  /* Filesystem cache */
+  int udfcache_level; /* 0 - turned off, 1 - on */
+  void *udfcache;
+
+  /* block aligned malloc */
+  void *align;
+  
+  /* error message verbosity level */
+  int verbose;
+};
+
+struct dvd_file_s {
+  /* Basic information. */
+  dvd_reader_t *dvd;
+  
+  /* Hack for selecting the right css title. */
+  int css_title;
+
+  /* Information required for an image file. */
+  uint32_t lb_start;
+  uint32_t seek_pos;
+
+  /* Information required for a directory path drive. */
+  size_t title_sizes[ 9 ];
+  dvd_input_t title_devs[ 9 ];
+
+  /* Calculated at open-time, size in blocks. */
+  ssize_t filesize;
+};
+
+
+#define DVDREAD_VERBOSE_DEFAULT 0
+
+int get_verbose(void)
+{
+  char *dvdread_verbose;
+  int verbose;
+  
+  dvdread_verbose = getenv("DVDREAD_VERBOSE");
+  if(dvdread_verbose) {
+    verbose = (int)strtol(dvdread_verbose, NULL, 0);
+  } else {
+    verbose = DVDREAD_VERBOSE_DEFAULT;
+  }
+  return verbose;
+}
+
+int dvdread_verbose(dvd_reader_t *dvd)
+{
+  return dvd->verbose;
+}
+
+dvd_reader_t *device_of_file(dvd_file_t *file)
+{
+  return file->dvd;
+}
+
+/**
+ * Returns the compiled version. (DVDREAD_VERSION as an int)
+ */
+int DVDVersion(void)
+{
+  return DVDREAD_VERSION;
+}
+
+
+/**
+ * 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;
+}
+
+void *GetAlignHandle(dvd_reader_t *device)
+{
+  struct dvd_reader_s *dev = (struct dvd_reader_s *)device;
+  
+  return dev->align;
+}
+
+void SetAlignHandle(dvd_reader_t *device, void *align)
+{
+  struct dvd_reader_s *dev = (struct dvd_reader_s *)device;
+
+  dev->align = align;
+}
+
+
+/* Loop over all titles and call dvdcss_title to crack the keys. */
+static int initAllCSSKeys( dvd_reader_t *dvd )
+{
+  struct timeval all_s, all_e;
+  struct timeval t_s, t_e;
+  char filename[ MAX_UDF_FILE_NAME_LEN ];
+  uint32_t start, len;
+  int title;
+        
+  char *nokeys_str = getenv("DVDREAD_NOKEYS");
+  if(nokeys_str != NULL)
+    return 0;
+    
+  if(dvd->verbose >= 1) {
+    fprintf( stderr, "\n" );
+    fprintf( stderr, "libdvdread: Attempting to retrieve all CSS keys\n" );
+    fprintf( stderr, "libdvdread: This can take a _long_ time, "
+             "please be patient\n\n" );
+  }
+  gettimeofday(&all_s, NULL);
+        
+  for( title = 0; title < 100; title++ ) {
+    gettimeofday( &t_s, NULL );
+    if( title == 0 ) {
+      sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
+    } else {
+      sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 0 );
+    }
+    start = UDFFindFile( dvd, filename, &len );
+    if( start != 0 && len != 0 ) {
+      /* Perform CSS key cracking for this title. */
+      if(dvd->verbose >= 1) {
+        fprintf( stderr, "libdvdread: Get key for %s at 0x%08x\n", 
+                 filename, start );
+      }
+      if( dvdinput_title( dvd->dev, (int)start ) < 0 ) {
+        if(dvd->verbose >= 0) {
+          fprintf( stderr, "libdvdread: Error cracking CSS key for %s (0x%08x)\n", filename, start);
+        }
+      }
+      gettimeofday( &t_e, NULL );
+      if(dvd->verbose >= 1) {
+        fprintf( stderr, "libdvdread: Elapsed time %ld\n",  
+                 (long int) t_e.tv_sec - t_s.tv_sec );
+      }
+    }
+            
+    if( title == 0 ) continue;
+            
+    gettimeofday( &t_s, NULL );
+    sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 1 );
+    start = UDFFindFile( dvd, filename, &len );
+    if( start == 0 || len == 0 ) break;
+            
+    /* Perform CSS key cracking for this title. */
+    if(dvd->verbose >= 1) {
+      fprintf( stderr, "libdvdread: Get key for %s at 0x%08x\n", 
+               filename, start );
+    }
+    if( dvdinput_title( dvd->dev, (int)start ) < 0 ) {
+      if(dvd->verbose >= 0) {
+        fprintf( stderr, "libdvdread: Error cracking CSS key for %s (0x%08x)!!\n", filename, start);
+      }
+    }
+    gettimeofday( &t_e, NULL );
+    if(dvd->verbose >= 1) {
+      fprintf( stderr, "libdvdread: Elapsed time %ld\n",  
+               (long int) t_e.tv_sec - t_s.tv_sec );
+    }
+  }
+  title--;
+    
+  if(dvd->verbose >= 1) {
+    fprintf( stderr, "libdvdread: Found %d VTS's\n", title );
+  }
+  gettimeofday(&all_e, NULL);
+  if(dvd->verbose >= 1) {
+    fprintf( stderr, "libdvdread: Elapsed time %ld\n",  
+             (long int) all_e.tv_sec - all_s.tv_sec );
+  }
+  return 0;
+}
+
+
+
+/**
+ * Open a DVD image or block device file.
+ * Checks if the root directory in the udf image file can be found.
+ * If not it assumes this isn't a valid udf image and returns NULL
+ */
+static dvd_reader_t *DVDOpenImageFile( const char *location, int have_css )
+{
+  dvd_reader_t *dvd;
+  dvd_input_t dev;
+  int verbose;
+
+  verbose = get_verbose();
+
+  dev = dvdinput_open( location );
+  if( !dev ) {
+    if(verbose >= 1) {
+      fprintf( stderr, "libdvdread: Can't open '%s' for reading: %s\n",
+               location, strerror(errno));
+    }
+    return NULL;
+  }
+
+  dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
+  if( !dvd ) {
+    int tmp_errno = errno;
+    dvdinput_close(dev);
+    errno = tmp_errno;
+    return NULL;
+  }
+  dvd->verbose = verbose;
+  dvd->isImageFile = 1;
+  dvd->dev = dev;
+  dvd->path_root = NULL;
+    
+  dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
+  dvd->udfcache = NULL;
+
+  dvd->align = 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
+     * don't have that information. */
+    
+    dvd->css_state = 1; /* Need key init. */
+  }
+  dvd->css_title = 0;
+  
+  /* sanity check, is it a valid UDF image, can we find the root dir */
+  if(!UDFFindFile(dvd, "/", NULL)) {
+    dvdinput_close(dvd->dev);
+    if(dvd->udfcache) {
+      FreeUDFCache(dvd, dvd->udfcache);
+    }
+    if(dvd->align) {
+      if(dvd->verbose >= 0) {
+        fprintf(stderr, "libdvdread: DVDOpenImageFile(): Memory leak in align functions 1\n");
+      }
+    }
+    free(dvd);
+    return NULL;
+  }
+  return dvd;
+}
+
+static dvd_reader_t *DVDOpenPath( const char *path_root )
+{
+  dvd_reader_t *dvd;
+
+  dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
+  if( !dvd ) {
+    return NULL;
+  }
+  dvd->verbose = get_verbose();
+  dvd->isImageFile = 0;
+  dvd->dev = 0;
+  dvd->path_root = strdup( path_root );
+  if(!dvd->path_root) {
+    free(dvd);
+    return 0;
+  }
+  dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
+  dvd->udfcache = NULL;
+
+  dvd->align = NULL;
+
+  dvd->css_state = 0; /* Only used in the UDF path */
+  dvd->css_title = 0; /* Only matters in the UDF path */
+
+  return dvd;
+}
+
+#if defined(__sun)
+/* /dev/rdsk/c0t6d0s0 (link to /devices/...)
+   /vol/dev/rdsk/c0t6d0/??
+   /vol/rdsk/<name> */
+static char *sun_block2char( const char *path )
+{
+  char *new_path;
+
+  /* Must contain "/dsk/" */ 
+  if( !strstr( path, "/dsk/" ) ) return (char *) strdup( path );
+
+  /* Replace "/dsk/" with "/rdsk/" */
+  new_path = malloc( strlen(path) + 2 );
+  strcpy( new_path, path );
+  strcpy( strstr( new_path, "/dsk/" ), "" );
+  strcat( new_path, "/rdsk/" );
+  strcat( new_path, strstr( path, "/dsk/" ) + strlen( "/dsk/" ) );
+
+  return new_path;
+}
+#endif
+
+#if defined(SYS_BSD)
+/* FreeBSD /dev/(r)(a)cd0c (a is for atapi), recomended to _not_ use r
+   update: FreeBSD and DragonFly no longer uses the prefix so don't add it.
+
+   OpenBSD /dev/rcd0c, it needs to be the raw device
+   NetBSD  /dev/rcd0[d|c|..] d for x86, c (for non x86), perhaps others
+   Darwin  /dev/rdisk0,  it needs to be the raw device
+   BSD/OS  /dev/sr0c (if not mounted) or /dev/rsr0c ('c' any letter will do)
+   
+   returns a string allocated with strdup which should be free()'d when
+   no longer used.
+*/
+static char *bsd_block2char( const char *path )
+{
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+  return (char *) strdup( path );
+#else
+  char *new_path;
+
+  /* If it doesn't start with "/dev/" or does start with "/dev/r" exit */ 
+  if( strncmp( path, "/dev/",  5 ) || !strncmp( path, "/dev/r", 6 ) ) 
+    return (char *) strdup( path );
+
+  /* Replace "/dev/" with "/dev/r" */
+  new_path = malloc( strlen(path) + 2 );
+  strcpy( new_path, "/dev/r" );
+  strcat( new_path, path + strlen( "/dev/" ) );
+
+  return new_path;
+#endif /* __FreeBSD__ || __DragonFly__ */
+}
+#endif
+
+
+dvd_reader_t *DVDOpen( const char *path )
+{
+  struct stat fileinfo;
+  int ret, have_css;
+  char *dev_name = NULL;
+  int internal_errno = 0;
+  int verbose;
+
+  if( path == NULL ) {
+    errno = EINVAL;
+    return NULL;
+  }
+  
+  verbose = get_verbose();
+
+#ifdef WIN32
+  /* Stat doesn't work on devices under mingwin/cygwin. */
+  if( path[0] && path[1] == ':' && path[2] == '\0' )
+    {
+      /* Don't try to stat the file */
+      fileinfo.st_mode = S_IFBLK;
+    }
+  else
+#endif
+    {
+      ret = stat( path, &fileinfo );
+      if( ret < 0 ) {
+        int tmp_errno = errno;
+        /* If we can't stat the file, give up */
+        if(verbose >= 1) {
+          fprintf( stderr, "libdvdread: Can't stat '%s': %s\n",
+                   path, strerror(errno));
+        }
+        errno = tmp_errno;
+        return NULL;
+      }
+    }
+
+  /* Try to open libdvdcss or fall back to standard functions */
+  have_css = dvdinput_setup();
+
+  /* First check if this is a block/char device or a file*/
+  if( S_ISBLK( fileinfo.st_mode ) || 
+      S_ISCHR( fileinfo.st_mode ) || 
+      S_ISREG( fileinfo.st_mode ) ) {
+    /**
+     * Block devices and regular files are assumed to be DVD-Video images.
+     */
+    dvd_reader_t *dvd = NULL;
+#if defined(__sun)
+    dev_name = sun_block2char( path );
+#elif defined(SYS_BSD)
+    dev_name = bsd_block2char( path );
+#else
+    dev_name = strdup( path );
+#endif
+    dvd = DVDOpenImageFile( dev_name, have_css );
+    free( dev_name );
+    
+    return dvd;
+  } else if( S_ISDIR( fileinfo.st_mode ) ) {
+    dvd_reader_t *auth_drive = 0;
+    char *path_copy;
+#if defined(SYS_BSD)
+    struct fstab* fe;
+#elif defined(__sun) || defined(__linux__) || defined(__CYGWIN__)
+    FILE *mntfile;
+#endif
+
+    /* XXX: We should scream real loud here. */
+    if( !(path_copy = strdup( path ) ) ) return 0;
+
+#ifndef WIN32 /* don't have fchdir, and getcwd( NULL, ... ) is strange */
+    /* Resolve any symlinks and get the absolut dir name. */
+    {
+      char *new_path;
+      char *current_path;
+
+      current_path = malloc(PATH_MAX);
+      if(current_path) {
+        if(!getcwd(current_path, PATH_MAX)) {
+          free(current_path);
+          current_path = NULL;
+        }
+      }
+      if(current_path) {
+        chdir( path_copy );
+        new_path = malloc(PATH_MAX);
+        if(new_path) {
+          if(!getcwd(new_path, PATH_MAX )) {
+            free(new_path);
+            new_path = NULL;
+          }
+        }
+
+        chdir(current_path);
+        free(current_path);
+        if( new_path ) {
+          free( path_copy );
+          path_copy = new_path;
+        }
+      }
+    }
+#endif
+        
+    /**
+     * If we're being asked to open a directory, check if that directory
+     * is the mountpoint for a DVD-ROM which we can use instead.
+     */
+
+    if( strlen( path_copy ) > 1 ) {
+      if( path_copy[ strlen( path_copy ) - 1 ] == '/' ) {
+        path_copy[ strlen( path_copy ) - 1 ] = '\0';
+      }
+    }
+
+    if( strlen( path_copy ) >= 9 ) {
+      if( !strcasecmp( &(path_copy[ strlen( path_copy ) - 9 ]), 
+                       "/video_ts" ) ) {
+        path_copy[ strlen( path_copy ) - 9 ] = '\0';
+        if(path_copy[0] == '\0') {
+          path_copy[0] = '/';
+          path_copy[1] = '\0';
+        }
+      }
+    }
+
+#if defined(SYS_BSD)
+    if( ( fe = getfsfile( path_copy ) ) ) {
+      dev_name = bsd_block2char( fe->fs_spec );
+      if(verbose >= 1) {
+        fprintf( stderr,
+                 "libdvdread: Attempting to use device %s"
+                 " mounted on %s%s\n",
+                 dev_name,
+                 fe->fs_file,
+                 have_css ? " for CSS authentication" : "");
+      }
+      auth_drive = DVDOpenImageFile( dev_name, have_css );
+      if(!auth_drive) {
+        internal_errno = errno;
+      }
+    }
+#elif defined(__sun)
+    mntfile = fopen( MNTTAB, "r" );
+    if( mntfile ) {
+      struct mnttab mp;
+      int res;
+      
+      while( ( res = getmntent( mntfile, &mp ) ) != -1 ) {
+        if( res == 0 && !strcmp( mp.mnt_mountp, path_copy ) ) {
+          dev_name = sun_block2char( mp.mnt_special );
+          if(verbose >= 1) {
+            fprintf( stderr, 
+                     "libdvdread: Attempting to use device %s"
+                     " mounted on %s%s\n",
+                     dev_name,
+                     mp.mnt_mountp,
+                     have_css ? " for CSS authentication" : "");
+          }
+          auth_drive = DVDOpenImageFile( dev_name, have_css );
+          if(!auth_drive) {
+            internal_errno = errno;
+          }
+          break;
+        }
+      }
+      fclose( mntfile );
+    }
+#elif defined(__linux__) || defined(__CYGWIN__)
+    mntfile = fopen( MOUNTED, "r" );
+    if( mntfile ) {
+      struct mntent *me;
+ 
+      while( ( me = getmntent( mntfile ) ) ) {
+        if( !strcmp( me->mnt_dir, path_copy ) ) {
+          if(verbose >= 1) {
+            fprintf( stderr, 
+                     "libdvdread: Attempting to use device %s"
+                     " mounted on %s%s\n",
+                     me->mnt_fsname,
+                     me->mnt_dir,
+                     have_css ? " for CSS authentication" : "");
+          }
+          auth_drive = DVDOpenImageFile( me->mnt_fsname, have_css );
+          if(!auth_drive) {
+            internal_errno = errno;
+          }
+          dev_name = strdup(me->mnt_fsname);
+          break;
+        }
+      }
+      fclose( mntfile );
+    }
+#elif defined(__MINGW32__)
+    dev_name = strdup(path);
+    auth_drive = DVDOpenImageFile( path, have_css );
+#endif
+    if( !dev_name ) {
+      if(verbose >= 1) {
+        fprintf( stderr, "libdvdread: Couldn't find device name.\n" );
+      }
+    } else if( !auth_drive ) {
+      if(verbose >= 1) {
+        fprintf( stderr, "libdvdread: Device %s inaccessible%s: %s\n",
+                 dev_name,
+                 have_css ? ", CSS authentication not available" : "",
+                 strerror(internal_errno));
+      }
+    }
+
+    free( dev_name );
+    free( path_copy );
+
+    /**
+     * If we've opened a drive, just use that.
+     */
+    if( auth_drive ) {
+      return auth_drive;
+    }
+    /**
+     * Otherwise, we now try to open the directory tree instead.
+     */
+    return DVDOpenPath( path );
+  }
+
+  /* If it's none of the above, screw it. */
+  if(verbose >= 1) {
+    fprintf( stderr, "libdvdread: Could not open %s\n", path );
+  }
+  return 0;
+}
+
+void DVDClose( dvd_reader_t *dvd )
+{
+  if( dvd ) {
+    if( dvd->dev ) dvdinput_close( dvd->dev );
+    if( dvd->path_root ) free( dvd->path_root );
+    if( dvd->udfcache ) FreeUDFCache( dvd, dvd->udfcache );
+    if(dvd->align) {
+      if(dvd->verbose >= 0) {
+        fprintf(stderr, "libdvdread: DVDClose(): Memory leak in align functions\n");
+      }
+    }
+
+    free( dvd );
+  }
+}
+
+void DVDInit(void)
+{
+  dvdinput_setup();
+}
+
+void DVDFinish(void)
+{
+  dvdinput_free();
+}
+
+/**
+ * Open an unencrypted file on a DVD image file.
+ */
+static dvd_file_t *DVDOpenFileUDF( dvd_reader_t *dvd, char *filename )
+{
+  uint32_t start, len;
+  dvd_file_t *dvd_file;
+
+  start = UDFFindFile( dvd, filename, &len );
+  if( !start ) return 0;
+
+  dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
+  if( !dvd_file ) return 0;
+  dvd_file->dvd = dvd;
+  dvd_file->lb_start = start;
+  dvd_file->seek_pos = 0;
+  memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
+  memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
+  dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
+
+  return dvd_file;
+}
+
+/**
+ * Searches for <file> in directory <path>, ignoring case.
+ * Returns 0 and full filename in <filename>.
+ *     or -1 on file not found.
+ *     or -2 on path not found.
+ */
+static int findDirFile( const char *path, const char *file, char *filename ) 
+{
+  DIR *dir;
+  struct dirent *ent;
+
+  dir = opendir( path );
+  if( !dir ) return -2;
+
+  while( ( ent = readdir( dir ) ) != NULL ) {
+    if( !strcasecmp( ent->d_name, file ) ) {
+      sprintf( filename, "%s%s%s", path,
+               ( ( path[ strlen( path ) - 1 ] == '/' ) ? "" : "/" ),
+               ent->d_name );
+      closedir(dir);
+      return 0;
+    }
+  }
+  closedir(dir);
+  return -1;
+}
+
+static int findDVDFile( dvd_reader_t *dvd, const char *file, char *filename )
+{
+  char video_path[ PATH_MAX + 1 ];
+  const char *nodirfile;
+  int ret;
+
+  /* Strip off the directory for our search */
+  if( !strncasecmp( "/VIDEO_TS/", file, 10 ) ) {
+    nodirfile = &(file[ 10 ]);
+  } else {
+    nodirfile = file;
+  }
+
+  ret = findDirFile( dvd->path_root, nodirfile, filename );
+  if( ret < 0 ) {
+    /* Try also with adding the path, just in case. */
+    sprintf( video_path, "%s/VIDEO_TS/", dvd->path_root );
+    ret = findDirFile( video_path, nodirfile, filename );
+    if( ret < 0 ) {
+      /* Try with the path, but in lower case. */
+      sprintf( video_path, "%s/video_ts/", dvd->path_root );
+      ret = findDirFile( video_path, nodirfile, filename );
+      if( ret < 0 ) {
+        return 0;
+      }
+    }
+  }
+
+  return 1;
+}
+
+/**
+ * Open an unencrypted file from a DVD directory tree.
+ */
+static dvd_file_t *DVDOpenFilePath( dvd_reader_t *dvd, char *filename )
+{
+  char full_path[ PATH_MAX + 1 ];
+  dvd_file_t *dvd_file;
+  struct stat fileinfo;
+  dvd_input_t dev;
+
+  /* Get the full path of the file. */
+  if( !findDVDFile( dvd, filename, full_path ) ) return 0;
+
+  dev = dvdinput_open( full_path );
+  if( !dev ) return 0;
+
+  dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
+  if( !dvd_file ) return 0;
+  dvd_file->dvd = dvd;
+  dvd_file->lb_start = 0;
+  dvd_file->seek_pos = 0;
+  memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
+  memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
+  dvd_file->filesize = 0;
+
+  if( stat( full_path, &fileinfo ) < 0 ) {
+    if(dvd->verbose >= 1) {
+      fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
+    }
+    free( dvd_file );
+    return 0;
+  }
+  dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+  dvd_file->title_devs[ 0 ] = dev;
+  dvd_file->filesize = dvd_file->title_sizes[ 0 ];
+
+  return dvd_file;
+}
+
+static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *dvd, int title, int menu )
+{
+  char filename[ MAX_UDF_FILE_NAME_LEN ];
+  uint32_t start, len;
+  dvd_file_t *dvd_file;
+
+  if( title == 0 ) {
+    sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
+  } else {
+    sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
+  }
+  start = UDFFindFile( dvd, filename, &len );
+  if( start == 0 ) return 0;
+
+  dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
+  if( !dvd_file ) return 0;
+  dvd_file->dvd = dvd;
+  /*Hack*/ dvd_file->css_title = title << 1 | menu;
+  dvd_file->lb_start = start;
+  dvd_file->seek_pos = 0;
+  memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
+  memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
+  dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
+
+  /* Calculate the complete file size for every file in the VOBS */
+  if( !menu ) {
+    int cur;
+
+    for( cur = 2; cur < 10; cur++ ) {
+      sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur );
+      if( !UDFFindFile( dvd, filename, &len ) ) break;
+      dvd_file->filesize += len / DVD_VIDEO_LB_LEN;
+    }
+  }
+    
+  if( dvd->css_state == 1 /* Need key init */ ) {
+//    initAllCSSKeys( dvd );
+//    dvd->css_state = 2;
+  }
+  /*    
+        if( dvdinput_title( dvd_file->dvd->dev, (int)start ) < 0 ) {
+        fprintf( stderr, "libdvdread: Error cracking CSS key for %s\n",
+        filename );
+        }
+  */
+    
+  return dvd_file;
+}
+
+static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *dvd, int title, int menu )
+{
+  char filename[ MAX_UDF_FILE_NAME_LEN ];
+  char full_path[ PATH_MAX + 1 ];
+  struct stat fileinfo;
+  dvd_file_t *dvd_file;
+  int i;
+
+  dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
+  if( !dvd_file ) return 0;
+  dvd_file->dvd = dvd;
+  /*Hack*/ dvd_file->css_title = title << 1 | menu;
+  dvd_file->lb_start = 0;
+  dvd_file->seek_pos = 0;
+  memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
+  memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
+  dvd_file->filesize = 0;
+    
+  if( menu ) {
+    dvd_input_t dev;
+
+    if( title == 0 ) {
+      sprintf( filename, "VIDEO_TS.VOB" );
+    } else {
+      sprintf( filename, "VTS_%02i_0.VOB", title );
+    }
+    if( !findDVDFile( dvd, filename, full_path ) ) {
+      free( dvd_file );
+      return 0;
+    }
+
+    dev = dvdinput_open( full_path );
+    if( dev == NULL ) {
+      free( dvd_file );
+      return 0;
+    }
+
+    if( stat( full_path, &fileinfo ) < 0 ) {
+      if(dvd->verbose >= 1) {
+        fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
+      }
+      free( dvd_file );
+      return 0;
+    }
+    dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+    dvd_file->title_devs[ 0 ] = dev;
+    dvdinput_title( dvd_file->title_devs[0], 0);
+    dvd_file->filesize = dvd_file->title_sizes[ 0 ];
+
+  } else {
+    for( i = 0; i < 9; ++i ) {
+
+      sprintf( filename, "VTS_%02i_%i.VOB", title, i + 1 );
+      if( !findDVDFile( dvd, filename, full_path ) ) {
+        break;
+      }
+
+      if( stat( full_path, &fileinfo ) < 0 ) {
+        if(dvd->verbose >= 1) {
+          fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
+        }
+        break;
+      }
+
+      dvd_file->title_sizes[ i ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+      dvd_file->title_devs[ i ] = dvdinput_open( full_path );
+      dvdinput_title( dvd_file->title_devs[ i ], 0 );
+      dvd_file->filesize += dvd_file->title_sizes[ i ];
+    }
+    if( !dvd_file->title_devs[ 0 ] ) {
+      free( dvd_file );
+      return 0;
+    }
+  }
+
+  return dvd_file;
+}
+
+dvd_file_t *DVDOpenFile( dvd_reader_t *dvd, int titlenum, 
+                         dvd_read_domain_t domain )
+{
+  char filename[ MAX_UDF_FILE_NAME_LEN ];
+    
+  /* Check arguments. */
+  if( dvd == NULL || titlenum < 0 ) {
+    errno = EINVAL;
+    return NULL;
+  }
+
+  switch( domain ) {
+  case DVD_READ_INFO_FILE:
+    if( titlenum == 0 ) {
+      sprintf( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
+    } else {
+      sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );
+    }
+    break;
+  case DVD_READ_INFO_BACKUP_FILE:
+    if( titlenum == 0 ) {
+      sprintf( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
+    } else {
+      sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );
+    }
+    break;
+  case DVD_READ_MENU_VOBS:
+    if( dvd->isImageFile ) {
+      return DVDOpenVOBUDF( dvd, titlenum, 1 );
+    } else {
+      return DVDOpenVOBPath( dvd, titlenum, 1 );
+    }
+    break;
+  case DVD_READ_TITLE_VOBS:
+    if( titlenum == 0 ) return 0;
+    if( dvd->isImageFile ) {
+      return DVDOpenVOBUDF( dvd, titlenum, 0 );
+    } else {
+      return DVDOpenVOBPath( dvd, titlenum, 0 );
+    }
+    break;
+  default:
+    if(dvd->verbose >= 1) {
+      fprintf( stderr, "libdvdread: Invalid domain for file open.\n" );
+    }
+    errno = EINVAL;
+    return NULL;
+  }
+    
+  if( dvd->isImageFile ) {
+    return DVDOpenFileUDF( dvd, filename );
+  } else {
+    return DVDOpenFilePath( dvd, filename );
+  }
+}
+
+void DVDCloseFile( dvd_file_t *dvd_file )
+{
+  int i;
+
+  if( dvd_file ) {
+    if( dvd_file->dvd->isImageFile ) {
+      ;
+    } else {
+      for( i = 0; i < 9; ++i ) {
+        if( dvd_file->title_devs[ i ] ) {
+          dvdinput_close( dvd_file->title_devs[i] );
+        }
+      }
+    }
+
+    free( dvd_file );
+    dvd_file = 0;
+  }
+}
+
+static int DVDFileStatVOBUDF(dvd_reader_t *dvd, int title, 
+                             int menu, dvd_stat_t *statbuf)
+{
+  char filename[ MAX_UDF_FILE_NAME_LEN ];
+  uint32_t size;
+  off_t tot_size;
+  off_t parts_size[9];
+  int nr_parts = 0;
+  int n;
+ 
+  if( title == 0 ) {
+    sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
+  } else {
+    sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
+  }
+  if(!UDFFindFile( dvd, filename, &size )) {
+    return -1;
+  }
+  tot_size = size;
+  nr_parts = 1;
+  parts_size[0] = size;
+
+  if( !menu ) {
+    int cur;
+
+    for( cur = 2; cur < 10; cur++ ) {
+      sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur );
+      if( !UDFFindFile( dvd, filename, &size ) ) {
+        break;
+      }
+      parts_size[nr_parts] = size;
+      tot_size += size;
+      nr_parts++;
+    }
+  }
+  
+  statbuf->size = tot_size;
+  statbuf->nr_parts = nr_parts;
+  for(n = 0; n < nr_parts; n++) {
+    statbuf->parts_size[n] = parts_size[n];
+  }
+  return 0;
+}
+
+
+static int DVDFileStatVOBPath( dvd_reader_t *dvd, int title,
+                                       int menu, dvd_stat_t *statbuf )
+{
+  char filename[ MAX_UDF_FILE_NAME_LEN ];
+  char full_path[ PATH_MAX + 1 ];
+  struct stat fileinfo;
+  off_t tot_size;
+  off_t parts_size[9];
+  int nr_parts = 0;
+  int n;
+
+ 
+    
+  if( title == 0 ) {
+    sprintf( filename, "VIDEO_TS.VOB" );
+  } else {
+    sprintf( filename, "VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
+  }
+  if( !findDVDFile( dvd, filename, full_path ) ) {
+    return -1;
+  }
+  
+  if( stat( full_path, &fileinfo ) < 0 ) {
+    if(dvd->verbose >= 1) {
+      fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
+    }
+    return -1;
+  }
+  
+
+  tot_size = fileinfo.st_size;
+  nr_parts = 1;
+  parts_size[0] = fileinfo.st_size;
+
+  if( !menu ) {
+    int cur;
+    
+    for( cur = 2; cur < 10; cur++ ) {
+
+      sprintf( filename, "VTS_%02d_%d.VOB", title, cur );
+      if( !findDVDFile( dvd, filename, full_path ) ) {
+        break;
+      }
+
+      if( stat( full_path, &fileinfo ) < 0 ) {
+        if(dvd->verbose >= 1) {
+          fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
+        }
+        break;
+      }
+      
+      parts_size[nr_parts] = fileinfo.st_size;
+      tot_size += parts_size[nr_parts];
+      nr_parts++;
+    }
+  }
+
+  statbuf->size = tot_size;
+  statbuf->nr_parts = nr_parts;
+  for(n = 0; n < nr_parts; n++) {
+    statbuf->parts_size[n] = parts_size[n];
+  }
+  return 0;
+}
+
+
+int DVDFileStat(dvd_reader_t *dvd, int titlenum, 
+                dvd_read_domain_t domain, dvd_stat_t *statbuf)
+{
+  char filename[ MAX_UDF_FILE_NAME_LEN ];
+  char full_path[ PATH_MAX + 1 ];
+  struct stat fileinfo;
+  uint32_t size;
+
+  /* Check arguments. */
+  if( dvd == NULL || titlenum < 0 ) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  switch( domain ) {
+  case DVD_READ_INFO_FILE:
+    if( titlenum == 0 ) {
+      sprintf( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
+    } else {
+      sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );
+    }
+    break;
+  case DVD_READ_INFO_BACKUP_FILE:
+    if( titlenum == 0 ) {
+      sprintf( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
+    } else {
+      sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );
+    }
+    break;
+  case DVD_READ_MENU_VOBS:
+    if( dvd->isImageFile ) {
+      return DVDFileStatVOBUDF( dvd, titlenum, 1, statbuf );
+    } else {
+      return DVDFileStatVOBPath( dvd, titlenum, 1, statbuf );
+    }
+    break;
+  case DVD_READ_TITLE_VOBS:
+    if( titlenum == 0 ) {
+      return -1;
+    }
+    if( dvd->isImageFile ) {
+      return DVDFileStatVOBUDF( dvd, titlenum, 0, statbuf );
+    } else {
+      return DVDFileStatVOBPath( dvd, titlenum, 0, statbuf );
+    }
+    break;
+  default:
+    if(dvd->verbose >= 1) {
+      fprintf( stderr, "libdvdread: Invalid domain for file stat.\n" );
+    }
+    errno = EINVAL;
+    return -1;
+  }
+  
+  if( dvd->isImageFile ) {
+    if( UDFFindFile( dvd, filename, &size ) ) {
+      statbuf->size = size;
+      statbuf->nr_parts = 1;
+      statbuf->parts_size[0] = size;
+      return 0;
+    }
+  } else {
+    if( findDVDFile( dvd, filename, full_path ) )  {
+      if( stat( full_path, &fileinfo ) < 0 ) {
+        if(dvd->verbose >= 1) {
+          fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
+        }
+      } else {
+        statbuf->size = fileinfo.st_size;
+        statbuf->nr_parts = 1;
+        statbuf->parts_size[0] = statbuf->size;
+        return 0;
+      }
+    }
+  }
+  return -1;
+}
+
+/**
+ * Internal, but used from dvd_udf.c 
+ *
+ * @param device A read handle.
+ * @param lb_number Logical block number to start read from.
+ * @param block_count Number of logical blocks to read.
+ * @param data Pointer to buffer where read data should be stored.
+ *             This buffer must be large enough to hold lb_number*2048 bytes.
+ *             The pointer must be aligned to the logical block size when
+ *             reading from a raw/O_DIRECT device.
+ * @param encrypted 0 if no decryption shall be performed,
+ *                  1 if decryption shall be performed
+ * @param return Returns number of blocks read on success, negative on error
+ */
+int UDFReadBlocksRaw( dvd_reader_t *device, uint32_t lb_number,
+                      size_t block_count, unsigned char *data, 
+                      int encrypted )
+{
+  int ret;
+
+  if( !device->dev ) {
+    if(device->verbose >= 1) {
+      fprintf( stderr, "libdvdread: Fatal error in block read.\n" );
+    }
+    return 0;
+  }
+
+  ret = dvdinput_seek( device->dev, (int) lb_number );
+  if( ret != (int) lb_number ) {
+    if(device->verbose >= 1) {
+      fprintf( stderr,
+               "libdvdread: UDFReadBlocksRaw: Can't seek to block %u\n",
+               lb_number );
+    }
+    return 0;
+  }
+
+  return dvdinput_read( device->dev, (char *) data, 
+                        (int) block_count, encrypted );
+}
+
+/**
+ * This is using a single input and starting from 'dvd_file->lb_start' offset.
+ *
+ * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
+ * into the buffer located at 'data' and if 'encrypted' is set
+ * descramble the data if it's encrypted.  Returning either an
+ * negative error or the number of blocks read.
+ *
+ * @param data Pointer to buffer where read data should be placed.
+ *             This buffer must be large enough to hold block_count*2048 bytes.
+ *             The pointer must be aligned to 2048 bytes when reading from
+ *             a raw/O_DIRECT device.
+ * @return Returns the number of blocks read on success or a negative error.
+ */
+static int DVDReadBlocksUDF( dvd_file_t *dvd_file, uint32_t offset,
+                             size_t block_count, unsigned char *data,
+                             int 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'.
+ * data must be aligned to logical block size (2048 bytes) of the device
+ * for raw/O_DIRECT devices to work
+ * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
+ * into the buffer located at 'data' and if 'encrypted' is set
+ * descramble the data if it's encrypted.  Returning either an
+ * negative error or the number of blocks read.
+ *
+ * @param dvd_file A file read handle.
+ * @param offset Block offset from start of file.
+ * @return Returns number of blocks read on success, negative on error.
+ */
+static int DVDReadBlocksPath( dvd_file_t *dvd_file, unsigned int offset,
+                              size_t block_count, unsigned char *data,
+                              int encrypted )
+{
+  int i;
+  int ret, ret2, off;
+
+  ret = 0;
+  ret2 = 0;
+  for( i = 0; i < 9; ++i ) {
+    if( !dvd_file->title_sizes[ i ] ) return 0; /* Past end of file */
+
+    if( offset < dvd_file->title_sizes[ i ] ) {
+      if( ( offset + block_count ) <= dvd_file->title_sizes[ i ] ) {
+        off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );
+        if( off < 0 || off != (int)offset ) {
+          if(dvd_file->dvd->verbose >= 1) {
+            fprintf( stderr, "libdvdread: DVDReadBlocksPath1: Can't seek to block %d\n", 
+                     offset );
+          }
+          return off < 0 ? off : 0;
+        }
+        ret = dvdinput_read( dvd_file->title_devs[ i ], data,
+                             (int)block_count, encrypted );
+        break;
+      } else {
+        size_t part1_size = dvd_file->title_sizes[ i ] - offset;
+        /* FIXME: Really needs to be a while loop.
+         * (This is only true if you try and read >1GB at a time) */
+                
+        /* Read part 1 */
+        off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );
+        if( off < 0 || off != (int)offset ) {
+          if(dvd_file->dvd->verbose >= 1) {
+            fprintf( stderr, "libdvdread: DVDReadBlocksPath2: Can't seek to block %d\n", 
+                     offset );
+          }
+          return off < 0 ? off : 0;
+        }
+        ret = dvdinput_read( dvd_file->title_devs[ i ], data,
+                             (int)part1_size, encrypted );
+        if( ret < 0 ) return ret;
+        /* FIXME: This is wrong if i is the last file in the set. 
+         * also error from this read will not show in ret. */
+                
+        /* 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 );
+        if( off < 0 || off != 0 ) {
+          if(dvd_file->dvd->verbose >= 1) {
+            fprintf( stderr, "libdvdread: DVDReadBlocksPath3: Can't seek to block %d\n", 0 );
+          }
+          return off < 0 ? off : 0;
+        }
+        ret2 = dvdinput_read( dvd_file->title_devs[ i + 1 ], 
+                              data + ( part1_size
+                                       * (int64_t)DVD_VIDEO_LB_LEN ),
+                              (int)(block_count - part1_size),
+                              encrypted );
+        if( ret2 < 0 ) return ret2;
+        break;
+      }
+    } else {
+      offset -= dvd_file->title_sizes[ i ];
+    }
+  }
+
+  return ret + ret2;
+}
+
+/**
+ * This is broken reading more than 2Gb at a time if ssize_t is 32-bit.
+ */
+ssize_t DVDReadBlocks( dvd_file_t *dvd_file, int offset, 
+                       size_t block_count, unsigned char *data )
+{
+  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 );
+    } 
+    /* 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 ) {
+    ret = DVDReadBlocksUDF( dvd_file, (uint32_t)offset, 
+                            block_count, data, DVDINPUT_READ_DECRYPT );
+  } else {
+    ret = DVDReadBlocksPath( dvd_file, (unsigned int)offset, 
+                             block_count, data, DVDINPUT_READ_DECRYPT );
+  }
+    
+  return (ssize_t)ret;
+}
+
+int DVDFileSeek( dvd_file_t *dvd_file, int offset )
+{
+  /* Check arguments. */
+  if( dvd_file == NULL || offset < 0 )
+    return -1;
+    
+  if( offset > dvd_file->filesize * DVD_VIDEO_LB_LEN ) {
+    return -1;
+  }
+  dvd_file->seek_pos = (uint32_t) offset;
+  return offset;
+}
+
+#ifndef HAVE_UINTPTR_T
+#warning "Assuming that (unsigned long) can hold (void *)"
+typedef unsigned long uintptr_t;
+#endif
+
+#define DVD_ALIGN(ptr) (void *)((((uintptr_t)(ptr)) + (DVD_VIDEO_LB_LEN-1)) \
+                                / DVD_VIDEO_LB_LEN * DVD_VIDEO_LB_LEN)
+
+ssize_t DVDReadBytes( dvd_file_t *dvd_file, void *data, size_t byte_size )
+{
+  unsigned char *secbuf_start;
+  unsigned char *secbuf; //must be aligned to 2048-bytes for raw/O_DIRECT
+  unsigned int numsec, seek_sector, seek_byte;
+  int ret;
+    
+  /* Check arguments. */
+  if( dvd_file == NULL || data == NULL ) {
+    errno = EINVAL;
+    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 ) +
+    ( ( ( seek_byte + byte_size ) % DVD_VIDEO_LB_LEN ) ? 1 : 0 );
+
+  /* must align to 2048 bytes if we are reading from raw/O_DIRECT */
+  secbuf_start = (unsigned char *) malloc( (numsec+1) * DVD_VIDEO_LB_LEN );
+  if( !secbuf_start ) {
+    /* errno will be set to ENOMEM by malloc */
+    return -1;
+  }
+
+  secbuf = DVD_ALIGN(secbuf_start);
+
+  if( dvd_file->dvd->isImageFile ) {
+    ret = DVDReadBlocksUDF( dvd_file, (uint32_t) seek_sector, 
+                            (size_t) numsec, secbuf, DVDINPUT_NOFLAGS );
+  } else {
+    ret = DVDReadBlocksPath( dvd_file, seek_sector, 
+                             (size_t) numsec, secbuf, DVDINPUT_NOFLAGS );
+  }
+
+  if( ret != (int) numsec ) {
+    free( secbuf_start );
+    return ret < 0 ? ret : 0;
+  }
+
+  memcpy( data, &(secbuf[ seek_byte ]), byte_size );
+  free( secbuf_start );
+
+  dvd_file->seek_pos += byte_size;
+  return byte_size;
+}
+
+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;
+  int nr_of_files = 0;
+  int tmp_errno;
+  int nofiles_errno = ENOENT;
+  /* Check arguments. */
+  if( dvd == NULL || discid == NULL ) {
+    errno = EINVAL;
+    return -1;
+  }
+  /* 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 );
+
+      nr_of_files++;
+
+      if( buffer == NULL ) {
+        /* errno will be set to ENOMEM by malloc */
+        return -1;
+      }
+
+      bytes_read = DVDReadBytes( dvd_file, buffer, file_size );
+      if( bytes_read != file_size ) {
+        tmp_errno = errno;
+        if(dvd->verbose >= 1) {
+          fprintf( stderr, "libdvdread: DVDDiscId read returned %d bytes"
+                   ", wanted %d\n", (int)bytes_read, (int)file_size );
+        }
+        free(buffer);
+        DVDCloseFile( dvd_file );
+        errno = tmp_errno;
+        return -1;
+      }
+            
+      md5_process_bytes( buffer, file_size,  &ctx );
+            
+      DVDCloseFile( dvd_file );
+      free( buffer );
+    } else {
+      if(errno != ENOENT) {
+        nofiles_errno = errno;
+      }
+    }
+  }
+  md5_finish_ctx( &ctx, discid );
+  if(nr_of_files == 0) {
+    errno = nofiles_errno;
+    return -1;
+  }
+  return 0;
+}
+
+
+int DVDISOVolumeInfo( dvd_reader_t *dvd,
+                      char *volid, unsigned int volid_size,
+                      unsigned char *volsetid, unsigned int volsetid_size )
+{
+  unsigned char *buffer; /* must be aligned to 2048 for raw/O_DIRECT */
+  unsigned char *buffer_start; 
+  int ret;
+
+  /* Check arguments. */
+  if( dvd == NULL ) {
+    errno = EINVAL;
+    return -1;
+  }
+  
+  if( dvd->dev == NULL ) {
+    /* No block access, so no ISO... */
+    errno = EINVAL;
+    return -1;
+  }
+  
+  buffer_start = malloc( 2 * DVD_VIDEO_LB_LEN );
+  if( buffer_start == NULL ) {
+    return -1;
+  }
+
+  buffer = DVD_ALIGN(buffer_start);
+  
+  ret = UDFReadBlocksRaw( dvd, 16, 1, buffer, 0 );
+  if( ret != 1 ) {
+    if(dvd->verbose >= 1) {
+      fprintf( stderr, "libdvdread: DVDISOVolumeInfo, failed to "
+               "read ISO9660 Primary Volume Descriptor!\n" );
+    }
+    free(buffer_start);
+    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);
+  }
+  free(buffer_start);
+
+  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;  
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/dvd_reader.h	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,344 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+#ifndef DVD_READER_H_INCLUDED
+#define DVD_READER_H_INCLUDED
+
+/*
+ * Copyright (C) 2001, 2002 Billy Biggs <vektor@dumbterm.net>,
+ *                          Håkan Hjort <d95hjort@dtek.chalmers.se>,
+ *                          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
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <sys/types.h>
+
+/**
+ * The DVD access interface.
+ *
+ * This file contains the functions that form the interface to to
+ * reading files located on a DVD.
+ */
+
+/**
+ * The current version. (0.9.4 => 904, 1.2.3 => 10203)
+ */
+#define DVDREAD_VERSION 907
+
+
+/**
+ * The length of one Logical Block of a DVD.
+ */
+#define DVD_VIDEO_LB_LEN 2048
+
+/**
+ * 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;
+
+/**
+ * Returns the compiled version. (DVDREAD_VERSION as an int)
+ */
+int DVDVersion(void);
+
+
+/**
+ * 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.
+ *
+ * If the given file is a block device, or is the mountpoint for a block
+ * device, then that device is used for CSS authentication using libdvdcss.
+ * If no device is available, then no CSS authentication is performed, 
+ * and we hope that the image is decrypted.
+ *
+ * If the path given is a directory, then the files in that directory may be
+ * in any one of these formats:
+ *
+ *   path/VIDEO_TS/VTS_01_1.VOB
+ *   path/video_ts/vts_01_1.vob
+ *   path/VTS_01_1.VOB
+ *   path/vts_01_1.vob
+ *
+ * @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);
+ *
+ * Threads: this function uses chdir() and getcwd().
+ * The current working directory is global to all threads,
+ * so using chdir/getcwd in another thread could give unexpected results.
+ */
+dvd_reader_t *DVDOpen( const char * );
+
+/**
+ * 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 * );
+
+/**
+ * Initializes libdvdread to be used with multithreading apps.
+ *
+ * You must call this function before using any other functions of libdvdread
+ * if you are going to use libdvdread in multiple threads in your program.
+ * If you are not using threads, or using libdvdread from just one thread,
+ * you do not need to call this, but you are allowed to do so anyway.
+ * 
+ * There are several restrictions on how you can use libdvdread in
+ * multithreading apps, see further documentation.
+ *
+ * If you have called DVDFinish() you need to call DVDInit again to use
+ * libdvdread in multiple threads.
+ *
+ * DVDInit(void);
+ */
+void DVDInit(void);
+
+/**
+ * frees any dlopened objects.
+ *
+ * You must DVDClose all handles opened with DVDOpen before calling this.
+ * Use this function if you need to close the dlopened libs and any other
+ * objects that have been dynamically allocated by libdvdread.
+ * 
+ * DVDFinish(void);
+ */
+void DVDFinish(void);
+
+/**
+ * 
+ */
+typedef enum {
+  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;
+
+/**
+ *
+ */
+typedef struct {
+  off_t size;          /**< Total size of file in bytes */
+  int nr_parts;           /**< Number of file parts */
+  off_t parts_size[9]; /**< Size of each part in bytes */
+} dvd_stat_t;
+
+/**
+ * Stats a file on the DVD given the title number and domain.
+ * The information about the file is stored in a dvd_stat_t
+ * which contains information about the size of the file and
+ * the number of parts in case of a multipart file and the respective
+ * sizes of the parts.
+ * A multipart file is for instance VTS_02_1.VOB, VTS_02_2.VOB, VTS_02_3.VOB
+ * The size of VTS_02_1.VOB will be stored in stat->parts_size[0],
+ * VTS_02_2.VOB in stat->parts_size[1], ...
+ * The total size (sum of all parts) is stored in stat->size and
+ * stat->nr_parts will hold the number of parts.
+ * Only DVD_READ_TITLE_VOBS (VTS_??_[1-9].VOB) can be multipart files.
+ * 
+ * This function is only of use if you want to get the size of each file
+ * in the filesystem. These sizes are not needed to use any other
+ * functions in libdvdread. 
+ *
+ * @param dvd  A dvd read handle.
+ * @param titlenum Which Video Title Set should be used, VIDEO_TS is 0.
+ * @param domain Which domain. 
+ * @param stat Pointer to where the result is stored.
+ * @return If successful 0, otherwise -1.
+ *
+ * int DVDFileStat(dvd, titlenum, domain, stat);
+ */
+int DVDFileStat(dvd_reader_t *, int, dvd_read_domain_t, dvd_stat_t *);
+  
+/**
+ * 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.
+ *
+ * @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 );
+
+/**
+ * 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 * );
+
+/**
+ * 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.
+ *             It must be aligned to the logical block size of the device when
+ *             reading from a raw/O_DIRECT device (2048 bytes for DVD)
+ * @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 * );
+
+/**
+ * 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 );
+
+/**
+ * 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 );
+  
+/**
+ * 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
+#endif /* DVD_READER_H_INCLUDED */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/dvd_udf.c	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,1217 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/*
+ * This code is based on dvdudf by:
+ *   Christian Wolff <scarabaeus@convergence.de>.
+ *
+ * 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
+ * project's page is at http://linuxtv.org/dvd/
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  Or, point your browser to
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+ 
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+#if defined(HAVE_INTTYPES_H)
+#include <inttypes.h>
+#elif defined(HAVE_STDINT_H)
+#include <stdint.h>
+#endif
+
+#include "dvd_reader.h"
+#include "dvd_udf.h"
+#include "dvdread_internal.h"
+
+#ifndef EMEDIUMTYPE
+#define EMEDIUMTYPE ENOENT
+#endif
+
+#ifndef HAVE_UINTPTR_T
+#warning "Assuming that (unsigned long) can hold (void *)"
+typedef unsigned long uintptr_t;
+#endif
+
+#define DVD_ALIGN(ptr) (void *)((((uintptr_t)(ptr)) + (DVD_VIDEO_LB_LEN-1)) \
+                                / DVD_VIDEO_LB_LEN * DVD_VIDEO_LB_LEN)
+
+typedef struct {
+  void *start;
+  void *aligned;
+} dvdalign_ptrs_t;
+
+typedef struct {
+  dvdalign_ptrs_t *ptrs;
+  uint32_t ptrs_in_use;
+  uint32_t ptrs_max;
+} dvdalign_t;
+
+extern void *GetAlignHandle(dvd_reader_t *device);
+extern void SetAlignHandle(dvd_reader_t *device, void *align);
+
+/**
+ * Allocates aligned memory (for use with reads from raw/O_DIRECT devices).
+ * This memory must be freed with dvdalign_free()
+ * The size of the memory that is allocate is num_lbs*2048 bytes.
+ * The memory will be suitably aligned for use with
+ * block reads from raw/O_DIRECT device.
+ * @param num_lbs Number of logical blocks (2048 bytes) to allocate.
+ * @return Returns pointer to allocated memory, or NULL on failure
+ * This isn't supposed to be fast/efficient, if that is needed
+ * this function should be rewritten to use posix_memalign or similar.
+ * It's just needed for aligning memory for small block reads from
+ * raw/O_DIRECT devices. 
+ * We assume that 2048 is enough alignment for all systems at the moment.
+ * Not thread safe. Only use this from one thread.
+ * Depends on sizeof(unsigned long) being at least as large as sizeof(void *)
+ */
+static void *dvdalign_lbmalloc(dvd_reader_t *device, uint32_t num_lbs)
+{
+  void *m;
+  int n;
+  dvdalign_t *a;
+  
+  m = malloc((num_lbs+1)*DVD_VIDEO_LB_LEN);
+  if(m == NULL) {
+    return m;
+  }
+  a = (dvdalign_t *)GetAlignHandle(device);
+  if(a == NULL) {
+    a = malloc(sizeof(dvdalign_t));
+    if(a == NULL) {
+      return a;
+    }
+    a->ptrs = NULL;
+    a->ptrs_in_use = 0;
+    a->ptrs_max = 0;
+    SetAlignHandle(device, (void *)a);
+  }
+  
+  if(a->ptrs_in_use >= a->ptrs_max) {
+    a->ptrs = realloc(a->ptrs, (a->ptrs_max+10)*sizeof(dvdalign_ptrs_t));
+    if(a->ptrs == NULL) {
+      free(m);
+      return NULL;
+    }
+    a->ptrs_max+=10;
+    for(n = a->ptrs_in_use; n < a->ptrs_max; n++) {
+      a->ptrs[n].start = NULL;
+      a->ptrs[n].aligned = NULL;
+    }
+    n = a->ptrs_in_use;
+  } else {
+    for(n = 0; n < a->ptrs_max; n++) {
+      if(a->ptrs[n].start == NULL) {
+        break;
+      }
+    }
+  }
+
+  a->ptrs[n].start = m;
+  a->ptrs[n].aligned = DVD_ALIGN(m);
+
+  a->ptrs_in_use++;
+
+  /* If this function starts to be used too much print a warning.
+     Either there is a memory leak somewhere or we need to rewrite this to
+     a more efficient version.
+  */
+  if(a->ptrs_in_use > 50) {
+    if(dvdread_verbose(device) >= 0) {
+      fprintf(stderr, "libdvdread: dvdalign_lbmalloc(), more allocs than supposed: %u\n", a->ptrs_in_use);
+    }
+  }
+
+  return  a->ptrs[n].aligned;
+}
+
+/**
+ * Frees memory allocated with dvdalign_lbmemory() 
+ * @param ptr Pointer to memory space to free
+ * Not thread safe.
+ */
+static void dvdalign_lbfree(dvd_reader_t *device, void *ptr)
+{
+  int n;
+  dvdalign_t *a;
+
+  a = (dvdalign_t *)GetAlignHandle(device);
+  if(a && a->ptrs) {
+    for(n = 0; n < a->ptrs_max; n++) {
+      if(a->ptrs[n].aligned == ptr) {
+        free(a->ptrs[n].start);
+        a->ptrs[n].start = NULL;
+        a->ptrs[n].aligned = NULL;
+        a->ptrs_in_use--;
+        if(a->ptrs_in_use == 0) {
+          free(a->ptrs);
+          a->ptrs = NULL;
+          a->ptrs_max = 0;
+          free(a);
+          a = NULL;
+          SetAlignHandle(device, (void *)a);
+        }
+        return;
+      }
+    }
+  }
+  if(dvdread_verbose(device) >= 0) {
+    fprintf(stderr, "libdvdread: dvdalign_lbfree(), error trying to free mem: %08lx (%u)\n", (unsigned long)ptr, a ? a->ptrs_in_use : 0);
+  }
+}
+
+
+/* Private but located in/shared with dvd_reader.c */
+extern int UDFReadBlocksRaw( dvd_reader_t *device, uint32_t lb_number,
+                             size_t block_count, unsigned char *data, 
+                             int encrypted );
+
+/** @internal
+ * Its required to either fail or deliver all the blocks asked for. 
+ *
+ * @param data Pointer to a buffer where data is returned. This must be large
+ *   enough to hold lb_number*2048 bytes.
+ *   It must be aligned to system specific (2048) logical blocks size when
+ *   reading from raw/O_DIRECT device.
+ */
+static int DVDReadLBUDF( dvd_reader_t *device, uint32_t lb_number,
+                         size_t block_count, unsigned char *data, 
+                         int encrypted )
+{
+  int ret;
+  size_t count = block_count;
+  
+  while(count > 0) {
+    
+    ret = UDFReadBlocksRaw(device, lb_number, count, data, encrypted);
+        
+    if(ret <= 0) {
+      /* One of the reads failed or nothing more to read, too bad.
+       * We won't even bother returning the reads that went ok. */
+      return ret;
+    }
+    
+    count -= (size_t)ret;
+    lb_number += (uint32_t)ret;
+  }
+
+  return block_count;
+}
+
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+struct Partition {
+  int valid;
+  char VolumeDesc[128];
+  uint16_t Flags;
+  uint16_t Number;
+  char Contents[32];
+  uint32_t AccessType;
+  uint32_t Start;
+  uint32_t Length;
+};
+
+struct AD {
+  uint32_t Location;
+  uint32_t Length;
+  uint8_t  Flags;
+  uint16_t Partition;
+};
+
+struct extent_ad {
+  uint32_t location;
+  uint32_t length;
+};
+
+struct avdp_t {
+  struct extent_ad mvds;
+  struct extent_ad rvds;
+};
+
+struct pvd_t {
+  uint8_t VolumeIdentifier[32];
+  uint8_t VolumeSetIdentifier[128];
+};
+
+struct lbudf {
+  uint32_t lb;
+  uint8_t *data;
+};
+
+struct icbmap {
+  uint32_t lbn;
+  struct AD file;
+  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(dvd_reader_t *device, void *cache)
+{
+  int n;
+  
+  struct udf_cache *c = (struct udf_cache *)cache;
+  if(c == NULL) {
+    return;
+  }
+
+  for(n = 0; n < c->lb_num; n++) {
+    if(c->lbs[n].data) {
+      /* free data */
+      dvdalign_lbfree(device, c->lbs[n].data);
+    }
+  }
+  c->lb_num = 0;
+
+  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))
+#define GETN3(p) ((uint32_t)data[p] | ((uint32_t)data[(p) + 1] << 8)    \
+                  | ((uint32_t)data[(p) + 2] << 16))
+#define GETN4(p) ((uint32_t)data[p]                     \
+                  | ((uint32_t)data[(p) + 1] << 8)      \
+                  | ((uint32_t)data[(p) + 2] << 16)     \
+                  | ((uint32_t)data[(p) + 3] << 24))
+/* This is wrong with regard to endianess */
+#define GETN(p, n, target) memcpy(target, &data[p], n)
+
+static int Unicodedecode( uint8_t *data, int len, char *target ) 
+{
+  int p = 1, i = 0;
+
+  if( ( data[ 0 ] == 8 ) || ( data[ 0 ] == 16 ) ) do {
+    if( data[ 0 ] == 16 ) p++;  /* Ignore MSB of unicode16 */
+    if( p < len ) {
+      target[ i++ ] = data[ p++ ];
+    }
+  } while( p < len );
+
+  target[ i ] = '\0';
+  return 0;
+}
+
+static int UDFDescriptor( uint8_t *data, uint16_t *TagID ) 
+{
+  *TagID = GETN2(0);
+  // TODO: check CRC 'n stuff
+  return 0;
+}
+
+static int UDFExtentAD( uint8_t *data, uint32_t *Length, uint32_t *Location ) 
+{
+  *Length   = GETN4(0);
+  *Location = GETN4(4);
+  return 0;
+}
+
+static int UDFShortAD( uint8_t *data, struct AD *ad, 
+                       struct Partition *partition ) 
+{
+  ad->Length = GETN4(0);
+  ad->Flags = ad->Length >> 30;
+  ad->Length &= 0x3FFFFFFF;
+  ad->Location = GETN4(4);
+  ad->Partition = partition->Number; // use number of current partition
+  return 0;
+}
+
+static int UDFLongAD( uint8_t *data, struct AD *ad )
+{
+  ad->Length = GETN4(0);
+  ad->Flags = ad->Length >> 30;
+  ad->Length &= 0x3FFFFFFF;
+  ad->Location = GETN4(4);
+  ad->Partition = GETN2(8);
+  //GETN(10, 6, Use);
+  return 0;
+}
+
+static int UDFExtAD( uint8_t *data, struct AD *ad )
+{
+  ad->Length = GETN4(0);
+  ad->Flags = ad->Length >> 30;
+  ad->Length &= 0x3FFFFFFF;
+  ad->Location = GETN4(12);
+  ad->Partition = GETN2(16);
+  //GETN(10, 6, Use);
+  return 0;
+}
+
+static int UDFICB( uint8_t *data, uint8_t *FileType, uint16_t *Flags )
+{
+  *FileType = GETN1(11);
+  *Flags = GETN2(18);
+  return 0;
+}
+
+
+static int UDFPartition( uint8_t *data, uint16_t *Flags, uint16_t *Number,
+                         char *Contents, uint32_t *Start, uint32_t *Length )
+{
+  *Flags = GETN2(20);
+  *Number = GETN2(22);
+  GETN(24, 32, Contents);
+  *Start = GETN4(188);
+  *Length = GETN4(192);
+  return 0;
+}
+
+/**
+ * Reads the volume descriptor and checks the parameters.  Returns 0 on OK, 1
+ * on error.
+ */
+static int UDFLogVolume( uint8_t *data, char *VolumeDescriptor )
+{
+  uint32_t lbsize, MT_L, N_PM;
+  Unicodedecode(&data[84], 128, VolumeDescriptor);
+  lbsize = GETN4(212);  // should be 2048
+  MT_L = GETN4(264);    // should be 6
+  N_PM = GETN4(268);    // should be 1
+  if (lbsize != DVD_VIDEO_LB_LEN) return 1;
+  return 0;
+}
+
+static int UDFFileEntry( uint8_t *data, uint8_t *FileType, 
+                         struct Partition *partition, struct AD *ad )
+{
+  uint16_t flags;
+  uint32_t L_EA, L_AD;
+  unsigned int p;
+
+  UDFICB( &data[ 16 ], FileType, &flags );
+   
+  /* Init ad for an empty file (i.e. there isn't a AD, L_AD == 0 ) */
+  ad->Length = GETN4( 60 ); // Really 8 bytes a 56
+  ad->Flags = 0;
+  ad->Location = 0; // what should we put here? 
+  ad->Partition = partition->Number; // use number of current partition
+
+  L_EA = GETN4( 168 );
+  L_AD = GETN4( 172 );
+  p = 176 + L_EA;
+  while( p < 176 + L_EA + L_AD ) {
+    switch( flags & 0x0007 ) {
+    case 0: UDFShortAD( &data[ p ], ad, partition ); p += 8;  break;
+    case 1: UDFLongAD( &data[ p ], ad );  p += 16; break;
+    case 2: UDFExtAD( &data[ p ], ad );   p += 20; break;
+    case 3:
+      switch( L_AD ) {
+      case 8:  UDFShortAD( &data[ p ], ad, partition ); break;
+      case 16: UDFLongAD( &data[ p ], ad );  break;
+      case 20: UDFExtAD( &data[ p ], ad );   break;
+      }
+      p += L_AD;
+      break;
+    default:
+      p += L_AD; break;
+    }
+  }
+  return 0;
+}
+
+static int UDFFileIdentifier( uint8_t *data, uint8_t *FileCharacteristics,
+                              char *FileName, struct AD *FileICB )
+{
+  uint8_t L_FI;
+  uint16_t L_IU;
+
+  *FileCharacteristics = GETN1(18);
+  L_FI = GETN1(19);
+  UDFLongAD(&data[20], FileICB);
+  L_IU = GETN2(36);
+  if (L_FI) Unicodedecode(&data[38 + L_IU], L_FI, FileName);
+  else FileName[0] = '\0';
+  return 4 * ((38 + L_FI + L_IU + 3) / 4);
+}
+
+/**
+ * Maps ICB to FileAD
+ * ICB: Location of ICB of directory to scan
+ * FileType: Type of the file
+ * File: Location of file the ICB is pointing to
+ * return 1 on success, 0 on error;
+ */
+static int UDFMapICB( dvd_reader_t *device, struct AD ICB, uint8_t *FileType,
+                      struct Partition *partition, struct AD *File ) 
+{
+  uint8_t *LogBlock;
+  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;
+  }
+
+  LogBlock = dvdalign_lbmalloc(device, 1);
+  if(!LogBlock) {
+    return 0;
+  }
+    
+  do {
+    if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) {
+      TagID = 0;
+    } else {
+      UDFDescriptor( LogBlock, &TagID );
+    }
+
+    if( TagID == 261 ) {
+      UDFFileEntry( LogBlock, FileType, partition, File );
+      tmpmap.file = *File;
+      tmpmap.filetype = *FileType;
+      SetUDFCache(device, MapCache, tmpmap.lbn, &tmpmap);
+      dvdalign_lbfree(device, LogBlock);
+      return 1;
+    };
+  } while( ( lbnum <= partition->Start + ICB.Location + ( ICB.Length - 1 )
+             / DVD_VIDEO_LB_LEN ) && ( TagID != 261 ) );
+
+  dvdalign_lbfree(device, LogBlock);
+  return 0;
+}
+
+/**
+ * Dir: Location of directory to scan
+ * FileName: Name of file to look for
+ * FileICB: Location of ICB of the found file
+ * return 1 on success, 0 on error;
+ */
+static int UDFScanDir( dvd_reader_t *device, struct AD Dir, char *FileName,
+                       struct Partition *partition, struct AD *FileICB,
+                       int cache_file_info) 
+{
+  char filename[ MAX_UDF_FILE_NAME_LEN ];
+  uint8_t *directory;
+  uint32_t lbnum;
+  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 = dvdalign_lbmalloc(device, dir_lba)) == NULL) {
+        return 0;
+      }
+      if( DVDReadLBUDF( device, lbnum, dir_lba, cached_dir, 0) <= 0 ) {
+        dvdalign_lbfree(device, cached_dir);
+        cached_dir = NULL;
+      }
+      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;
+  }
+
+  directory = dvdalign_lbmalloc(device, 2);
+  if(!directory) {
+    return 0;
+  }
+  if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) {
+    dvdalign_lbfree(device, directory);
+    return 0;
+  }
+
+  p = 0;
+  while( p < Dir.Length ) {
+    if( p > DVD_VIDEO_LB_LEN ) {
+      ++lbnum;
+      p -= DVD_VIDEO_LB_LEN;
+      Dir.Length -= DVD_VIDEO_LB_LEN;
+      if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) {
+        dvdalign_lbfree(device, directory);
+        return 0;
+      }
+    }
+    UDFDescriptor( &directory[ p ], &TagID );
+    if( TagID == 257 ) {
+      p += UDFFileIdentifier( &directory[ p ], &filechar,
+                              filename, FileICB );
+      if( !strcasecmp( FileName, filename ) ) {
+        dvdalign_lbfree(device, directory);
+        return 1;
+      }
+    } else {
+      dvdalign_lbfree(device, directory);
+      return 0;
+    }
+  }
+
+  dvdalign_lbfree(device, directory);
+  return 0;
+}
+
+
+static int UDFGetAVDP( dvd_reader_t *device,
+                       struct avdp_t *avdp)
+{
+  uint8_t *Anchor;
+  uint32_t lbnum, MVDS_location, MVDS_length;
+  uint16_t TagID;
+  uint32_t lastsector;
+  int terminate;
+  struct avdp_t; 
+  
+  if(GetUDFCache(device, AVDPCache, 0, avdp)) {
+    return 1;
+  }
+  
+  /* Find Anchor */
+  lastsector = 0;
+  lbnum = 256;   /* Try #1, prime anchor */
+  terminate = 0;
+  
+  Anchor = dvdalign_lbmalloc(device, 1);
+  if(!Anchor) {
+    return 0;
+  }
+  for(;;) {
+    if( DVDReadLBUDF( device, lbnum, 1, Anchor, 0 ) > 0 ) {
+      UDFDescriptor( Anchor, &TagID );
+    } else {
+      TagID = 0;
+    }
+    if (TagID != 2) {
+      /* Not an anchor */
+      if( terminate ) {
+        dvdalign_lbfree(device, Anchor);
+        errno = EMEDIUMTYPE;
+        return 0; /* Final try failed */
+      } 
+      
+      if( lastsector ) {
+        /* We already found the last sector.  Try #3, alternative
+         * backup anchor.  If that fails, don't try again.
+         */
+        lbnum = lastsector;
+        terminate = 1;
+      } else {
+        /* TODO: Find last sector of the disc (this is optional). */
+        if( lastsector ) {
+          /* Try #2, backup anchor */
+          lbnum = lastsector - 256;
+        } else {
+          /* Unable to find last sector */
+          dvdalign_lbfree(device, Anchor);
+          errno = EMEDIUMTYPE;
+          return 0;
+        }
+      }
+    } else {
+      /* It's an anchor! We can leave */
+      break;
+    }
+  }
+  /* Main volume descriptor */
+  UDFExtentAD( &Anchor[ 16 ], &MVDS_length, &MVDS_location );
+  avdp->mvds.location = MVDS_location;
+  avdp->mvds.length = MVDS_length;
+  
+  /* Backup volume descriptor */
+  UDFExtentAD( &Anchor[ 24 ], &MVDS_length, &MVDS_location );
+  avdp->rvds.location = MVDS_location;
+  avdp->rvds.length = MVDS_length;
+  
+  SetUDFCache(device, AVDPCache, 0, avdp);
+  
+  dvdalign_lbfree(device, Anchor);
+  return 1;
+}
+
+/**
+ * Looks for partition on the disc.  Returns 1 if partition found, 0 on error.
+ *   partnum: Number of the partition, starting at 0.
+ *   part: structure to fill with the partition information
+ */
+static int UDFFindPartition( dvd_reader_t *device, int partnum,
+                             struct Partition *part ) 
+{
+  uint8_t *LogBlock;
+  uint32_t lbnum, MVDS_location, MVDS_length;
+  uint16_t TagID;
+  int i, volvalid;
+  struct avdp_t avdp;
+
+    
+  if(!UDFGetAVDP(device, &avdp)) {
+    return 0;
+  }
+
+  LogBlock = dvdalign_lbmalloc(device, 1);
+  if(!LogBlock) {
+    return 0;
+  }
+  /* Main volume descriptor */
+  MVDS_location = avdp.mvds.location;
+  MVDS_length = avdp.mvds.length;
+
+  part->valid = 0;
+  volvalid = 0;
+  part->VolumeDesc[ 0 ] = '\0';
+  i = 1;
+  do {
+    /* Find Volume Descriptor */
+    lbnum = MVDS_location;
+    do {
+
+      if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) {
+        TagID = 0;
+      } else {
+        UDFDescriptor( LogBlock, &TagID );
+      }
+
+      if( ( TagID == 5 ) && ( !part->valid ) ) {
+        /* Partition Descriptor */
+        UDFPartition( LogBlock, &part->Flags, &part->Number,
+                      part->Contents, &part->Start, &part->Length );
+        part->valid = ( partnum == part->Number );
+      } else if( ( TagID == 6 ) && ( !volvalid ) ) {
+        /* Logical Volume Descriptor */
+        if( UDFLogVolume( LogBlock, part->VolumeDesc ) ) {  
+          /* TODO: sector size wrong! */
+        } else {
+          volvalid = 1;
+        }
+      }
+
+    } while( ( lbnum <= MVDS_location + ( MVDS_length - 1 )
+               / DVD_VIDEO_LB_LEN ) && ( TagID != 8 )
+             && ( ( !part->valid ) || ( !volvalid ) ) );
+
+    if( ( !part->valid) || ( !volvalid ) ) {
+      /* Backup volume descriptor */
+      MVDS_location = avdp.mvds.location;
+      MVDS_length = avdp.mvds.length;
+    }
+  } while( i-- && ( ( !part->valid ) || ( !volvalid ) ) );
+
+  dvdalign_lbfree(device, LogBlock);
+  /* We only care for the partition, not the volume */
+  return part->valid;
+}
+
+uint32_t UDFFindFile( dvd_reader_t *device, char *filename,
+                      uint32_t *filesize )
+{
+  uint8_t *LogBlock;
+  uint32_t lbnum;
+  uint16_t TagID;
+  struct Partition partition;
+  struct AD RootICB, File, ICB;
+  char tokenline[ MAX_UDF_FILE_NAME_LEN ];
+  char *token;
+  uint8_t filetype;
+  
+  if(filesize) {
+    *filesize = 0;
+  }
+  tokenline[0] = '\0';
+  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);
+    
+    LogBlock = dvdalign_lbmalloc(device, 1);
+    if(!LogBlock) {
+      return 0;
+    }
+    /* Find root dir ICB */
+    lbnum = partition.Start;
+    do {
+      if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) {
+        TagID = 0;
+      } else {
+        UDFDescriptor( LogBlock, &TagID );
+      }
+
+      /* File Set Descriptor */
+      if( TagID == 256 ) {  // File Set Descriptor
+        UDFLongAD( &LogBlock[ 400 ], &RootICB );
+      }
+    } while( ( lbnum < partition.Start + partition.Length )
+             && ( TagID != 8 ) && ( TagID != 256 ) );
+
+    dvdalign_lbfree(device, LogBlock);
+      
+    /* 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,
+                       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, "/" );
+    }
+  } 
+
+  /* Sanity check. */
+  if( File.Partition != 0 ) {
+    return 0;
+  }
+
+  if(filesize) {
+    *filesize = File.Length;
+  }
+  /* Hack to not return partition.Start for empty files. */
+  if( !File.Location ) {
+    return 0;
+  } else {
+    return partition.Start + File.Location;
+  }
+}
+
+
+
+/**
+ * Gets a Descriptor .
+ * Returns 1 if descriptor found, 0 on error.
+ * id, tagid of descriptor
+ * bufsize, size of BlockBuf (must be >= DVD_VIDEO_LB_LEN)
+ * and aligned for raw/O_DIRECT read.
+ */
+static int UDFGetDescriptor( dvd_reader_t *device, int id,
+                             uint8_t *descriptor, int bufsize) 
+{
+  uint32_t lbnum, MVDS_location, MVDS_length;
+  struct avdp_t avdp;
+  uint16_t TagID;
+  uint32_t lastsector;
+  int i, terminate;
+  int desc_found = 0;
+  /* Find Anchor */
+  lastsector = 0;
+  lbnum = 256;   /* Try #1, prime anchor */
+  terminate = 0;
+  if(bufsize < DVD_VIDEO_LB_LEN) {
+    return 0;
+  }
+  
+  if(!UDFGetAVDP(device, &avdp)) {
+    return 0;
+  }
+
+  /* Main volume descriptor */
+  MVDS_location = avdp.mvds.location;
+  MVDS_length = avdp.mvds.length;
+  
+  i = 1;
+  do {
+    /* Find  Descriptor */
+    lbnum = MVDS_location;
+    do {
+      
+      if( DVDReadLBUDF( device, lbnum++, 1, descriptor, 0 ) <= 0 ) {
+        TagID = 0;
+      } else {
+        UDFDescriptor( descriptor, &TagID );
+      }
+      
+      if( (TagID == id) && ( !desc_found ) ) {
+        /* Descriptor */
+        desc_found = 1;
+      }
+    } while( ( lbnum <= MVDS_location + ( MVDS_length - 1 )
+               / DVD_VIDEO_LB_LEN ) && ( TagID != 8 )
+             && ( !desc_found) );
+    
+    if( !desc_found ) {
+      /* Backup volume descriptor */
+      MVDS_location = avdp.rvds.location;
+      MVDS_length = avdp.rvds.length;
+    }
+  } while( i-- && ( !desc_found )  );
+
+  
+  return desc_found;
+}
+
+
+static int UDFGetPVD(dvd_reader_t *device, struct pvd_t *pvd)
+{
+  uint8_t *pvd_buf;
+  
+  if(GetUDFCache(device, PVDCache, 0, pvd)) {
+    return 1;
+  }
+  
+  pvd_buf = dvdalign_lbmalloc(device, 1);
+  if(!pvd_buf) {
+    return 0;
+  }
+  if(!UDFGetDescriptor( device, 1, pvd_buf, 1*DVD_VIDEO_LB_LEN)) {
+    dvdalign_lbfree(device, pvd_buf);
+    return 0;
+  }
+  
+  memcpy(pvd->VolumeIdentifier, &pvd_buf[24], 32);
+  memcpy(pvd->VolumeSetIdentifier, &pvd_buf[72], 128);
+  SetUDFCache(device, PVDCache, 0, pvd);
+  
+  dvdalign_lbfree(device, pvd_buf);
+
+  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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/dvd_udf.h	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,66 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+#ifndef DVD_UDF_H_INCLUDED
+#define DVD_UDF_H_INCLUDED
+
+/*
+ * This code is based on dvdudf by:
+ *   Christian Wolff <scarabaeus@convergence.de>.
+ *
+ * Modifications by:
+ *   Billy Biggs <vektor@dumbterm.net>.
+ *   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
+ * project's page is at http://linuxtv.org/dvd/
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  Or, point your browser to
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#if defined(HAVE_INTTYPES_H)
+#include <inttypes.h>
+#elif defined(HAVE_STDINT_H)
+#include <stdint.h>
+#endif
+
+#include "dvd_reader.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Looks for a file on the UDF disc/imagefile and returns the block number
+ * where it begins, or 0 if it is not found.  The filename should be an
+ * absolute pathname on the UDF filesystem, starting with '/'.  For example,
+ * '/VIDEO_TS/VTS_01_1.IFO'.  On success, filesize will be set to the size of
+ * the file in bytes.
+ * This implementation relies on that the file size is less than 2^32
+ * A DVD file can at most be 2^30 (-2048 ?).
+ */
+uint32_t UDFFindFile( dvd_reader_t *device, char *filename, uint32_t *size );
+  
+void FreeUDFCache(dvd_reader_t *device, 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
+#endif /* DVD_UDF_H_INCLUDED */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/dvdread_internal.h	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,13 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+#ifndef DVDREAD_INTERNAL_H
+#define DVDREAD_INTERNAL_H
+
+
+#define CHECK_VALUE(arg)
+
+
+int get_verbose(void);
+int dvdread_verbose(dvd_reader_t *dvd);
+dvd_reader_t *device_of_file(dvd_file_t *file);
+
+#endif /* DVDREAD_INTERNAL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/ifo_print.c	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,1190 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/* 
+ * Copyright (C) 2000, 2001, 2002, 2003
+ *               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 changelog at http://svn.mplayerhq.hu/mplayer/trunk/
+ * $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
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#if defined(HAVE_INTTYPES_H)
+#include <inttypes.h>
+#elif defined(HAVE_STDINT_H)
+#include <stdint.h>
+#endif
+
+#include "ifo_types.h"
+#include "ifo_read.h"
+#include "ifo_print.h"
+#include "cmd_print.h"
+#include "dvdread_internal.h"
+
+/* Put this in some other file / package?  It's used in nav_print too. */
+static void ifoPrint_time(dvd_time_t *dtime) {
+  const char *rate;
+  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,
+         dtime->minute,
+         dtime->second,
+         dtime->frame_u & 0x3f);
+  switch((dtime->frame_u & 0xc0) >> 6) {
+  case 1:
+    rate = "25.00";
+    break;
+  case 3:
+    rate = "29.97";
+    break;
+  default:
+    if(dtime->hour == 0 && dtime->minute == 0 
+       && dtime->second == 0 && dtime->frame_u == 0)
+      rate = "no";
+    else
+      rate = "(please send a bug report)";
+    break;
+  } 
+  printf(" @ %s fps", rate);
+}
+
+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)) */
+  if(attr->mpeg_version == 0 
+     && attr->video_format == 0 
+     && attr->display_aspect_ratio == 0 
+     && attr->permitted_df == 0 
+     && attr->unknown1 == 0 
+     && attr->line21_cc_1 == 0 
+     && attr->line21_cc_2 == 0 
+     && attr->bit_rate == 0 
+     && attr->video_format == 0 
+     && attr->letterboxed == 0 
+     && attr->film_mode == 0) {
+    printf("-- Unspecified --");
+    return;
+  }
+  
+  switch(attr->mpeg_version) {
+  case 0:
+    printf("mpeg1 ");
+    break;
+  case 1:
+    printf("mpeg2 ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+  
+  switch(attr->video_format) {
+  case 0:
+    printf("ntsc ");
+    break;
+  case 1:
+    printf("pal ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+  
+  switch(attr->display_aspect_ratio) {
+  case 0:
+    printf("4:3 ");
+    break;
+  case 3:
+    printf("16:9 ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+  
+  // Wide is allways allowed..!!!
+  switch(attr->permitted_df) {
+  case 0:
+    printf("pan&scan+letterboxed ");
+    break;
+  case 1:
+    printf("only pan&scan "); //??
+    break;
+  case 2:
+    printf("only letterboxed ");
+    break;
+  case 3:
+    // not specified
+    break;
+  default:
+    printf("(please send a bug report)");
+  }
+  
+  printf("U%x ", attr->unknown1);
+  CHECK_VALUE(!attr->unknown1);
+  
+  if(attr->line21_cc_1 || attr->line21_cc_2) {
+    printf("NTSC CC ");
+    if(attr->line21_cc_1)
+      printf("1 ");
+    if(attr->line21_cc_2)
+      printf("2 ");
+  }
+
+  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;
+    if(attr->video_format != 0) 
+      height = 576;
+    switch(attr->picture_size) {
+    case 0:
+      printf("720x%d ", height);
+      break;
+    case 1:
+      printf("704x%d ", height);
+      break;
+    case 2:
+      printf("352x%d ", height);
+      break;
+    case 3:
+      printf("352x%d ", height/2);
+      break;      
+    default:
+      printf("(please send a bug report) ");
+    }
+  }
+
+  if(attr->letterboxed) {
+    printf("source letterboxed ");
+  }
+  
+  if(attr->film_mode) {
+    printf("film");
+  } else {
+    printf("video"); //camera
+  }
+}
+
+static void ifoPrint_audio_attributes(audio_attr_t *attr) {
+  
+  if(attr->audio_format == 0
+     && attr->multichannel_extension == 0
+     && attr->lang_type == 0
+     && attr->application_mode == 0
+     && attr->quantization == 0
+     && attr->sample_frequency == 0
+     && attr->channels == 0
+     && attr->lang_code == 0
+     && attr->lang_extension == 0
+     && attr->code_extension == 0
+     && attr->unknown3 == 0
+     && attr->unknown1 == 0) {
+    printf("-- Unspecified --");
+    return;
+  }
+  
+  switch(attr->audio_format) {
+  case 0:
+    printf("ac3 ");
+    break;
+  case 1:
+    printf("(please send a bug report) ");
+    break;
+  case 2:
+    printf("mpeg1 ");
+    break;
+  case 3:
+    printf("mpeg2ext ");
+    break;
+  case 4:
+    printf("lpcm ");
+    break;
+  case 5:
+    printf("(please send a bug report) ");
+    break;
+  case 6:
+    printf("dts ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+  
+  if(attr->multichannel_extension)
+    printf("multichannel_extension ");
+  
+  switch(attr->lang_type) {
+  case 0:
+    // not specified
+    CHECK_VALUE(attr->lang_code == 0 || attr->lang_code == 0xffff);
+    break;
+  case 1:
+    printf("%c%c (%c) ", attr->lang_code>>8, attr->lang_code & 0xff,
+           attr->lang_extension ? attr->lang_extension : ' ');
+    if(attr->lang_extension) {
+      printf("(please send a bug report) lang_extension != 0");
+    }
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+
+  switch(attr->application_mode) {
+  case 0:
+    // not specified
+    break;
+  case 1:
+    printf("karaoke mode ");
+    break;
+  case 2:
+    printf("surround sound mode ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+  
+  switch(attr->audio_format) {
+  case 0: //ac3
+    if(attr->quantization != 3) {
+      printf("(please send a bug report) ac3 quant/drc not 3 (%d)",
+             attr->quantization);
+    }
+    break;
+  case 2: //mpeg 1 or mpeg 2 without extension stream
+  case 3: //mpeg 2 with extension stream
+    switch(attr->quantization) {
+    case 0: //no drc
+      printf("no drc ");
+      break;
+    case 1:
+      printf("drc ");
+      break;
+    default:
+      printf("(please send a bug report) mpeg reserved quant/drc  (%d)",
+             attr->quantization);
+      break;
+    }
+    break;
+  case 4:
+    switch(attr->quantization) {
+    case 0:
+      printf("16bit ");
+      break;
+    case 1:
+      printf("20bit ");
+      break;
+    case 2:
+      printf("24bit ");
+      break;
+    case 3:
+      printf("(please send a bug report) lpcm reserved quant/drc  (%d)",
+             attr->quantization);
+      break;
+    }
+    break;
+  case 6: //dts
+    if(attr->quantization != 3) {
+      printf("(please send a bug report) dts quant/drc not 3 (%d)",
+             attr->quantization);
+    }
+    break;
+  default:
+    break;
+  }
+
+  switch(attr->sample_frequency) {
+  case 0:
+    printf("48kHz ");
+    break;
+  case 1:
+    printf("96kHz ");
+    break;
+  default:
+    printf("sample_frequency %i (please send a bug report) ", 
+           attr->sample_frequency);
+  }
+  
+  printf("%dCh ", attr->channels + 1);
+  
+  switch(attr->code_extension) {
+  case 0:
+    printf("Not specified ");
+    break;
+  case 1: // Normal audio
+    printf("Normal Caption ");
+    break;
+  case 2: // visually imparied
+    printf("Audio for visually impaired ");
+    break;
+  case 3: // Directors 1
+    printf("Director's comments 1 ");
+    break;
+  case 4: // Directors 2
+    printf("Director's comments 2 ");
+    break;
+    //case 4: // Music score ?    
+  default:
+    printf("(please send a bug report) ");
+  }
+    
+  printf("%d ", attr->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(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->code_extension == 0) {
+    printf("-- Unspecified --");
+    return;
+  }
+  
+  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("lang not specified ");
+  }
+  
+  printf("%d ", attr->zero1);
+  printf("%d ", attr->zero2);
+  printf("%d ", attr->lang_extension);
+  
+  switch(attr->code_extension) {
+  case 0:
+    printf("Not specified ");
+    break;
+  case 1:
+    printf("Caption with normal size character ");
+    break;
+  case 2:
+    printf("Caption with bigger size character ");
+    break;
+  case 3: 
+    printf("Caption for children ");
+    break;
+  case 4:
+    printf("reserved ");
+    break;
+  case 5:
+    printf("Closed Caption with normal size character ");
+    break;
+  case 6:
+    printf("Closed Caption with bigger size character ");
+    break;
+  case 7:
+    printf("Closed Caption for children ");
+    break;
+  case 8:
+    printf("reserved ");
+    break;
+  case 9:
+    printf("Forced Caption");
+    break;
+  case 10:
+    printf("reserved ");
+    break;
+  case 11:
+    printf("reserved ");
+    break;
+  case 12:
+    printf("reserved ");
+    break;
+  case 13:
+    printf("Director's comments with normal size character ");
+    break;
+  case 14:
+    printf("Director's comments with bigger size character ");
+    break;
+  case 15:
+    printf("Director's comments for children ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+
+}
+
+
+static void ifoPrint_USER_OPS(user_ops_t *user_ops) {
+  uint32_t uops;
+  unsigned char *ptr = (unsigned char *)user_ops;
+  
+  uops  = (*ptr++ << 24);
+  uops |= (*ptr++ << 16);
+  uops |= (*ptr++ << 8);
+  uops |= (*ptr++);
+  
+  if(uops == 0) {
+    printf("None\n");
+  } else if(uops == 0x01ffffff) {
+    printf("All\n");
+  } else {
+    if(user_ops->title_or_time_play)
+      printf("Title or Time Play, ");
+    if(user_ops->chapter_search_or_play)
+      printf("Chapter Search or Play, ");
+    if(user_ops->title_play)
+      printf("Title Play, ");
+    if(user_ops->stop)
+      printf("Stop, ");
+    if(user_ops->go_up)
+      printf("Go Up, ");
+    if(user_ops->time_or_chapter_search)
+      printf("Time or Chapter Search, ");
+    if(user_ops->prev_or_top_pg_search)
+      printf("Prev or Top PG Search, ");
+    if(user_ops->next_pg_search)
+      printf("Next PG Search, ");
+    if(user_ops->forward_scan)
+      printf("Forward Scan, ");
+    if(user_ops->backward_scan)
+      printf("Backward Scan, ");
+    if(user_ops->title_menu_call)
+      printf("Title Menu Call, ");
+    if(user_ops->root_menu_call)
+      printf("Root Menu Call, ");
+    if(user_ops->subpic_menu_call)
+      printf("SubPic Menu Call, ");
+    if(user_ops->audio_menu_call)
+      printf("Audio Menu Call, ");
+    if(user_ops->angle_menu_call)
+      printf("Angle Menu Call, ");
+    if(user_ops->chapter_menu_call)
+      printf("Chapter Menu Call, ");
+    if(user_ops->resume)
+      printf("Resume, ");
+    if(user_ops->button_select_or_activate)
+      printf("Button Select or Activate, ");
+    if(user_ops->still_off)
+      printf("Still Off, ");
+    if(user_ops->pause_on)
+      printf("Pause On, ");
+    if(user_ops->audio_stream_change)
+      printf("Audio Stream Change, ");
+    if(user_ops->subpic_stream_change)
+      printf("SubPic Stream Change, ");
+    if(user_ops->angle_change)
+      printf("Angle Change, ");
+    if(user_ops->karaoke_audio_pres_mode_change)
+      printf("Karaoke Audio Pres Mode Change, ");
+    if(user_ops->video_pres_mode_change)
+      printf("Video Pres Mode Change, ");
+    printf("\n");
+  }
+}
+
+
+void ifoPrint_VMGI_MAT(vmgi_mat_t *vmgi_mat) {
+  
+  printf("VMG Identifier: %.12s\n", vmgi_mat->vmg_identifier);
+  printf("Last Sector of VMG: %08x\n", vmgi_mat->vmg_last_sector);
+  printf("Last Sector of VMGI: %08x\n", vmgi_mat->vmgi_last_sector);
+  printf("Specification version number: %01x.%01x\n", 
+         vmgi_mat->specification_version >> 4, 
+         vmgi_mat->specification_version & 0xf);
+  /* Byte 2 of 'VMG Category' (00xx0000) is the Region Code */
+  printf("VMG Category: %08x\n", vmgi_mat->vmg_category);
+  printf("VMG Number of Volumes: %i\n", vmgi_mat->vmg_nr_of_volumes);
+  printf("VMG This Volume: %i\n", vmgi_mat->vmg_this_volume_nr);
+  printf("Disc side %i\n", vmgi_mat->disc_side);
+  printf("VMG Number of Title Sets %i\n", vmgi_mat->vmg_nr_of_title_sets);
+  printf("Provider ID: %.32s\n", vmgi_mat->provider_identifier);
+  printf("VMG POS Code: %08x", (uint32_t)(vmgi_mat->vmg_pos_code >> 32));
+  printf("%08x\n", (uint32_t)vmgi_mat->vmg_pos_code);
+  printf("End byte of VMGI_MAT: %08x\n", vmgi_mat->vmgi_last_byte);
+  printf("Start byte of First Play PGC FP PGC: %08x\n", 
+         vmgi_mat->first_play_pgc);
+  printf("Start sector of VMGM_VOBS: %08x\n", vmgi_mat->vmgm_vobs);
+  printf("Start sector of TT_SRPT: %08x\n", vmgi_mat->tt_srpt);
+  printf("Start sector of VMGM_PGCI_UT: %08x\n", vmgi_mat->vmgm_pgci_ut);
+  printf("Start sector of PTL_MAIT: %08x\n", vmgi_mat->ptl_mait);
+  printf("Start sector of VTS_ATRT: %08x\n", vmgi_mat->vts_atrt);
+  printf("Start sector of TXTDT_MG: %08x\n", vmgi_mat->txtdt_mgi);
+  printf("Start sector of VMGM_C_ADT: %08x\n", vmgi_mat->vmgm_c_adt);
+  printf("Start sector of VMGM_VOBU_ADMAP: %08x\n", 
+         vmgi_mat->vmgm_vobu_admap);
+  printf("Video attributes of VMGM_VOBS: ");
+  ifoPrint_video_attributes(&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(&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(&vmgi_mat->vmgm_subp_attr);
+    printf("\n");
+  }
+}
+
+
+void ifoPrint_VTSI_MAT(vtsi_mat_t *vtsi_mat) {
+  int i;
+
+  printf("VTS Identifier: %.12s\n", vtsi_mat->vts_identifier);
+  printf("Last Sector of VTS: %08x\n", vtsi_mat->vts_last_sector);
+  printf("Last Sector of VTSI: %08x\n", vtsi_mat->vtsi_last_sector);
+  printf("Specification version number: %01x.%01x\n", 
+         vtsi_mat->specification_version>>4, 
+         vtsi_mat->specification_version&0xf);
+  printf("VTS Category: %08x\n", vtsi_mat->vts_category);
+  printf("End byte of VTSI_MAT: %08x\n", vtsi_mat->vtsi_last_byte);
+  printf("Start sector of VTSM_VOBS:  %08x\n", vtsi_mat->vtsm_vobs);
+  printf("Start sector of VTSTT_VOBS: %08x\n", vtsi_mat->vtstt_vobs);
+  printf("Start sector of VTS_PTT_SRPT: %08x\n", vtsi_mat->vts_ptt_srpt);
+  printf("Start sector of VTS_PGCIT:    %08x\n", vtsi_mat->vts_pgcit);
+  printf("Start sector of VTSM_PGCI_UT: %08x\n", vtsi_mat->vtsm_pgci_ut);
+  printf("Start sector of VTS_TMAPT:    %08x\n", vtsi_mat->vts_tmapt);
+  printf("Start sector of VTSM_C_ADT:      %08x\n", vtsi_mat->vtsm_c_adt);
+  printf("Start sector of VTSM_VOBU_ADMAP: %08x\n",vtsi_mat->vtsm_vobu_admap);
+  printf("Start sector of VTS_C_ADT:       %08x\n", vtsi_mat->vts_c_adt);
+  printf("Start sector of VTS_VOBU_ADMAP:  %08x\n", vtsi_mat->vts_vobu_admap);
+
+  printf("Video attributes of VTSM_VOBS: ");
+  ifoPrint_video_attributes(&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(&vtsi_mat->vtsm_audio_attr);
+    printf("\n");
+  }
+  
+  printf("VTSM Number of Sub-picture attributes: %i\n", 
+         vtsi_mat->nr_of_vtsm_subp_streams);
+  if(vtsi_mat->nr_of_vtsm_subp_streams > 0) {
+    printf("\tstream %2i status: ", 1);
+    ifoPrint_subp_attributes(&vtsi_mat->vtsm_subp_attr);
+    printf("\n");
+  }
+  
+  printf("Video attributes of VTS_VOBS: ");
+  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(&vtsi_mat->vts_audio_attr[i]);
+    printf("\n");
+  }
+  
+  printf("VTS Number of Subpicture attributes: %i\n", 
+         vtsi_mat->nr_of_vts_subp_streams);
+  for(i = 0; i < vtsi_mat->nr_of_vts_subp_streams; i++) {
+    printf("\tstream %2i status: ", i);
+    ifoPrint_subp_attributes(&vtsi_mat->vts_subp_attr[i]);
+    printf("\n");
+  }
+  
+  /* FIXME:  Add printing of MultiChannel Extension */
+}
+
+
+static void ifoPrint_PGC_COMMAND_TBL(pgc_command_tbl_t *cmd_tbl) {
+  int i;
+  
+  if(cmd_tbl == NULL) {
+    printf("No Command table present\n");
+    return;
+  }
+  
+  printf("Number of Pre commands: %i\n", cmd_tbl->nr_of_pre);
+  for(i = 0; i < cmd_tbl->nr_of_pre; i++) {
+    cmdPrint_CMD(i, &cmd_tbl->pre_cmds[i]);
+  }
+
+  printf("Number of Post commands: %i\n", cmd_tbl->nr_of_post);
+  for(i = 0; i < cmd_tbl->nr_of_post; i++) {
+    cmdPrint_CMD(i, &cmd_tbl->post_cmds[i]);
+  }
+
+  printf("Number of Cell commands: %i\n", cmd_tbl->nr_of_cell);
+  for(i = 0; i < cmd_tbl->nr_of_cell; i++) {
+    cmdPrint_CMD(i, &cmd_tbl->cell_cmds[i]);
+  }
+}
+
+
+static void ifoPrint_PGC_PROGRAM_MAP(pgc_program_map_t *program_map, int nr) {
+  int i;
+  
+  if(program_map == NULL) {
+    printf("No Program map present\n");
+    return;
+  }
+  
+  for(i = 0; i < nr; i++) {
+    printf("Program %3i Entry Cell: %3i\n", i + 1, program_map[i]);
+  }
+}
+
+
+static void ifoPrint_CELL_PLAYBACK(cell_playback_t *cell_playback, int nr) {
+  int i;
+  
+  if(cell_playback == NULL) {
+    printf("No Cell Playback info present\n");
+    return;
+  }
+  
+  for(i=0;i<nr;i++) {
+    printf("Cell: %3i ", i + 1);
+
+    ifoPrint_time(&cell_playback[i].playback_time);
+    printf("\t");
+
+    if(cell_playback[i].block_mode || cell_playback[i].block_type) {
+      const char *s;
+      switch(cell_playback[i].block_mode) {
+      case 0:
+        s = "not a"; break;
+      case 1:
+        s = "the first"; break;
+      case 2:
+      default:
+        s = ""; break;
+      case 3:
+        s = "last"; break;
+      }
+      printf("%s cell in the block ", s);
+      
+      switch(cell_playback[i].block_type) {
+      case 0:
+        printf("not part of the block ");
+        break;
+      case 1:
+        printf("angle block ");
+        break;
+      case 2:
+      case 3:
+        printf("(send bug repport) ");
+        break;
+      }
+    }
+    if(cell_playback[i].seamless_play)
+      printf("presented seamlessly ");
+    if(cell_playback[i].interleaved)
+      printf("cell is interleaved ");
+    if(cell_playback[i].stc_discontinuity)
+      printf("STC_discontinuty ");
+    if(cell_playback[i].seamless_angle)
+      printf("only seamless angle ");
+    if(cell_playback[i].restricted)
+      printf("restricted cell ");
+    
+    if(cell_playback[i].still_time)
+      printf("still time %d ", cell_playback[i].still_time);
+    if(cell_playback[i].cell_cmd_nr)
+      printf("cell command %d", cell_playback[i].cell_cmd_nr);
+    
+    printf("\n\tStart sector: %08x\tFirst ILVU end  sector: %08x\n", 
+           cell_playback[i].first_sector, 
+           cell_playback[i].first_ilvu_end_sector);
+    printf("\tEnd   sector: %08x\tLast VOBU start sector: %08x\n", 
+           cell_playback[i].last_sector, 
+           cell_playback[i].last_vobu_start_sector);
+  }
+}
+
+static void ifoPrint_CELL_POSITION(cell_position_t *cell_position, int nr) {
+  int i;
+  
+  if(cell_position == NULL) {
+    printf("No Cell Position info present\n");
+    return;
+  }
+  
+  for(i=0;i<nr;i++) {
+    printf("Cell: %3i has VOB ID: %3i, Cell ID: %3i\n", i + 1, 
+           cell_position[i].vob_id_nr, cell_position[i].cell_nr);
+  }
+}
+
+
+void ifoPrint_PGC(pgc_t *pgc) {
+  int i;
+  
+  if(pgc == NULL) {
+    printf("Error: No PGC present\n");
+    return;
+  }
+
+  printf("Number of Programs: %i\n", pgc->nr_of_programs);
+  printf("Number of Cells: %i\n", pgc->nr_of_cells);
+  /* Check that time is 0:0:0:0 also if nr_of_programs==0 */
+  printf("Playback time: ");
+  ifoPrint_time(&pgc->playback_time); printf("\n");
+
+  /* If no programs/no time then does this mean anything? */
+  printf("Prohibited user operations: ");
+  ifoPrint_USER_OPS(&pgc->prohibited_ops);
+  
+  for(i = 0; i < 8; i++) {
+    if(pgc->audio_control[i] & 0x8000) { /* The 'is present' bit */
+      printf("Audio stream %i control: %04x\n", 
+             i, pgc->audio_control[i]);
+    }
+  }
+  
+  for(i = 0; i < 32; i++) {
+    if(pgc->subp_control[i] & 0x80000000) { /* The 'is present' bit */
+      printf("Subpicture stream %2i control: %08x\n", 
+             i, pgc->subp_control[i]);
+    }
+  }
+  
+  printf("Next PGC number: %i\n", pgc->next_pgc_nr);
+  printf("Prev PGC number: %i\n", pgc->prev_pgc_nr);
+  printf("GoUp PGC number: %i\n", pgc->goup_pgc_nr);
+  if(pgc->nr_of_programs != 0) {
+    printf("Still time: %i seconds (255=inf)\n", pgc->still_time);
+    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) {
+    for(i = 0; i < 16; i++) {
+      printf("Color %2i: %08x\n", i, pgc->palette[i]);
+    }
+  }
+  
+  /* Memmory offsets to div. tables. */
+  ifoPrint_PGC_COMMAND_TBL(pgc->command_tbl);
+  ifoPrint_PGC_PROGRAM_MAP(pgc->program_map, pgc->nr_of_programs);
+  ifoPrint_CELL_PLAYBACK(pgc->cell_playback, pgc->nr_of_cells);
+  ifoPrint_CELL_POSITION(pgc->cell_position, pgc->nr_of_cells);
+}
+
+
+void ifoPrint_TT_SRPT(tt_srpt_t *tt_srpt) {
+  int i;
+  
+  printf("Number of TitleTrack search pointers: %i\n",
+         tt_srpt->nr_of_srpts);
+  for(i=0;i<tt_srpt->nr_of_srpts;i++) {
+    printf("Title Track index %i\n", i + 1);
+    printf("\tTitle set number (VTS): %i", 
+           tt_srpt->title[i].title_set_nr);
+    printf("\tVTS_TTN: %i\n", tt_srpt->title[i].vts_ttn);
+    printf("\tNumber of PTTs: %i\n", tt_srpt->title[i].nr_of_ptts);
+    printf("\tNumber of angles: %i\n", 
+           tt_srpt->title[i].nr_of_angles);
+    printf("\tTitle playback type: %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", 
+           tt_srpt->title[i].title_set_sector);
+  }
+}
+
+
+void ifoPrint_VTS_PTT_SRPT(vts_ptt_srpt_t *vts_ptt_srpt) {
+  int i, j;
+  printf(" nr_of_srpts %i last byte %i\n", 
+         vts_ptt_srpt->nr_of_srpts, 
+         vts_ptt_srpt->last_byte);
+  for(i=0;i<vts_ptt_srpt->nr_of_srpts;i++) {
+    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, 
+             vts_ptt_srpt->title[i].ptt[j].pgcn,
+             vts_ptt_srpt->title[i].ptt[j].pgn );
+    }
+  }
+}
+
+
+void ifoPrint_PTL_MAIT(ptl_mait_t *ptl_mait) {
+  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);
+  
+  for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+    
+    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);
+    
+    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;
+  
+  printf("Number of VOBs in this VOBS: %i\n", c_adt->nr_of_vobs);
+  //entries = c_adt->nr_of_vobs;
+  entries = (c_adt->last_byte + 1 - C_ADT_SIZE)/sizeof(cell_adr_t);
+  
+  for(i = 0; i < entries; i++) {
+    printf("VOB ID: %3i, Cell ID: %3i   ", 
+           c_adt->cell_adr_table[i].vob_id, c_adt->cell_adr_table[i].cell_id);
+    printf("Sector (first): 0x%08x   (last): 0x%08x\n",
+           c_adt->cell_adr_table[i].start_sector, 
+           c_adt->cell_adr_table[i].last_sector);
+  }
+}
+
+
+void ifoPrint_VOBU_ADMAP(vobu_admap_t *vobu_admap) {
+  int i, entries;
+  
+  entries = (vobu_admap->last_byte + 1 - VOBU_ADMAP_SIZE)/4;
+  for(i = 0; i < entries; i++) {
+    printf("VOBU %5i  First sector: 0x%08x\n", i + 1,
+           vobu_admap->vobu_start_sectors[i]);
+  }
+}
+
+
+void ifoPrint_PGCIT(pgcit_t *pgcit) {
+  int i;
+  
+  for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+    printf("\nProgram (PGC): %3i\t", i + 1);
+    printf("PGC Category: Entry id 0x%02x, ", pgcit->pgci_srp[i].entry_id);
+    printf("Parental ID mask 0x%04x\n", pgcit->pgci_srp[i].ptl_id_mask);
+    ifoPrint_PGC(pgcit->pgci_srp[i].pgc);
+  }
+}
+
+
+void ifoPrint_PGCI_UT(pgci_ut_t *pgci_ut) {
+  int i;
+  
+  printf("Number of Menu Language Units (PGCI_LU): %3i\n", pgci_ut->nr_of_lus);
+  for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+    printf("\nMenu Language Code: %c%c (%c)\n",
+           pgci_ut->lu[i].lang_code >> 8,
+           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);
+  }
+}
+
+
+static void ifoPrint_VTS_ATTRIBUTES(vts_attributes_t *vts_attributes) {
+  int i;
+  
+  printf("VTS_CAT Application type: %08x\n", vts_attributes->vts_cat);
+ 
+  printf("Video attributes of VTSM_VOBS: ");
+  ifoPrint_video_attributes(&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(&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(&vts_attributes->vtsm_subp_attr);
+    printf("\n");
+  }
+   
+  printf("Video attributes of VTSTT_VOBS: ");
+  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(&vts_attributes->vtstt_audio_attr[i]);
+    printf("\n");
+  }
+  
+  printf("Number of Subpicture streams: %i\n", 
+         vts_attributes->nr_of_vtstt_subp_streams);
+  for(i = 0; i < vts_attributes->nr_of_vtstt_subp_streams; i++) {
+    printf("\tstream %2i attributes: ", i);    
+    ifoPrint_subp_attributes(&vts_attributes->vtstt_subp_attr[i]);
+    printf("\n");
+  }
+}
+
+
+void ifoPrint_VTS_ATRT(vts_atrt_t *vts_atrt) {
+  int i;
+  
+  printf("Number of Video Title Sets: %3i\n", vts_atrt->nr_of_vtss);
+  for(i = 0; i < vts_atrt->nr_of_vtss; i++) {
+    printf("\nVideo Title Set %i\n", i + 1);
+    printf("  offset %d relative to VMG_VTS_ATRT\n", 
+           vts_atrt->vts_atrt_offsets[i]);
+    ifoPrint_VTS_ATTRIBUTES(&vts_atrt->vts[i]);
+  }
+}
+
+
+void ifoPrint(dvd_reader_t *dvd, int title) {
+  ifo_handle_t *ifohandle;
+
+  ifohandle = ifoOpen(dvd, title);
+  if(!ifohandle) {
+    if(dvdread_verbose(dvd) >= 0) {
+      fprintf(stderr, "Can't open info file for title %d\n", title);
+    }
+    return;
+  }
+  
+  
+  if(ifohandle->vmgi_mat) {
+
+    printf("VMG top level\n-------------\n");
+    ifoPrint_VMGI_MAT(ifohandle->vmgi_mat);
+
+    printf("\nFirst Play PGC\n--------------\n");
+    if(ifohandle->first_play_pgc) {
+      ifoPrint_PGC(ifohandle->first_play_pgc);
+    } else {
+      printf("No First Play PGC present\n");
+    }
+
+    printf("\nTitle Track search pointer table\n");
+    printf(  "------------------------------------------------\n");
+    ifoPrint_TT_SRPT(ifohandle->tt_srpt);
+
+    printf("\nMenu PGCI Unit table\n");
+    printf(  "--------------------\n");
+    if(ifohandle->pgci_ut) {
+      ifoPrint_PGCI_UT(ifohandle->pgci_ut);
+    } else {
+      printf("No PGCI Unit table present\n");
+    }
+
+    printf("\nParental Manegment Information table\n");
+    printf(  "------------------------------------\n");
+    if(ifohandle->ptl_mait) {
+      ifoPrint_PTL_MAIT(ifohandle->ptl_mait);
+    } else {
+      printf("No Parental Management Information present\n");
+    }
+
+    printf("\nVideo Title Set Attribute Table\n");
+    printf(  "-------------------------------\n");
+    ifoPrint_VTS_ATRT(ifohandle->vts_atrt);
+    
+    printf("\nText Data Manager Information\n");
+    printf(  "-----------------------------\n");
+    if(ifohandle->txtdt_mgi) {
+      //ifoPrint_TXTDT_MGI(&(vmgi->txtdt_mgi));
+    } else {
+      printf("No Text Data Manager Information present\n");
+    }
+
+    printf("\nMenu Cell Adress table\n");
+    printf(  "-----------------\n");
+    if(ifohandle->menu_c_adt) {
+      ifoPrint_C_ADT(ifohandle->menu_c_adt);
+    } else {
+      printf("No Menu Cell Adress table present\n");
+    }
+
+    printf("\nVideo Manager Menu VOBU address map\n");
+    printf(  "-----------------\n");
+    if(ifohandle->menu_vobu_admap) {
+      ifoPrint_VOBU_ADMAP(ifohandle->menu_vobu_admap);
+    } else {
+      printf("No Menu VOBU address map present\n");   
+    }
+  }
+
+
+  if(ifohandle->vtsi_mat) {
+
+    printf("VTS top level\n-------------\n");
+    ifoPrint_VTSI_MAT(ifohandle->vtsi_mat);
+
+    printf("\nPart of Title Track search pointer table\n");
+    printf(  "----------------------------------------------\n");
+    ifoPrint_VTS_PTT_SRPT(ifohandle->vts_ptt_srpt);
+
+    printf("\nPGCI Unit table\n");
+    printf(  "--------------------\n");
+    ifoPrint_PGCIT(ifohandle->vts_pgcit);
+
+    printf("\nMenu PGCI Unit table\n");
+    printf(  "--------------------\n");
+    if(ifohandle->pgci_ut) {
+      ifoPrint_PGCI_UT(ifohandle->pgci_ut);
+    } else {
+      printf("No Menu PGCI Unit table present\n");
+    }
+    
+    printf("\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");
+    if(ifohandle->menu_c_adt) {
+      ifoPrint_C_ADT(ifohandle->menu_c_adt);
+    } else {
+      printf("No Cell Adress table present\n");
+    }
+
+    printf("\nVideo Title Set Menu VOBU address map\n");
+    printf(  "-----------------\n");
+    if(ifohandle->menu_vobu_admap) {
+      ifoPrint_VOBU_ADMAP(ifohandle->menu_vobu_admap);
+    } else {
+      printf("No Menu VOBU address map present\n");
+    }
+
+    printf("\nCell Adress table\n");
+    printf(  "-----------------\n");
+    ifoPrint_C_ADT(ifohandle->vts_c_adt);
+
+    printf("\nVideo Title Set VOBU address map\n");
+    printf(  "-----------------\n");
+    ifoPrint_VOBU_ADMAP(ifohandle->vts_vobu_admap);
+  } 
+
+  ifoClose(ifohandle);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/ifo_print.h	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,60 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+#ifndef IFO_PRINT_H_INCLUDED
+#define IFO_PRINT_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001 Björn Englund <d4bjorn@dtek.chalmers.se>,
+ *                          Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <libdvdread/ifo_types.h>
+#include <libdvdread/dvd_reader.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * This file provides example functions for printing information about the IFO
+ * file to stdout.
+ */
+
+/**
+ * Print the complete parsing information for the given file.
+ */
+
+/* ifoPrint(dvd, title); */
+void ifoPrint(dvd_reader_t *, int);
+
+void ifoPrint_VMGI_MAT(vmgi_mat_t *);
+void ifoPrint_VTSI_MAT(vtsi_mat_t *);
+
+void ifoPrint_PTL_MAIT(ptl_mait_t *);
+void ifoPrint_VTS_ATRT(vts_atrt_t *);
+void ifoPrint_TT_SRPT(tt_srpt_t *);
+void ifoPrint_VTS_PTT_SRPT(vts_ptt_srpt_t *);
+void ifoPrint_PGC(pgc_t *);
+void ifoPrint_PGCIT(pgcit_t *);
+void ifoPrint_PGCI_UT(pgci_ut_t *);
+void ifoPrint_VTS_TMAPT(vts_tmapt_t *);
+void ifoPrint_C_ADT(c_adt_t *);
+void ifoPrint_VOBU_ADMAP(vobu_admap_t *);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* IFO_PRINT_H_INCLUDED */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/ifo_read.c	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,2185 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2000, 2001, 2002, 2003
+ *               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 changelog at http://svn.mplayerhq.hu/mplayer/trunk/
+ * $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
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(HAVE_INTTYPES_H)
+#include <inttypes.h>
+#elif defined(HAVE_STDINT_H)
+#include <stdint.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+
+#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;                                                  \
+    fprintf(stderr, "*** Zero check failed in %s:%i\n    for %s = 0x",  \
+            __FILE__, __LINE__, # arg );                                \
+    for(i_CZ = 0; i_CZ < sizeof(arg); i_CZ++)                           \
+      fprintf(stderr, "%02x", *((uint8_t *)&arg + i_CZ));               \
+    fprintf(stderr, "\n");                                              \
+  }
+static const uint8_t my_friendly_zeros[2048];
+#else
+#define CHECK_ZERO0(arg) (void)(arg)
+#define CHECK_ZERO(arg) (void)(arg)
+#endif
+
+
+/* Prototypes for internal functions */
+static int ifoRead_VMG(ifo_handle_t *ifofile);
+static int ifoRead_VTS(ifo_handle_t *ifofile);
+static int ifoRead_PGC(ifo_handle_t *ifofile, pgc_t *pgc, unsigned int offset);
+static int ifoRead_PGC_COMMAND_TBL(ifo_handle_t *ifofile, 
+                                   pgc_command_tbl_t *cmd_tbl, 
+                                   unsigned int offset);
+static int ifoRead_PGC_PROGRAM_MAP(ifo_handle_t *ifofile, 
+                                   pgc_program_map_t *program_map, 
+                                   unsigned int nr, unsigned int offset);
+static int ifoRead_CELL_PLAYBACK_TBL(ifo_handle_t *ifofile, 
+                                     cell_playback_t *cell_playback, 
+                                     unsigned int nr, unsigned int offset);
+static int ifoRead_CELL_POSITION_TBL(ifo_handle_t *ifofile, 
+                                     cell_position_t *cell_position, 
+                                     unsigned int nr, unsigned int offset);
+static int ifoRead_VTS_ATTRIBUTES(ifo_handle_t *ifofile, 
+                                  vts_attributes_t *vts_attributes, 
+                                  unsigned int offset);
+static int ifoRead_C_ADT_internal(ifo_handle_t *ifofile, c_adt_t *c_adt, 
+                                  unsigned int sector);
+static int ifoRead_VOBU_ADMAP_internal(ifo_handle_t *ifofile, 
+                                       vobu_admap_t *vobu_admap, 
+                                       unsigned int sector);
+static int ifoRead_PGCIT_internal(ifo_handle_t *ifofile, pgcit_t *pgcit, 
+                                  unsigned int offset);
+
+static void ifoFree_PGC(pgc_t *pgc);
+static void ifoFree_PGC_COMMAND_TBL(pgc_command_tbl_t *cmd_tbl);
+static void ifoFree_PGCIT_internal(pgcit_t *pgcit);
+
+static ifo_handle_t *ifoOpen_File(ifo_handle_t *ifofile, int title, 
+                                  char *suffix);
+static ifo_handle_t *ifoOpenVMGI_File(ifo_handle_t *ifofile, char *suffix);
+static ifo_handle_t *ifoOpenVTSI_File(ifo_handle_t *ifofile, int title,
+                                      char *suffix);
+
+static inline int DVDFileSeek_( dvd_file_t *dvd_file, uint32_t offset ) {
+  return (DVDFileSeek(dvd_file, (int)offset) == (int)offset);
+}
+
+
+ifo_handle_t *ifoOpen(dvd_reader_t *dvd, int title) {
+  ifo_handle_t *ifofile;
+
+  ifofile = malloc(sizeof(ifo_handle_t));
+  if(!ifofile)
+    return NULL;
+
+  memset(ifofile, 0, sizeof(ifo_handle_t));
+
+  ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_FILE);
+  if(!ifoOpen_File(ifofile, title, "IFO")) {
+    if(title) {
+      if(dvdread_verbose(dvd) >= 1) {
+        fprintf(stderr, "libdvdread: Can't open file VTS_%02d_0.%s.\n", 
+                title, "IFO");
+      }
+    } else {
+      if(dvdread_verbose(dvd) >= 1) {
+        fprintf(stderr, "libdvdread: Can't open file VIDEO_TS.%s.\n", "IFO");
+      }
+    }
+    /* lower functions free the pointer, reallocate */
+    ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
+    if(!ifofile)
+      return NULL;
+
+    memset(ifofile, 0, sizeof(ifo_handle_t));
+
+    ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_BACKUP_FILE);
+    if(!ifoOpen_File(ifofile, title, "BUP")) {
+      if(title) {
+        if(dvdread_verbose(dvd) >= 1) {
+          fprintf(stderr, "libdvdread: Can't open file VTS_%02d_0.%s.\n", 
+                  title, "BUP");
+        }
+      } else {
+        if(dvdread_verbose(dvd) >= 1) {
+          fprintf(stderr, "libdvdread: Can't open file VIDEO_TS.%s.\n", "BUP");
+        }
+      }
+      return NULL;
+    }
+  }
+  return ifofile;
+}
+
+static ifo_handle_t *ifoOpen_File(ifo_handle_t *ifofile, int title, 
+                                  char *suffix) {
+  if(!ifofile->file) {
+    free(ifofile);
+    return NULL;
+  }
+
+  /* First check if this is a VMGI file. */
+  if(ifoRead_VMG(ifofile)) {
+
+    /* These are both mandatory. */
+    if(!ifoRead_FP_PGC(ifofile) || !ifoRead_TT_SRPT(ifofile)) {
+      if(dvdread_verbose(device_of_file(ifofile->file)) >= 0) {
+        fprintf(stderr, "libdvdread: Invalid main menu IFO (VIDEO_TS.%s).\n",
+                suffix);
+      }
+      ifoClose(ifofile);
+      return NULL;
+    }
+
+    ifoRead_PGCI_UT(ifofile);
+    ifoRead_PTL_MAIT(ifofile);
+
+    /* This is also mandatory. */
+    if(!ifoRead_VTS_ATRT(ifofile)) {
+      if(dvdread_verbose(device_of_file(ifofile->file)) >= 0) {
+        fprintf(stderr, "libdvdread: Invalid main menu IFO (VIDEO_TS.%s).\n",
+                suffix);
+      }
+      ifoClose(ifofile);
+      return NULL;
+    }
+
+    ifoRead_TXTDT_MGI(ifofile);
+    ifoRead_C_ADT(ifofile);
+    ifoRead_VOBU_ADMAP(ifofile);
+
+    return ifofile;
+  }
+
+  if(ifoRead_VTS(ifofile)) {
+
+    if(!ifoRead_VTS_PTT_SRPT(ifofile) || !ifoRead_PGCIT(ifofile)) {
+      if(dvdread_verbose(device_of_file(ifofile->file)) >= 0) {
+        fprintf(stderr, "libdvdread: Invalid title IFO (VTS_%02d_0.%s).\n",
+                title, suffix);
+      }
+      ifoClose(ifofile);
+      return NULL;
+    }
+
+    ifoRead_PGCI_UT(ifofile);
+    ifoRead_VTS_TMAPT(ifofile);
+    ifoRead_C_ADT(ifofile);
+    ifoRead_VOBU_ADMAP(ifofile);
+
+    if(!ifoRead_TITLE_C_ADT(ifofile) || !ifoRead_TITLE_VOBU_ADMAP(ifofile)) {
+      if(dvdread_verbose(device_of_file(ifofile->file)) >= 0) {
+        fprintf(stderr, "libdvdread: Invalid title IFO (VTS_%02d_0.%s).\n",
+                title, suffix);
+      }
+      ifoClose(ifofile);
+      return NULL;
+    }
+
+    return ifofile;
+  }
+
+  if(title) {
+    if(dvdread_verbose(device_of_file(ifofile->file)) >= 0) {
+      fprintf(stderr, "libdvdread: Invalid IFO for title %d (VTS_%02d_0.%s).\n",
+              title, title, suffix);
+    }
+  } else {
+    if(dvdread_verbose(device_of_file(ifofile->file)) >= 0) {
+      fprintf(stderr, "libdvdread: Invalid IFO for VMGM (VIDEO_TS.%s).\n", 
+              suffix);
+    }
+  }
+  ifoClose(ifofile);
+  return NULL;
+}
+
+
+ifo_handle_t *ifoOpenVMGI(dvd_reader_t *dvd) {
+  ifo_handle_t *ifofile;
+
+  ifofile = malloc(sizeof(ifo_handle_t));
+  if(!ifofile)
+    return NULL;
+
+  memset(ifofile, 0, sizeof(ifo_handle_t));
+
+  ifofile->file = DVDOpenFile(dvd, 0, DVD_READ_INFO_FILE);
+  if(!ifoOpenVMGI_File(ifofile, "IFO")) {
+    if(dvdread_verbose(dvd) >= 1) {
+      fprintf(stderr, "libdvdread: Can't open file VIDEO_TS.IFO: %s\n",
+              strerror(errno));
+    }
+
+    /* lower functions free the pointer, reallocate */
+    ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
+    if(!ifofile)
+      return NULL;
+
+    memset(ifofile, 0, sizeof(ifo_handle_t));
+
+    ifofile->file = DVDOpenFile(dvd, 0, DVD_READ_INFO_BACKUP_FILE);
+    if(!ifoOpenVMGI_File(ifofile, "BUP"))
+      if(dvdread_verbose(dvd) >= 1) {
+        fprintf(stderr, "libdvdread: Can't open file VIDEO_TS.BUP: %s\n",
+                strerror(errno));
+      }
+      return NULL;
+  }
+  return ifofile;
+}
+
+static ifo_handle_t *ifoOpenVMGI_File(ifo_handle_t *ifofile, char *suffix) {
+  if(!ifofile->file) {
+    free(ifofile);
+    return NULL;
+  }
+
+  if(ifoRead_VMG(ifofile))
+    return ifofile;
+
+  if(dvdread_verbose(device_of_file(ifofile->file)) >= 0) {
+    fprintf(stderr, "libdvdread: Invalid main menu IFO (VIDEO_TS.%s).\n", 
+            suffix);
+  }
+  ifoClose(ifofile);
+  return NULL;
+}
+
+
+ifo_handle_t *ifoOpenVTSI(dvd_reader_t *dvd, int title) {
+  ifo_handle_t *ifofile;
+  
+  ifofile = malloc(sizeof(ifo_handle_t));
+  if(!ifofile)
+    return NULL;
+
+  memset(ifofile, 0, sizeof(ifo_handle_t));
+  
+  if(title <= 0 || title > 99) {
+    if(dvdread_verbose(dvd) >= 0) {
+      fprintf(stderr, "libdvdread: ifoOpenVTSI invalid title (%d).\n", title);
+    }
+    free(ifofile);
+    errno = EINVAL;
+    return NULL;
+  }
+    
+  ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_FILE);
+  if(!ifoOpenVTSI_File(ifofile, title, "IFO")) {
+    if(dvdread_verbose(dvd) >= 1) {
+      fprintf(stderr, "libdvdread: Can't open file VTS_%02d_0.%s.\n", title, "IFO");
+    }
+    /* lower functions free the pointer, reallocate */
+    ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
+    if(!ifofile)
+      return NULL;
+
+    memset(ifofile, 0, sizeof(ifo_handle_t));
+
+    ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_BACKUP_FILE);
+    if(!ifoOpenVTSI_File(ifofile, title, "BUP"))
+      if(dvdread_verbose(dvd) >= 1) {
+        fprintf(stderr, "libdvdread: Can't open file VTS_%02d_0.%s.\n", title, "BUP");
+      }
+      return NULL;
+  }
+  return ifofile;
+}
+
+static ifo_handle_t *ifoOpenVTSI_File(ifo_handle_t* ifofile, int title, char *suffix) {
+  if(!ifofile->file) {
+    free(ifofile);
+    return NULL;
+  }
+
+  ifoRead_VTS(ifofile);
+  if(ifofile->vtsi_mat)
+    return ifofile;
+
+  if(dvdread_verbose(device_of_file(ifofile->file)) >= 0) {
+    fprintf(stderr, "libdvdread: Invalid IFO for title %d (VTS_%02d_0.%s).\n",
+            title, title, suffix);
+  }
+  ifoClose(ifofile);
+  return NULL;
+}
+
+
+void ifoClose(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  ifoFree_VOBU_ADMAP(ifofile);
+  ifoFree_TITLE_VOBU_ADMAP(ifofile);
+  ifoFree_C_ADT(ifofile);
+  ifoFree_TITLE_C_ADT(ifofile);
+  ifoFree_TXTDT_MGI(ifofile);
+  ifoFree_VTS_ATRT(ifofile);
+  ifoFree_PTL_MAIT(ifofile);
+  ifoFree_PGCI_UT(ifofile);
+  ifoFree_TT_SRPT(ifofile);
+  ifoFree_FP_PGC(ifofile);
+  ifoFree_PGCIT(ifofile);
+  ifoFree_VTS_PTT_SRPT(ifofile);
+  ifoFree_VTS_TMAPT(ifofile);
+
+  if(ifofile->vmgi_mat)
+    free(ifofile->vmgi_mat);
+
+  if(ifofile->vtsi_mat)
+    free(ifofile->vtsi_mat);
+
+  DVDCloseFile(ifofile->file);
+  ifofile->file = 0;
+  free(ifofile);
+  ifofile = 0;
+}
+
+
+static int ifoRead_VMG(ifo_handle_t *ifofile) {
+  vmgi_mat_t *vmgi_mat;
+
+  vmgi_mat = malloc(sizeof(vmgi_mat_t));
+  if(!vmgi_mat)
+    return 0;
+
+  ifofile->vmgi_mat = vmgi_mat;
+
+  if(!DVDFileSeek_(ifofile->file, 0)) {
+    free(ifofile->vmgi_mat);
+    ifofile->vmgi_mat = 0;
+    return 0;
+  }
+
+  if(!DVDReadBytes(ifofile->file, vmgi_mat, sizeof(vmgi_mat_t))) {
+    free(ifofile->vmgi_mat);
+    ifofile->vmgi_mat = 0;
+    return 0;
+  }
+
+  if(strncmp("DVDVIDEO-VMG", vmgi_mat->vmg_identifier, 12) != 0) {
+    free(ifofile->vmgi_mat);
+    ifofile->vmgi_mat = 0;
+    return 0;
+  }
+  
+  B2N_32(vmgi_mat->vmg_last_sector);
+  B2N_32(vmgi_mat->vmgi_last_sector);
+  B2N_32(vmgi_mat->vmg_category);
+  B2N_16(vmgi_mat->vmg_nr_of_volumes);
+  B2N_16(vmgi_mat->vmg_this_volume_nr);
+  B2N_16(vmgi_mat->vmg_nr_of_title_sets);
+  B2N_64(vmgi_mat->vmg_pos_code);
+  B2N_32(vmgi_mat->vmgi_last_byte);
+  B2N_32(vmgi_mat->first_play_pgc);
+  B2N_32(vmgi_mat->vmgm_vobs);
+  B2N_32(vmgi_mat->tt_srpt);
+  B2N_32(vmgi_mat->vmgm_pgci_ut);
+  B2N_32(vmgi_mat->ptl_mait);
+  B2N_32(vmgi_mat->vts_atrt);
+  B2N_32(vmgi_mat->txtdt_mgi);
+  B2N_32(vmgi_mat->vmgm_c_adt);
+  B2N_32(vmgi_mat->vmgm_vobu_admap);
+  B2N_16(vmgi_mat->vmgm_audio_attr.lang_code);
+  B2N_16(vmgi_mat->vmgm_subp_attr.lang_code);
+
+
+  CHECK_ZERO(vmgi_mat->zero_1);
+  CHECK_ZERO(vmgi_mat->zero_2);
+  CHECK_ZERO(vmgi_mat->zero_3);
+  CHECK_ZERO(vmgi_mat->zero_4);
+  CHECK_ZERO(vmgi_mat->zero_5);
+  CHECK_ZERO(vmgi_mat->zero_6);
+  CHECK_ZERO(vmgi_mat->zero_7);
+  CHECK_ZERO(vmgi_mat->zero_8);
+  CHECK_ZERO(vmgi_mat->zero_9);
+  CHECK_ZERO(vmgi_mat->zero_10);  
+  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 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));
+  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);
+
+  CHECK_VALUE(vmgi_mat->nr_of_vmgm_audio_streams <= 1);
+  CHECK_VALUE(vmgi_mat->nr_of_vmgm_subp_streams <= 1);
+
+  return 1;
+}
+
+
+static int ifoRead_VTS(ifo_handle_t *ifofile) {
+  vtsi_mat_t *vtsi_mat;
+  int i;
+
+  vtsi_mat = malloc(sizeof(vtsi_mat_t));
+  if(!vtsi_mat)
+    return 0;
+  
+  ifofile->vtsi_mat = vtsi_mat;
+
+  if(!DVDFileSeek_(ifofile->file, 0)) {
+    free(ifofile->vtsi_mat);
+    ifofile->vtsi_mat = 0;
+    return 0;
+  }
+
+  if(!(DVDReadBytes(ifofile->file, vtsi_mat, sizeof(vtsi_mat_t)))) {
+    free(ifofile->vtsi_mat);
+    ifofile->vtsi_mat = 0;
+    return 0;
+  }
+
+  if(strncmp("DVDVIDEO-VTS", vtsi_mat->vts_identifier, 12) != 0) {
+    free(ifofile->vtsi_mat);
+    ifofile->vtsi_mat = 0;
+    return 0;
+  }
+
+  B2N_32(vtsi_mat->vts_last_sector);
+  B2N_32(vtsi_mat->vtsi_last_sector);
+  B2N_32(vtsi_mat->vts_category);
+  B2N_32(vtsi_mat->vtsi_last_byte);
+  B2N_32(vtsi_mat->vtsm_vobs);
+  B2N_32(vtsi_mat->vtstt_vobs);
+  B2N_32(vtsi_mat->vts_ptt_srpt);
+  B2N_32(vtsi_mat->vts_pgcit);
+  B2N_32(vtsi_mat->vtsm_pgci_ut);
+  B2N_32(vtsi_mat->vts_tmapt);
+  B2N_32(vtsi_mat->vtsm_c_adt);
+  B2N_32(vtsi_mat->vtsm_vobu_admap);
+  B2N_32(vtsi_mat->vts_c_adt);
+  B2N_32(vtsi_mat->vts_vobu_admap);
+  B2N_16(vtsi_mat->vtsm_audio_attr.lang_code);
+  B2N_16(vtsi_mat->vtsm_subp_attr.lang_code);
+  for(i = 0; i < 8; i++)
+    B2N_16(vtsi_mat->vts_audio_attr[i].lang_code);
+  for(i = 0; i < 32; i++)
+    B2N_16(vtsi_mat->vts_subp_attr[i].lang_code);
+
+
+  CHECK_ZERO(vtsi_mat->zero_1);
+  CHECK_ZERO(vtsi_mat->zero_2);
+  CHECK_ZERO(vtsi_mat->zero_3);
+  CHECK_ZERO(vtsi_mat->zero_4);
+  CHECK_ZERO(vtsi_mat->zero_5);
+  CHECK_ZERO(vtsi_mat->zero_6);
+  CHECK_ZERO(vtsi_mat->zero_7);
+  CHECK_ZERO(vtsi_mat->zero_8);
+  CHECK_ZERO(vtsi_mat->zero_9);
+  CHECK_ZERO(vtsi_mat->zero_10);
+  CHECK_ZERO(vtsi_mat->zero_11);
+  CHECK_ZERO(vtsi_mat->zero_12);
+  CHECK_ZERO(vtsi_mat->zero_13);
+  CHECK_ZERO(vtsi_mat->zero_14);
+  CHECK_ZERO(vtsi_mat->zero_15);
+  CHECK_ZERO(vtsi_mat->zero_16);
+  CHECK_ZERO(vtsi_mat->zero_17);
+  CHECK_ZERO(vtsi_mat->zero_18);
+  CHECK_ZERO(vtsi_mat->zero_19);
+  CHECK_ZERO(vtsi_mat->zero_20);
+  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));
+  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));
+  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);
+  
+  CHECK_VALUE(vtsi_mat->nr_of_vtsm_audio_streams <= 1);
+  CHECK_VALUE(vtsi_mat->nr_of_vtsm_subp_streams <= 1);
+
+  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]);
+
+  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;
+}
+
+
+static int ifoRead_PGC_COMMAND_TBL(ifo_handle_t *ifofile, 
+                                   pgc_command_tbl_t *cmd_tbl, 
+                                   unsigned int offset) {
+  unsigned int total;
+
+  memset(cmd_tbl, 0, sizeof(pgc_command_tbl_t));
+
+  if(!DVDFileSeek_(ifofile->file, offset))
+    return 0;
+
+  if(!(DVDReadBytes(ifofile->file, cmd_tbl, PGC_COMMAND_TBL_SIZE)))
+    return 0;
+
+  B2N_16(cmd_tbl->nr_of_pre);
+  B2N_16(cmd_tbl->nr_of_post);
+  B2N_16(cmd_tbl->nr_of_cell);
+  B2N_16(cmd_tbl->last_byte);
+  
+  total = cmd_tbl->nr_of_pre + cmd_tbl->nr_of_post + cmd_tbl->nr_of_cell;
+  CHECK_VALUE(PGC_COMMAND_TBL_SIZE + total * COMMAND_DATA_SIZE 
+              <= cmd_tbl->last_byte + 1U);
+  CHECK_VALUE(total <= 255);
+
+  if(cmd_tbl->nr_of_pre != 0) {
+    unsigned int pre_cmds_size  = cmd_tbl->nr_of_pre * COMMAND_DATA_SIZE;
+    cmd_tbl->pre_cmds = malloc(pre_cmds_size);
+    if(!cmd_tbl->pre_cmds)
+      return 0;
+
+    if(!(DVDReadBytes(ifofile->file, cmd_tbl->pre_cmds, pre_cmds_size))) {
+      free(cmd_tbl->pre_cmds);
+      return 0;
+    }
+  }
+
+  if(cmd_tbl->nr_of_post != 0) {
+    unsigned int post_cmds_size = cmd_tbl->nr_of_post * COMMAND_DATA_SIZE;
+    cmd_tbl->post_cmds = malloc(post_cmds_size);
+    if(!cmd_tbl->post_cmds) {
+      if(cmd_tbl->pre_cmds) 
+        free(cmd_tbl->pre_cmds);
+      return 0;
+    }
+    if(!(DVDReadBytes(ifofile->file, cmd_tbl->post_cmds, post_cmds_size))) {
+      if(cmd_tbl->pre_cmds) 
+        free(cmd_tbl->pre_cmds);
+      free(cmd_tbl->post_cmds);
+      return 0;
+    }
+  }
+
+  if(cmd_tbl->nr_of_cell != 0) {
+    unsigned int cell_cmds_size = cmd_tbl->nr_of_cell * COMMAND_DATA_SIZE;
+    cmd_tbl->cell_cmds = malloc(cell_cmds_size);
+    if(!cmd_tbl->cell_cmds) {
+      if(cmd_tbl->pre_cmds)
+        free(cmd_tbl->pre_cmds);
+      if(cmd_tbl->post_cmds)
+        free(cmd_tbl->post_cmds);
+      return 0;
+    }
+    if(!(DVDReadBytes(ifofile->file, cmd_tbl->cell_cmds, cell_cmds_size))) {
+      if(cmd_tbl->pre_cmds) 
+        free(cmd_tbl->pre_cmds);
+      if(cmd_tbl->post_cmds) 
+        free(cmd_tbl->post_cmds);
+      free(cmd_tbl->cell_cmds);
+      return 0;
+    }
+  }
+  
+  /* 
+   * Make a run over all the commands and see that we can interpret them all?
+   */
+  return 1;
+}
+
+
+static void ifoFree_PGC_COMMAND_TBL(pgc_command_tbl_t *cmd_tbl) {
+  if(cmd_tbl) {
+    if(cmd_tbl->nr_of_pre && cmd_tbl->pre_cmds)
+      free(cmd_tbl->pre_cmds);
+    if(cmd_tbl->nr_of_post && cmd_tbl->post_cmds)
+      free(cmd_tbl->post_cmds);
+    if(cmd_tbl->nr_of_cell && cmd_tbl->cell_cmds)
+      free(cmd_tbl->cell_cmds);
+    free(cmd_tbl);
+  }
+}
+
+static int ifoRead_PGC_PROGRAM_MAP(ifo_handle_t *ifofile, 
+                                   pgc_program_map_t *program_map, 
+                                   unsigned int nr, unsigned int offset) {
+  unsigned int size = nr * sizeof(pgc_program_map_t);
+
+  if(!DVDFileSeek_(ifofile->file, offset))
+    return 0;
+ 
+  if(!(DVDReadBytes(ifofile->file, program_map, size)))
+    return 0;
+
+  return 1;
+}
+
+static int ifoRead_CELL_PLAYBACK_TBL(ifo_handle_t *ifofile, 
+                                     cell_playback_t *cell_playback,
+                                     unsigned int nr, unsigned int offset) {
+  unsigned int i;
+  unsigned int size = nr * sizeof(cell_playback_t);
+
+  if(!DVDFileSeek_(ifofile->file, offset))
+    return 0;
+
+  if(!(DVDReadBytes(ifofile->file, cell_playback, size)))
+    return 0;
+
+  for(i = 0; i < nr; i++) {
+    B2N_32(cell_playback[i].first_sector);
+    B2N_32(cell_playback[i].first_ilvu_end_sector);
+    B2N_32(cell_playback[i].last_vobu_start_sector);
+    B2N_32(cell_playback[i].last_sector);
+    
+    /* Changed < to <= because this was false in the movie 'Pi'. */
+    CHECK_VALUE(cell_playback[i].last_vobu_start_sector <= 
+                cell_playback[i].last_sector);
+    CHECK_VALUE(cell_playback[i].first_sector <= 
+                cell_playback[i].last_vobu_start_sector);
+  }
+
+  return 1;
+}
+
+
+static int ifoRead_CELL_POSITION_TBL(ifo_handle_t *ifofile, 
+                                     cell_position_t *cell_position, 
+                                     unsigned int nr, unsigned int offset) {
+  unsigned int i;
+  unsigned int size = nr * sizeof(cell_position_t);
+
+  if(!DVDFileSeek_(ifofile->file, offset))
+    return 0;
+
+  if(!(DVDReadBytes(ifofile->file, cell_position, size)))
+    return 0;
+
+  for(i = 0; i < nr; i++) {
+    B2N_16(cell_position[i].vob_id_nr);
+    CHECK_ZERO(cell_position[i].zero_1);
+  }
+
+  return 1;
+}
+
+static int ifoRead_PGC(ifo_handle_t *ifofile, pgc_t *pgc, unsigned int offset) {
+  unsigned int i;
+
+  if(!DVDFileSeek_(ifofile->file, offset))
+    return 0;
+ 
+  if(!(DVDReadBytes(ifofile->file, pgc, PGC_SIZE)))
+    return 0;
+
+  B2N_16(pgc->next_pgc_nr);
+  B2N_16(pgc->prev_pgc_nr);
+  B2N_16(pgc->goup_pgc_nr);
+  B2N_16(pgc->command_tbl_offset);
+  B2N_16(pgc->program_map_offset);
+  B2N_16(pgc->cell_playback_offset);
+  B2N_16(pgc->cell_position_offset);
+
+  for(i = 0; i < 8; i++)
+    B2N_16(pgc->audio_control[i]);
+  for(i = 0; i < 32; i++)
+    B2N_32(pgc->subp_control[i]);
+  for(i = 0; i < 16; i++)
+    B2N_32(pgc->palette[i]);
+  
+  CHECK_ZERO(pgc->zero_1);
+  CHECK_VALUE(pgc->nr_of_programs <= pgc->nr_of_cells);
+
+  /* verify time (look at print_time) */
+  for(i = 0; i < 8; i++)
+    if(!pgc->audio_control[i] & 0x8000)
+      CHECK_ZERO(pgc->audio_control[i]);
+  for(i = 0; i < 32; i++)
+    if(!pgc->subp_control[i] & 0x80000000)
+      CHECK_ZERO(pgc->subp_control[i]);
+  
+  /* Check that time is 0:0:0:0 also if nr_of_programs == 0 */
+  if(pgc->nr_of_programs == 0) {
+    CHECK_ZERO(pgc->still_time);
+    CHECK_ZERO(pgc->pg_playback_mode); // ??
+    CHECK_VALUE(pgc->program_map_offset == 0);
+    CHECK_VALUE(pgc->cell_playback_offset == 0);
+    CHECK_VALUE(pgc->cell_position_offset == 0);
+  } else {
+    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) {
+    pgc->command_tbl = malloc(sizeof(pgc_command_tbl_t));
+    if(!pgc->command_tbl)
+      return 0;
+
+    if(!ifoRead_PGC_COMMAND_TBL(ifofile, pgc->command_tbl, 
+                                offset + pgc->command_tbl_offset)) {
+      free(pgc->command_tbl);
+      return 0;
+    }
+  } else {
+    pgc->command_tbl = NULL;
+  }
+  
+  if(pgc->program_map_offset != 0) {
+    if(pgc->nr_of_programs != 0) {
+
+    pgc->program_map = malloc(pgc->nr_of_programs * sizeof(pgc_program_map_t));
+    if(!pgc->program_map) {
+      ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+      return 0;
+    }
+    if(!ifoRead_PGC_PROGRAM_MAP(ifofile, pgc->program_map,pgc->nr_of_programs,
+                                offset + pgc->program_map_offset)) {
+      ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+      free(pgc->program_map);
+      return 0;
+    }
+    } else {
+      pgc->program_map = NULL;
+    }
+  } else {
+    pgc->program_map = NULL;
+  }
+  
+  if(pgc->cell_playback_offset != 0) {
+    if(pgc->nr_of_cells != 0) {
+
+    pgc->cell_playback = malloc(pgc->nr_of_cells * sizeof(cell_playback_t));
+    if(!pgc->cell_playback) {
+      ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+      if(pgc->program_map)
+        free(pgc->program_map);
+      return 0;
+    }
+    if(!ifoRead_CELL_PLAYBACK_TBL(ifofile, pgc->cell_playback, 
+                                  pgc->nr_of_cells,
+                                  offset + pgc->cell_playback_offset)) {
+      ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+      if(pgc->program_map)
+        free(pgc->program_map);
+      free(pgc->cell_playback);
+      return 0;
+    }
+    } else {
+      pgc->cell_playback = NULL;
+    }
+  } else {
+    pgc->cell_playback = NULL;
+  }
+  
+  if(pgc->cell_position_offset != 0) {
+    if(pgc->nr_of_cells != 0) {
+
+    pgc->cell_position = malloc(pgc->nr_of_cells * sizeof(cell_position_t));
+    if(!pgc->cell_position) {
+      ifoFree_PGC(pgc);
+      return 0;
+    }
+    if(!ifoRead_CELL_POSITION_TBL(ifofile, pgc->cell_position, 
+                                  pgc->nr_of_cells,
+                                  offset + pgc->cell_position_offset)) {
+      ifoFree_PGC(pgc);
+      return 0;
+    }
+    } else {
+      pgc->cell_position = NULL;
+    }
+  } else {
+    pgc->cell_position = NULL;
+  }
+
+  return 1;
+}
+
+int ifoRead_FP_PGC(ifo_handle_t *ifofile) {
+
+  if(!ifofile)
+    return 0;
+
+  if(!ifofile->vmgi_mat)
+    return 0;
+  
+  /* It seems that first_play_pgc is optional after all. */
+  ifofile->first_play_pgc = 0;
+  if(ifofile->vmgi_mat->first_play_pgc == 0)
+    return 1;
+  
+  ifofile->first_play_pgc = malloc(sizeof(pgc_t));
+  if(!ifofile->first_play_pgc)
+    return 0;
+  
+  if(!ifoRead_PGC(ifofile, ifofile->first_play_pgc, 
+                  ifofile->vmgi_mat->first_play_pgc)) {
+    free(ifofile->first_play_pgc);
+    ifofile->first_play_pgc = 0;
+    return 0;
+  }
+
+  return 1;
+}
+
+static void ifoFree_PGC(pgc_t *pgc) {
+  if(pgc) {
+    ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+    if(pgc->program_map)
+      free(pgc->program_map);
+    if(pgc->cell_playback)
+      free(pgc->cell_playback);
+    if(pgc->cell_position)
+      free(pgc->cell_position);
+  }
+}
+
+void ifoFree_FP_PGC(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  if(ifofile->first_play_pgc) {
+    ifoFree_PGC(ifofile->first_play_pgc);
+    free(ifofile->first_play_pgc);
+    ifofile->first_play_pgc = 0;
+  }
+}
+
+
+int ifoRead_TT_SRPT(ifo_handle_t *ifofile) {
+  tt_srpt_t *tt_srpt;
+  int i, info_length;
+
+  if(!ifofile)
+    return 0;
+
+  if(!ifofile->vmgi_mat)
+    return 0;
+
+  if(ifofile->vmgi_mat->tt_srpt == 0) /* mandatory */
+    return 0;
+
+  if(!DVDFileSeek_(ifofile->file, ifofile->vmgi_mat->tt_srpt * DVD_BLOCK_LEN))
+    return 0;
+
+  tt_srpt = malloc(sizeof(tt_srpt_t));
+  if(!tt_srpt)
+    return 0;
+
+  ifofile->tt_srpt = tt_srpt;
+  
+  if(!(DVDReadBytes(ifofile->file, tt_srpt, TT_SRPT_SIZE))) {
+    if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
+      fprintf(stderr, "libdvdread: Unable to read read TT_SRPT.\n");
+    }
+    free(tt_srpt);
+    return 0;
+  }
+
+  B2N_16(tt_srpt->nr_of_srpts);
+  B2N_32(tt_srpt->last_byte);
+  
+  info_length = tt_srpt->last_byte + 1 - TT_SRPT_SIZE;
+
+  tt_srpt->title = malloc(info_length);
+  if(!tt_srpt->title) {
+    free(tt_srpt);
+    ifofile->tt_srpt = 0;
+    return 0;
+  }
+  if(!(DVDReadBytes(ifofile->file, tt_srpt->title, info_length))) {
+    if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
+      fprintf(stderr, "libdvdread: Unable to read read TT_SRPT.\n");
+    }
+    ifoFree_TT_SRPT(ifofile);
+    return 0;
+  }
+
+  for(i =  0; i < tt_srpt->nr_of_srpts; i++) {
+    B2N_16(tt_srpt->title[i].nr_of_ptts);
+    B2N_16(tt_srpt->title[i].parental_id);
+    B2N_32(tt_srpt->title[i].title_set_sector);
+  }
+  
+
+  CHECK_ZERO(tt_srpt->zero_1);
+  CHECK_VALUE(tt_srpt->nr_of_srpts != 0);
+  CHECK_VALUE(tt_srpt->nr_of_srpts < 100); // ??
+  CHECK_VALUE(tt_srpt->nr_of_srpts * sizeof(title_info_t) <= info_length);
+  
+  for(i = 0; i < tt_srpt->nr_of_srpts; i++) {
+    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:
+    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
+#if 0
+  if(memcmp((uint8_t *)tt_srpt->title + 
+            tt_srpt->nr_of_srpts * sizeof(title_info_t), 
+            my_friendly_zeros, 
+            info_length - tt_srpt->nr_of_srpts * sizeof(title_info_t))) {
+    fprintf(stderr, "VMG_PTT_SRPT slack is != 0, ");
+    hexdump((uint8_t *)tt_srpt->title + 
+            tt_srpt->nr_of_srpts * sizeof(title_info_t), 
+            info_length - tt_srpt->nr_of_srpts * sizeof(title_info_t));
+  }
+#endif
+
+  return 1;
+}
+
+
+void ifoFree_TT_SRPT(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  if(ifofile->tt_srpt) {
+    free(ifofile->tt_srpt->title);
+    free(ifofile->tt_srpt);
+    ifofile->tt_srpt = 0;
+  }
+}
+
+
+int ifoRead_VTS_PTT_SRPT(ifo_handle_t *ifofile) {
+  vts_ptt_srpt_t *vts_ptt_srpt;
+  int info_length, i, j;
+  uint32_t *data;
+
+  if(!ifofile)
+    return 0;
+  
+  if(!ifofile->vtsi_mat)
+    return 0;
+
+  if(ifofile->vtsi_mat->vts_ptt_srpt == 0) /* mandatory */
+    return 0;
+    
+  if(!DVDFileSeek_(ifofile->file,
+                   ifofile->vtsi_mat->vts_ptt_srpt * DVD_BLOCK_LEN))
+    return 0;
+
+  vts_ptt_srpt = malloc(sizeof(vts_ptt_srpt_t));
+  if(!vts_ptt_srpt)
+    return 0;
+
+  ifofile->vts_ptt_srpt = vts_ptt_srpt;
+
+  if(!(DVDReadBytes(ifofile->file, vts_ptt_srpt, VTS_PTT_SRPT_SIZE))) {
+    if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
+      fprintf(stderr, "libdvdread: Unable to read PTT search table.\n");
+    }
+    free(vts_ptt_srpt);
+    return 0;
+  }
+
+  B2N_16(vts_ptt_srpt->nr_of_srpts);
+  B2N_32(vts_ptt_srpt->last_byte);
+
+  CHECK_ZERO(vts_ptt_srpt->zero_1);
+  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;
+  
+  data = malloc(info_length);
+  if(!data) {
+    free(vts_ptt_srpt);
+    ifofile->vts_ptt_srpt = 0;
+    return 0;
+  }
+  if(!(DVDReadBytes(ifofile->file, data, info_length))) {
+    if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
+      fprintf(stderr, "libdvdread: Unable to read PTT search table.\n");
+    }
+    free(vts_ptt_srpt);
+    free(data);
+    ifofile->vts_ptt_srpt = 0;
+    return 0;
+  }
+
+  for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+    B2N_32(data[i]);
+    /* assert(data[i] + sizeof(ptt_info_t) <= vts_ptt_srpt->last_byte + 1);
+       Magic Knight Rayearth Daybreak is mastered very strange and has 
+       Titles with 0 PTTs. They all have a data[i] offsets beyond the end of
+       of the vts_ptt_srpt structure. */
+    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) {
+    free(vts_ptt_srpt);
+    free(data);
+    ifofile->vts_ptt_srpt = 0;
+    return 0;
+  }
+  for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+    int n;
+    if(i < vts_ptt_srpt->nr_of_srpts - 1)
+      n = (data[i+1] - data[i]);
+    else
+      n = (vts_ptt_srpt->last_byte + 1 - data[i]);
+    /* assert(n > 0 && (n % 4) == 0);
+       Magic Knight Rayearth Daybreak is mastered very strange and has 
+       Titles with 0 PTTs. */
+    if(n < 0) n = 0;
+    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));
+    if(!vts_ptt_srpt->title[i].ptt) {
+      for(n = 0; n < i; n++)
+        free(vts_ptt_srpt->title[n].ptt);
+      free(vts_ptt_srpt);
+      free(data);
+      ifofile->vts_ptt_srpt = 0;
+      return 0;
+    }
+    for(j = 0; j < vts_ptt_srpt->title[i].nr_of_ptts; j++) {
+      /* The assert placed here because of Magic Knight Rayearth Daybreak */
+      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);
+    }
+  }
+  
+  for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+    for(j = 0; j < vts_ptt_srpt->title[i].nr_of_ptts; j++) {
+      B2N_16(vts_ptt_srpt->title[i].ptt[j].pgcn);
+      B2N_16(vts_ptt_srpt->title[i].ptt[j].pgn);
+    }
+  }
+  
+  for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+    CHECK_VALUE(vts_ptt_srpt->title[i].nr_of_ptts < 1000); // ??
+    for(j = 0; j < vts_ptt_srpt->title[i].nr_of_ptts; j++) {
+      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); // ??
+    }
+  }
+
+  return 1;
+}
+
+
+void ifoFree_VTS_PTT_SRPT(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  if(ifofile->vts_ptt_srpt) {
+    int i;
+    for(i = 0; i < ifofile->vts_ptt_srpt->nr_of_srpts; i++)
+      free(ifofile->vts_ptt_srpt->title[i].ptt);
+    free(ifofile->vts_ptt_srpt->ttu_offset);
+    free(ifofile->vts_ptt_srpt->title);
+    free(ifofile->vts_ptt_srpt);
+    ifofile->vts_ptt_srpt = 0;
+  }
+}
+
+
+int ifoRead_PTL_MAIT(ifo_handle_t *ifofile) {
+  ptl_mait_t *ptl_mait;
+  int info_length;
+  unsigned int i, j;
+
+  if(!ifofile)
+    return 0;
+  
+  if(!ifofile->vmgi_mat)
+    return 0;
+  
+  if(ifofile->vmgi_mat->ptl_mait == 0)
+    return 1;
+
+  if(!DVDFileSeek_(ifofile->file, ifofile->vmgi_mat->ptl_mait * DVD_BLOCK_LEN))
+    return 0;
+
+  ptl_mait = malloc(sizeof(ptl_mait_t));
+  if(!ptl_mait)
+    return 0;
+
+  ifofile->ptl_mait = ptl_mait;
+
+  if(!(DVDReadBytes(ifofile->file, ptl_mait, PTL_MAIT_SIZE))) {
+    free(ptl_mait);
+    ifofile->ptl_mait = 0;
+    return 0;
+  }
+
+  B2N_16(ptl_mait->nr_of_countries);
+  B2N_16(ptl_mait->nr_of_vtss);
+  B2N_32(ptl_mait->last_byte);
+  
+  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);
+  
+  info_length = ptl_mait->nr_of_countries * sizeof(ptl_mait_country_t);
+  ptl_mait->countries = malloc(info_length);
+  if(!ptl_mait->countries) {
+    free(ptl_mait);
+    ifofile->ptl_mait = 0;
+    return 0;
+  }
+  
+  for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+    if(!(DVDReadBytes(ifofile->file, &ptl_mait->countries[i], PTL_MAIT_COUNTRY_SIZE))) {
+      if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
+        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++) {
+    B2N_16(ptl_mait->countries[i].country_code);
+    B2N_16(ptl_mait->countries[i].pf_ptl_mai_start_byte);
+  }
+  
+  for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+    CHECK_ZERO(ptl_mait->countries[i].zero_1);
+    CHECK_ZERO(ptl_mait->countries[i].zero_2);    
+    CHECK_VALUE(ptl_mait->countries[i].pf_ptl_mai_start_byte +
+                16U * (ptl_mait->nr_of_vtss + 1) <= ptl_mait->last_byte + 1U);
+  }
+
+  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)) {
+      if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
+        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 = 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))) {
+      if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
+        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 = 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;
+
+  /* Seems to be optional, at least when there are no OneSequencial Titles */
+  if(ifofile->vtsi_mat->vts_tmapt == 0) {
+    ifofile->vts_tmapt = NULL;
+    return 1;
+  }
+  
+  offset = ifofile->vtsi_mat->vts_tmapt * DVD_BLOCK_LEN;
+  
+  if(!DVDFileSeek_(ifofile->file, offset)) 
+    return 0;
+  
+  vts_tmapt = malloc(sizeof(vts_tmapt_t));
+  if(!vts_tmapt)
+    return 0;
+  
+  ifofile->vts_tmapt = vts_tmapt;
+  
+  if(!(DVDReadBytes(ifofile->file, vts_tmapt, VTS_TMAPT_SIZE))) {
+    if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
+      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 = 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))) {
+    if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
+      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 = 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))) {
+      if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
+        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 = 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))) {
+      if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
+        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)
+    return 0;
+
+  if(!ifofile->vtsi_mat)
+    return 0;
+
+  if(ifofile->vtsi_mat->vts_c_adt == 0) /* mandatory */
+    return 0;
+
+  ifofile->vts_c_adt = malloc(sizeof(c_adt_t));
+  if(!ifofile->vts_c_adt)
+    return 0;
+
+  if(!ifoRead_C_ADT_internal(ifofile, ifofile->vts_c_adt, 
+                             ifofile->vtsi_mat->vts_c_adt)) {
+    free(ifofile->vts_c_adt);
+    ifofile->vts_c_adt = 0;
+    return 0;
+  }
+
+  return 1;
+}
+
+int ifoRead_C_ADT(ifo_handle_t *ifofile) {
+  unsigned int sector;
+
+  if(!ifofile)
+    return 0;
+  
+  if(ifofile->vmgi_mat) {
+    if(ifofile->vmgi_mat->vmgm_c_adt == 0)
+      return 1;
+    sector = ifofile->vmgi_mat->vmgm_c_adt;
+  } else if(ifofile->vtsi_mat) {
+    if(ifofile->vtsi_mat->vtsm_c_adt == 0)
+      return 1;
+    sector = ifofile->vtsi_mat->vtsm_c_adt;
+  } else {
+    return 0;
+  }
+  
+  ifofile->menu_c_adt = malloc(sizeof(c_adt_t));
+  if(!ifofile->menu_c_adt)
+    return 0;
+
+  if(!ifoRead_C_ADT_internal(ifofile, ifofile->menu_c_adt, sector)) {
+    free(ifofile->menu_c_adt);
+    ifofile->menu_c_adt = 0;
+    return 0;
+  }
+
+  return 1;
+}
+
+static int ifoRead_C_ADT_internal(ifo_handle_t *ifofile, 
+                                  c_adt_t *c_adt, unsigned int sector) {
+  int i, info_length;
+
+  if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN))
+    return 0;
+
+  if(!(DVDReadBytes(ifofile->file, c_adt, C_ADT_SIZE)))
+    return 0;
+
+  B2N_16(c_adt->nr_of_vobs);
+  B2N_32(c_adt->last_byte);
+  
+  info_length = c_adt->last_byte + 1 - C_ADT_SIZE;
+  
+  CHECK_ZERO(c_adt->zero_1);
+  /* assert(c_adt->nr_of_vobs > 0);  
+     Magic Knight Rayearth Daybreak is mastered very strange and has 
+     Titles with a VOBS that has no cells. */
+  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
+     is to high, they high ones are never referenced though. */
+  if(info_length / sizeof(cell_adr_t) < c_adt->nr_of_vobs) {
+    if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
+      fprintf(stderr, "libdvdread: *C_ADT nr_of_vobs > avaiable info entries\n");
+    }
+    c_adt->nr_of_vobs = info_length / sizeof(cell_adr_t);
+  }
+  
+  c_adt->cell_adr_table = malloc(info_length);
+  if(!c_adt->cell_adr_table)
+    return 0;
+
+  if(info_length && 
+     !(DVDReadBytes(ifofile->file, c_adt->cell_adr_table, info_length))) {
+    free(c_adt->cell_adr_table);
+    return 0;
+  }
+
+  for(i = 0; i < info_length/sizeof(cell_adr_t); i++) {
+    B2N_16(c_adt->cell_adr_table[i].vob_id);
+    B2N_32(c_adt->cell_adr_table[i].start_sector);
+    B2N_32(c_adt->cell_adr_table[i].last_sector);
+
+    CHECK_ZERO(c_adt->cell_adr_table[i].zero_1);
+    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);
+  }
+
+  return 1;
+}
+
+
+static void ifoFree_C_ADT_internal(c_adt_t *c_adt) {
+  if(c_adt) {
+    free(c_adt->cell_adr_table);
+    free(c_adt);
+  }
+}
+
+void ifoFree_C_ADT(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  ifoFree_C_ADT_internal(ifofile->menu_c_adt);
+  ifofile->menu_c_adt = 0;
+}
+
+void ifoFree_TITLE_C_ADT(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  ifoFree_C_ADT_internal(ifofile->vts_c_adt);
+  ifofile->vts_c_adt = 0;
+}
+
+int ifoRead_TITLE_VOBU_ADMAP(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return 0;
+
+  if(!ifofile->vtsi_mat)
+    return 0;
+  
+  if(ifofile->vtsi_mat->vts_vobu_admap == 0) /* mandatory */
+    return 0;
+  
+  ifofile->vts_vobu_admap = malloc(sizeof(vobu_admap_t));
+  if(!ifofile->vts_vobu_admap)
+    return 0;
+
+  if(!ifoRead_VOBU_ADMAP_internal(ifofile, ifofile->vts_vobu_admap,
+                                  ifofile->vtsi_mat->vts_vobu_admap)) {
+    free(ifofile->vts_vobu_admap);
+    ifofile->vts_vobu_admap = 0;
+    return 0;
+  }
+
+  return 1;
+}
+
+int ifoRead_VOBU_ADMAP(ifo_handle_t *ifofile) {
+  unsigned int sector;
+
+  if(!ifofile)
+    return 0;
+     
+  if(ifofile->vmgi_mat) {
+    if(ifofile->vmgi_mat->vmgm_vobu_admap == 0)
+      return 1;
+    sector = ifofile->vmgi_mat->vmgm_vobu_admap;
+  } else if(ifofile->vtsi_mat) {
+    if(ifofile->vtsi_mat->vtsm_vobu_admap == 0)
+      return 1;
+    sector = ifofile->vtsi_mat->vtsm_vobu_admap;
+  } else {
+    return 0;
+  }
+  
+  ifofile->menu_vobu_admap = malloc(sizeof(vobu_admap_t));
+  if(!ifofile->menu_vobu_admap)
+    return 0;
+  
+  if(!ifoRead_VOBU_ADMAP_internal(ifofile, ifofile->menu_vobu_admap, sector)) {
+    free(ifofile->menu_vobu_admap);
+    ifofile->menu_vobu_admap = 0;
+    return 0;
+  }
+
+  return 1;
+}
+
+static int ifoRead_VOBU_ADMAP_internal(ifo_handle_t *ifofile, 
+                                       vobu_admap_t *vobu_admap, 
+                                       unsigned int sector) {
+  unsigned int i;
+  int info_length;
+
+  if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN))
+    return 0;
+
+  if(!(DVDReadBytes(ifofile->file, vobu_admap, VOBU_ADMAP_SIZE)))
+    return 0;
+
+  B2N_32(vobu_admap->last_byte);
+  
+  info_length = vobu_admap->last_byte + 1 - VOBU_ADMAP_SIZE;
+  /* assert(info_length > 0);
+     Magic Knight Rayearth Daybreak is mastered very strange and has 
+     Titles with a VOBS that has no VOBUs. */
+  CHECK_VALUE(info_length % sizeof(uint32_t) == 0);
+  
+  vobu_admap->vobu_start_sectors = malloc(info_length);
+  if(!vobu_admap->vobu_start_sectors) {
+    return 0;
+  }
+  if(info_length && 
+     !(DVDReadBytes(ifofile->file, 
+                    vobu_admap->vobu_start_sectors, info_length))) {
+    free(vobu_admap->vobu_start_sectors);
+    return 0;
+  }
+
+  for(i = 0; i < info_length/sizeof(uint32_t); i++)
+    B2N_32(vobu_admap->vobu_start_sectors[i]);
+
+  return 1;
+}
+
+
+static void ifoFree_VOBU_ADMAP_internal(vobu_admap_t *vobu_admap) {
+  if(vobu_admap) {
+    free(vobu_admap->vobu_start_sectors);
+    free(vobu_admap);
+  }
+}
+
+void ifoFree_VOBU_ADMAP(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  ifoFree_VOBU_ADMAP_internal(ifofile->menu_vobu_admap);
+  ifofile->menu_vobu_admap = 0;
+}
+
+void ifoFree_TITLE_VOBU_ADMAP(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  ifoFree_VOBU_ADMAP_internal(ifofile->vts_vobu_admap);
+  ifofile->vts_vobu_admap = 0;
+}
+
+int ifoRead_PGCIT(ifo_handle_t *ifofile) {
+
+  if(!ifofile)
+    return 0;
+  
+  if(!ifofile->vtsi_mat)
+    return 0;
+  
+  if(ifofile->vtsi_mat->vts_pgcit == 0) /* mandatory */
+    return 0;
+  
+  ifofile->vts_pgcit = malloc(sizeof(pgcit_t));
+  if(!ifofile->vts_pgcit)
+    return 0;
+
+  if(!ifoRead_PGCIT_internal(ifofile, ifofile->vts_pgcit, 
+                             ifofile->vtsi_mat->vts_pgcit * DVD_BLOCK_LEN)) {
+    free(ifofile->vts_pgcit);
+    ifofile->vts_pgcit = 0;
+    return 0;
+  }
+
+  return 1;
+}
+
+static int ifoRead_PGCIT_internal(ifo_handle_t *ifofile, pgcit_t *pgcit, 
+                                  unsigned int offset) {
+  int i, info_length;
+  uint8_t *data, *ptr;
+  
+  if(!DVDFileSeek_(ifofile->file, offset))
+    return 0;
+
+  if(!(DVDReadBytes(ifofile->file, pgcit, PGCIT_SIZE)))
+    return 0;
+
+  B2N_16(pgcit->nr_of_pgci_srp);
+  B2N_32(pgcit->last_byte);
+  
+  CHECK_ZERO(pgcit->zero_1);
+  /* assert(pgcit->nr_of_pgci_srp != 0);
+     Magic Knight Rayearth Daybreak is mastered very strange and has 
+     Titles with 0 PTTs. */
+  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);
+  if(!data)
+    return 0;
+
+  if(info_length && !(DVDReadBytes(ifofile->file, data, info_length))) {
+    free(data);
+    return 0;
+  }
+
+  pgcit->pgci_srp = malloc(pgcit->nr_of_pgci_srp * sizeof(pgci_srp_t));
+  if(!pgcit->pgci_srp) {
+    free(data);
+    return 0;
+  }
+  ptr = data;
+  for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+    memcpy(&pgcit->pgci_srp[i], ptr, PGCI_SRP_SIZE);
+    ptr += PGCI_SRP_SIZE;
+    B2N_16(pgcit->pgci_srp[i].ptl_id_mask);
+    B2N_32(pgcit->pgci_srp[i].pgc_start_byte);
+    CHECK_VALUE(pgcit->pgci_srp[i].unknown1 == 0);
+  }
+  free(data);
+  
+  for(i = 0; i < pgcit->nr_of_pgci_srp; i++)
+    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));
+    if(!pgcit->pgci_srp[i].pgc) {
+      int j;
+      for(j = 0; j < i; j++) {
+        ifoFree_PGC(pgcit->pgci_srp[j].pgc);
+        free(pgcit->pgci_srp[j].pgc);
+      }
+      free(pgcit->pgci_srp);
+      pgcit->pgci_srp = NULL;
+      return 0;
+    }
+    if(!ifoRead_PGC(ifofile, pgcit->pgci_srp[i].pgc, 
+                    offset + pgcit->pgci_srp[i].pgc_start_byte)) {
+      int j;
+      for(j = 0; j < i; j++) {
+        ifoFree_PGC(pgcit->pgci_srp[j].pgc);
+        free(pgcit->pgci_srp[j].pgc);
+      }
+      free(pgcit->pgci_srp);
+      pgcit->pgci_srp = NULL;
+      return 0;
+    }
+  }
+
+  return 1;
+}
+
+static void ifoFree_PGCIT_internal(pgcit_t *pgcit) {
+  if(pgcit) {
+    int i;
+    for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+      ifoFree_PGC(pgcit->pgci_srp[i].pgc);
+      free(pgcit->pgci_srp[i].pgc);
+    }
+    free(pgcit->pgci_srp);
+  }
+}
+
+void ifoFree_PGCIT(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  if(ifofile->vts_pgcit) {
+    ifoFree_PGCIT_internal(ifofile->vts_pgcit);
+    free(ifofile->vts_pgcit);
+    ifofile->vts_pgcit = 0;
+  }
+}
+
+
+int ifoRead_PGCI_UT(ifo_handle_t *ifofile) {
+  pgci_ut_t *pgci_ut;
+  unsigned int sector;
+  unsigned int i;  
+  int info_length;
+  uint8_t *data, *ptr;
+
+  if(!ifofile)
+    return 0;
+  
+  if(ifofile->vmgi_mat) {
+    if(ifofile->vmgi_mat->vmgm_pgci_ut == 0)
+      return 1;
+    sector = ifofile->vmgi_mat->vmgm_pgci_ut;
+  } else if(ifofile->vtsi_mat) {
+    if(ifofile->vtsi_mat->vtsm_pgci_ut == 0)
+      return 1;
+    sector = ifofile->vtsi_mat->vtsm_pgci_ut;
+  } else {
+    return 0;
+  }
+  
+  ifofile->pgci_ut = malloc(sizeof(pgci_ut_t));
+  if(!ifofile->pgci_ut)
+    return 0;
+  
+  if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN)) {
+    free(ifofile->pgci_ut);
+    ifofile->pgci_ut = 0;
+    return 0;
+  }
+  
+  if(!(DVDReadBytes(ifofile->file, ifofile->pgci_ut, PGCI_UT_SIZE))) {
+    free(ifofile->pgci_ut);
+    ifofile->pgci_ut = 0;
+    return 0;
+  }
+  
+  pgci_ut = ifofile->pgci_ut;
+  
+  B2N_16(pgci_ut->nr_of_lus);
+  B2N_32(pgci_ut->last_byte);
+  
+  CHECK_ZERO(pgci_ut->zero_1);
+  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);
+  if(!data) {
+    free(pgci_ut);
+    ifofile->pgci_ut = 0;
+    return 0;
+  }
+  if(!(DVDReadBytes(ifofile->file, data, info_length))) {
+    free(data);
+    free(pgci_ut);
+    ifofile->pgci_ut = 0;
+    return 0;
+  }
+
+  pgci_ut->lu = malloc(pgci_ut->nr_of_lus * sizeof(pgci_lu_t));
+  if(!pgci_ut->lu) {
+    free(data);
+    free(pgci_ut);
+    ifofile->pgci_ut = 0;
+    return 0;
+  }
+  ptr = data;
+  for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+    memcpy(&pgci_ut->lu[i], ptr, PGCI_LU_SIZE);
+    ptr += PGCI_LU_SIZE;
+    B2N_16(pgci_ut->lu[i].lang_code); 
+    B2N_32(pgci_ut->lu[i].lang_start_byte); 
+  }
+  free(data);
+  
+  for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+    // Maybe this is only defined for v1.1 and later titles?
+    /* If the bits in 'lu[i].exists' are enumerated abcd efgh then:
+       VTS_x_yy.IFO        VIDEO_TS.IFO
+       a == 0x83 "Root"         0x82 "Title"
+       b == 0x84 "Subpicture"
+       c == 0x85 "Audio"
+       d == 0x86 "Angle"
+       e == 0x87 "PTT"
+    */
+    CHECK_VALUE((pgci_ut->lu[i].exists & 0x07) == 0);
+  }
+
+  for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+    pgci_ut->lu[i].pgcit = malloc(sizeof(pgcit_t));
+    if(!pgci_ut->lu[i].pgcit) {
+      unsigned int j;
+      for(j = 0; j < i; j++) {
+        ifoFree_PGCIT_internal(pgci_ut->lu[j].pgcit);
+        free(pgci_ut->lu[j].pgcit);
+      }
+      free(pgci_ut->lu);
+      free(pgci_ut);
+      ifofile->pgci_ut = 0;
+      return 0;
+    }
+    if(!ifoRead_PGCIT_internal(ifofile, pgci_ut->lu[i].pgcit, 
+                               sector * DVD_BLOCK_LEN 
+                               + pgci_ut->lu[i].lang_start_byte)) {
+      unsigned int j;
+      for(j = 0; j < i; j++) {
+        ifoFree_PGCIT_internal(pgci_ut->lu[j].pgcit);
+        free(pgci_ut->lu[j].pgcit);
+      }
+      free(pgci_ut->lu[i].pgcit);
+      free(pgci_ut->lu);
+      free(pgci_ut);
+      ifofile->pgci_ut = 0;
+      return 0;
+    }
+    // FIXME: Iterate and verify that all menus that should exists accordingly
+    //        to pgci_ut->lu[i].exists really do?
+  }
+
+  return 1;
+}
+
+
+void ifoFree_PGCI_UT(ifo_handle_t *ifofile) {
+  unsigned int i;
+
+  if(!ifofile)
+    return;
+  
+  if(ifofile->pgci_ut) {
+    for(i = 0; i < ifofile->pgci_ut->nr_of_lus; i++) {
+      ifoFree_PGCIT_internal(ifofile->pgci_ut->lu[i].pgcit);
+      free(ifofile->pgci_ut->lu[i].pgcit);
+    }
+    free(ifofile->pgci_ut->lu);
+    free(ifofile->pgci_ut);
+    ifofile->pgci_ut = 0;
+  }
+}
+
+static int ifoRead_VTS_ATTRIBUTES(ifo_handle_t *ifofile, 
+                                  vts_attributes_t *vts_attributes, 
+                                  unsigned int offset) {
+  unsigned int i;
+
+  if(!DVDFileSeek_(ifofile->file, offset))
+    return 0;
+
+  if(!(DVDReadBytes(ifofile->file, vts_attributes, sizeof(vts_attributes_t))))
+    return 0;
+
+  B2N_32(vts_attributes->last_byte);
+  B2N_32(vts_attributes->vts_cat);
+  B2N_16(vts_attributes->vtsm_audio_attr.lang_code);
+  B2N_16(vts_attributes->vtsm_subp_attr.lang_code);
+  for(i = 0; i < 8; i++)
+    B2N_16(vts_attributes->vtstt_audio_attr[i].lang_code);
+  for(i = 0; i < 32; i++)
+    B2N_16(vts_attributes->vtstt_subp_attr[i].lang_code);
+  
+  CHECK_ZERO(vts_attributes->zero_1);
+  CHECK_ZERO(vts_attributes->zero_2);
+  CHECK_ZERO(vts_attributes->zero_3);
+  CHECK_ZERO(vts_attributes->zero_4);
+  CHECK_ZERO(vts_attributes->zero_5);
+  CHECK_ZERO(vts_attributes->zero_6);
+  CHECK_ZERO(vts_attributes->zero_7);
+  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]);
+  CHECK_VALUE(vts_attributes->nr_of_vtstt_subp_streams <= 32);
+  {
+    unsigned int nr_coded;
+    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;
+    }
+    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]);
+  }
+
+  return 1;
+}
+
+
+
+int ifoRead_VTS_ATRT(ifo_handle_t *ifofile) {
+  vts_atrt_t *vts_atrt;
+  unsigned int i, info_length, sector;
+  uint32_t *data;
+
+  if(!ifofile)
+    return 0;
+  
+  if(!ifofile->vmgi_mat)
+    return 0;
+  
+  if(ifofile->vmgi_mat->vts_atrt == 0) /* mandatory */
+    return 0;
+  
+  sector = ifofile->vmgi_mat->vts_atrt;
+  if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN))
+    return 0;
+
+  vts_atrt = malloc(sizeof(vts_atrt_t));
+  if(!vts_atrt)
+    return 0;
+
+  ifofile->vts_atrt = vts_atrt;
+  
+  if(!(DVDReadBytes(ifofile->file, vts_atrt, VTS_ATRT_SIZE))) {
+    free(vts_atrt);
+    ifofile->vts_atrt = 0;
+    return 0;
+  }
+
+  B2N_16(vts_atrt->nr_of_vtss);
+  B2N_32(vts_atrt->last_byte);
+
+  CHECK_ZERO(vts_atrt->zero_1);
+  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);
+  data = malloc(info_length);
+  if(!data) {
+    free(vts_atrt);
+    ifofile->vts_atrt = 0;
+    return 0;
+  }
+
+  vts_atrt->vts_atrt_offsets = data;   
+
+  if(!(DVDReadBytes(ifofile->file, data, info_length))) {
+    free(data);
+    free(vts_atrt);
+    ifofile->vts_atrt = 0;
+    return 0;
+  }
+  
+  for(i = 0; i < vts_atrt->nr_of_vtss; i++) {
+    B2N_32(data[i]);
+    CHECK_VALUE(data[i] + VTS_ATTRIBUTES_MIN_SIZE < vts_atrt->last_byte + 1);
+  }
+  
+  info_length = vts_atrt->nr_of_vtss * sizeof(vts_attributes_t);
+  vts_atrt->vts = malloc(info_length);
+  if(!vts_atrt->vts) {
+    free(data);
+    free(vts_atrt);
+    ifofile->vts_atrt = 0;
+    return 0;
+  }
+  for(i = 0; i < vts_atrt->nr_of_vtss; i++) {
+    unsigned int offset = data[i];
+    if(!ifoRead_VTS_ATTRIBUTES(ifofile, &(vts_atrt->vts[i]),
+                               (sector * DVD_BLOCK_LEN) + offset)) {
+      free(data);
+      free(vts_atrt);
+      ifofile->vts_atrt = 0;
+      return 0;
+    }
+
+    // This assert cant be in ifoRead_VTS_ATTRIBUTES
+    CHECK_VALUE(offset + vts_atrt->vts[i].last_byte <= vts_atrt->last_byte + 1);
+    // Is this check correct?
+  }
+
+  return 1;
+}
+
+
+void ifoFree_VTS_ATRT(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  if(ifofile->vts_atrt) {
+    free(ifofile->vts_atrt->vts);
+    free(ifofile->vts_atrt->vts_atrt_offsets);
+    free(ifofile->vts_atrt);
+    ifofile->vts_atrt = 0;
+  }
+}
+
+
+int ifoRead_TXTDT_MGI(ifo_handle_t *ifofile) {
+  txtdt_mgi_t *txtdt_mgi;
+
+  if(!ifofile)
+    return 0;
+  
+  if(!ifofile->vmgi_mat)
+    return 0;
+ 
+  /* Return successfully if there is nothing to read. */ 
+  if(ifofile->vmgi_mat->txtdt_mgi == 0)
+    return 1;
+
+  if(!DVDFileSeek_(ifofile->file, 
+                   ifofile->vmgi_mat->txtdt_mgi * DVD_BLOCK_LEN))
+    return 0;
+  
+  txtdt_mgi = malloc(sizeof(txtdt_mgi_t));
+  if(!txtdt_mgi) {
+    return 0;
+  }
+  ifofile->txtdt_mgi = txtdt_mgi;
+
+  if(!(DVDReadBytes(ifofile->file, txtdt_mgi, TXTDT_MGI_SIZE))) {
+    if(dvdread_verbose(device_of_file(ifofile->file)) >= 1) {
+      fprintf(stderr, "libdvdread: Unable to read TXTDT_MGI.\n");
+    }
+    free(txtdt_mgi);
+    ifofile->txtdt_mgi = 0;
+    return 0;
+  }
+
+  // fprintf(stderr, "-- Not done yet --\n");
+  return 1;
+}
+
+void ifoFree_TXTDT_MGI(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  if(ifofile->txtdt_mgi) {
+    free(ifofile->txtdt_mgi);
+    ifofile->txtdt_mgi = 0;
+  }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/ifo_read.h	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,228 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+#ifndef IFO_READ_H_INCLUDED
+#define IFO_READ_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001, 2002 Björn Englund <d4bjorn@dtek.chalmers.se>,
+ *                                Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <libdvdread/ifo_types.h>
+#include <libdvdread/dvd_reader.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * handle = ifoOpen(dvd, title);
+ *
+ * Opens an IFO and reads in all the data for the IFO file corresponding to the
+ * given title.  If title 0 is given, the video manager IFO file is read.
+ * Returns a handle to a completely parsed structure.
+ */
+ifo_handle_t *ifoOpen(dvd_reader_t *, int );
+
+/**
+ * handle = ifoOpenVMGI(dvd);
+ *
+ * Opens an IFO and reads in _only_ the vmgi_mat data.  This call can be used
+ * together with the calls below to read in each segment of the IFO file on
+ * demand.
+ */
+ifo_handle_t *ifoOpenVMGI(dvd_reader_t *);
+
+/**
+ * handle = ifoOpenVTSI(dvd, title);
+ *
+ * Opens an IFO and reads in _only_ the vtsi_mat data.  This call can be used
+ * together with the calls below to read in each segment of the IFO file on
+ * demand.
+ */
+ifo_handle_t *ifoOpenVTSI(dvd_reader_t *, int);
+
+/**
+ * ifoClose(ifofile);
+ * Cleans up the IFO information.  This will free all data allocated for the
+ * substructures.
+ */
+void ifoClose(ifo_handle_t *);
+
+/**
+ * The following functions are for reading only part of the VMGI/VTSI files.
+ * Returns 1 if the data was successfully read and 0 on error.
+ */
+
+/**
+ * okay = ifoRead_PLT_MAIT(ifofile);
+ *
+ * Read in the Parental Management Information table, filling the
+ * ifofile->ptl_mait structure and its substructures.  This data is only
+ * located in the video manager information file.  This fills the
+ * ifofile->ptl_mait structure and all its substructures.
+ */
+int ifoRead_PTL_MAIT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_VTS_ATRT(ifofile);
+ *
+ * Read in the attribute table for the main menu vob, filling the
+ * ifofile->vts_atrt structure and its substructures.  Only located in the
+ * video manager information file.  This fills in the ifofile->vts_atrt
+ * structure and all its substructures.
+ */
+int ifoRead_VTS_ATRT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_TT_SRPT(ifofile);
+ *
+ * Reads the title info for the main menu, filling the ifofile->tt_srpt
+ * structure and its substructures.  This data is only located in the video
+ * manager information file.  This structure is mandatory in the IFO file.
+ */
+int ifoRead_TT_SRPT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_VTS_PTT_SRPT(ifofile);
+ *
+ * Reads in the part of title search pointer table, filling the
+ * ifofile->vts_ptt_srpt structure and its substructures.  This data is only
+ * located in the video title set information file.  This structure is
+ * mandatory, and must be included in the VTSI file.
+ */
+int ifoRead_VTS_PTT_SRPT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_FP_PGC(ifofile);
+ *
+ * Reads in the first play program chain data, filling the
+ * ifofile->first_play_pgc structure.  This data is only located in the video
+ * manager information file (VMGI).  This structure is optional.
+ */
+int ifoRead_FP_PGC(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_PGCIT(ifofile);
+ *
+ * Reads in the program chain information table for the video title set.  Fills
+ * in the ifofile->vts_pgcit structure and its substructures, which includes
+ * the data for each program chain in the set.  This data is only located in
+ * the video title set information file.  This structure is mandatory, and must
+ * be included in the VTSI file.
+ */
+int ifoRead_PGCIT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_PGCI_UT(ifofile);
+ *
+ * Reads in the menu PGCI unit table for the menu VOB.  For the video manager,
+ * this corresponds to the VIDEO_TS.VOB file, and for each title set, this
+ * corresponds to the VTS_XX_0.VOB file.  This data is located in both the
+ * video manager and video title set information files.  For VMGI files, this
+ * fills the ifofile->vmgi_pgci_ut structure and all its substructures.  For
+ * VTSI files, this fills the ifofile->vtsm_pgci_ut structure.
+ */
+int ifoRead_PGCI_UT(ifo_handle_t *);
+  
+/**
+ * okay = ifoRead_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);
+ *
+ * Reads in the cell address table for the menu VOB.  For the video manager,
+ * this corresponds to the VIDEO_TS.VOB file, and for each title set, this
+ * corresponds to the VTS_XX_0.VOB file.  This data is located in both the
+ * video manager and video title set information files.  For VMGI files, this
+ * fills the ifofile->vmgm_c_adt structure and all its substructures.  For VTSI
+ * files, this fills the ifofile->vtsm_c_adt structure.
+ */
+int ifoRead_C_ADT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_TITLE_C_ADT(ifofile);
+ *
+ * Reads in the cell address table for the video title set corresponding to
+ * this IFO file.  This data is only located in the video title set information
+ * file.  This structure is mandatory, and must be included in the VTSI file.
+ * This call fills the ifofile->vts_c_adt structure and its substructures.
+ */
+int ifoRead_TITLE_C_ADT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_VOBU_ADMAP(ifofile);
+ *
+ * Reads in the VOBU address map for the menu VOB.  For the video manager, this
+ * corresponds to the VIDEO_TS.VOB file, and for each title set, this
+ * corresponds to the VTS_XX_0.VOB file.  This data is located in both the
+ * video manager and video title set information files.  For VMGI files, this
+ * fills the ifofile->vmgm_vobu_admap structure and all its substructures.  For
+ * VTSI files, this fills the ifofile->vtsm_vobu_admap structure.
+ */
+int ifoRead_VOBU_ADMAP(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_TITLE_VOBU_ADMAP(ifofile);
+ *
+ * Reads in the VOBU address map for the associated video title set.  This data
+ * is only located in the video title set information file.  This structure is
+ * mandatory, and must be included in the VTSI file.  Fills the
+ * ifofile->vts_vobu_admap structure and its substructures.
+ */
+int ifoRead_TITLE_VOBU_ADMAP(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_TXTDT_MGI(ifofile);
+ *
+ * Reads in the text data strings for the DVD.  Fills the ifofile->txtdt_mgi
+ * structure and all its substructures.  This data is only located in the video
+ * manager information file.  This structure is mandatory, and must be included
+ * in the VMGI file.
+ */
+int ifoRead_TXTDT_MGI(ifo_handle_t *);
+
+/**
+ * The following functions are used for freeing parsed sections of the
+ * ifo_handle_t structure and the allocated substructures.  The free calls
+ * below are safe:  they will not mind if you attempt to free part of an IFO
+ * file which was not read in or which does not exist.
+ */
+void ifoFree_PTL_MAIT(ifo_handle_t *);
+void ifoFree_VTS_ATRT(ifo_handle_t *);
+void ifoFree_TT_SRPT(ifo_handle_t *);
+void ifoFree_VTS_PTT_SRPT(ifo_handle_t *);
+void ifoFree_FP_PGC(ifo_handle_t *);
+void ifoFree_PGCIT(ifo_handle_t *);
+void ifoFree_PGCI_UT(ifo_handle_t *);
+void ifoFree_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 *);
+void ifoFree_TITLE_VOBU_ADMAP(ifo_handle_t *);
+void ifoFree_TXTDT_MGI(ifo_handle_t *);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* IFO_READ_H_INCLUDED */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/ifo_types.h	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,897 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+#ifndef IFO_TYPES_H_INCLUDED
+#define IFO_TYPES_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001 Björn Englund <d4bjorn@dtek.chalmers.se>,
+ *                          Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
+ * detailed changelog at http://svn.mplayerhq.hu/mplayer/trunk/
+ * $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
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <libdvdread/dvd_reader.h>
+
+#if defined(__BEOS__)
+#if !defined(_INTTYPES_H_) && !defined(_INTTYPES_H) && !defined(_STDINT_H_) && !defined(_STDINT_H)
+#error "Must include <inttypes.h> or <stdint.h> before any libdvdread header."
+#endif
+#else
+#if !defined(UINT8_MAX) || !defined(UINT16_MAX) || !defined(INT32_MAX)
+#error "Must include <inttypes.h> or <stdint.h> before any libdvdread header."
+#endif
+#endif
+
+#undef ATTRIBUTE_PACKED
+#undef PRAGMA_PACK_BEGIN 
+#undef PRAGMA_PACK_END
+
+#if defined(__GNUC__)
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
+#define ATTRIBUTE_PACKED __attribute__ ((packed))
+#define PRAGMA_PACK 0
+#endif
+#endif
+
+#if !defined(ATTRIBUTE_PACKED)
+#define ATTRIBUTE_PACKED
+#define PRAGMA_PACK 1
+#endif
+
+#if PRAGMA_PACK
+#pragma pack(1)
+#endif
+
+
+/**
+ * Common
+ *
+ * The following structures are used in both the VMGI and VTSI.
+ */
+
+
+/**
+ * DVD Time Information.
+ */
+typedef struct {
+  uint8_t hour;
+  uint8_t minute;
+  uint8_t second;
+  uint8_t frame_u; /* The two high bits are the frame rate. */
+} ATTRIBUTE_PACKED dvd_time_t;
+
+/**
+ * Type to store per-command data.
+ */
+typedef struct {
+  uint8_t bytes[8];
+} ATTRIBUTE_PACKED vm_cmd_t;
+#define COMMAND_DATA_SIZE 8U
+
+
+/**
+ * Video Attributes.
+ */
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+  unsigned int mpeg_version         : 2;
+  unsigned int video_format         : 2;
+  unsigned int display_aspect_ratio : 2;
+  unsigned int permitted_df         : 2;
+  
+  unsigned int line21_cc_1          : 1;
+  unsigned int line21_cc_2          : 1;
+  unsigned int unknown1             : 1;
+  unsigned int bit_rate             : 1;
+  
+  unsigned int picture_size         : 2;
+  unsigned int letterboxed          : 1;
+  unsigned int film_mode            : 1;
+#else
+  unsigned int permitted_df         : 2;
+  unsigned int display_aspect_ratio : 2;
+  unsigned int video_format         : 2;
+  unsigned int mpeg_version         : 2;
+  
+  unsigned int film_mode            : 1;
+  unsigned int letterboxed          : 1;
+  unsigned int picture_size         : 2;
+  
+  unsigned int 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.
+ */
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+  unsigned int audio_format           : 3;
+  unsigned int multichannel_extension : 1;
+  unsigned int lang_type              : 2;
+  unsigned int application_mode       : 2;
+  
+  unsigned int quantization           : 2;
+  unsigned int sample_frequency       : 2;
+  unsigned int unknown1               : 1;
+  unsigned int channels               : 3;
+#else
+  unsigned int application_mode       : 2;
+  unsigned int lang_type              : 2;
+  unsigned int multichannel_extension : 1;
+  unsigned int audio_format           : 3;
+  
+  unsigned int channels               : 3;
+  unsigned int unknown1               : 1;
+  unsigned int sample_frequency       : 2;
+  unsigned int quantization           : 2;
+#endif
+  uint16_t lang_code;
+  uint8_t  lang_extension;
+  uint8_t  code_extension;
+  uint8_t unknown3;
+  union {
+    struct {
+#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
+    } ATTRIBUTE_PACKED karaoke;
+    struct {
+#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
+    } ATTRIBUTE_PACKED surround;
+  } ATTRIBUTE_PACKED app_info;
+} ATTRIBUTE_PACKED audio_attr_t;
+
+
+/**
+ * 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 {
+  /*
+   * type: 0 not specified
+   *       1 language
+   *       2 other
+   * coding mode: 0 run length
+   *              1 extended
+   *              2 other
+   * language: indicates language if type == 1
+   * lang extension: if type == 1 contains the lang extension
+   */
+#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  code_extension;
+} ATTRIBUTE_PACKED subp_attr_t;
+
+
+
+/**
+ * PGC Command Table.
+ */ 
+typedef struct {
+  uint16_t nr_of_pre;
+  uint16_t nr_of_post;
+  uint16_t nr_of_cell;
+  uint16_t last_byte;
+  vm_cmd_t *pre_cmds;
+  vm_cmd_t *post_cmds;
+  vm_cmd_t *cell_cmds;
+} ATTRIBUTE_PACKED pgc_command_tbl_t;
+#define PGC_COMMAND_TBL_SIZE 8U
+
+/**
+ * PGC Program Map
+ */
+typedef uint8_t pgc_program_map_t; 
+
+/**
+ * Cell Playback Information.
+ */
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+  unsigned int block_mode       : 2;
+  unsigned int block_type       : 2;
+  unsigned int seamless_play    : 1;
+  unsigned int interleaved      : 1;
+  unsigned int stc_discontinuity: 1;
+  unsigned int seamless_angle   : 1;
+  
+  unsigned int 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;
+  unsigned int stc_discontinuity: 1;
+  unsigned int interleaved      : 1;
+  unsigned int seamless_play    : 1;
+  unsigned int block_type       : 2;
+  unsigned int block_mode       : 2;
+  
+  unsigned int unknown2         : 6;
+  unsigned int restricted       : 1;
+  unsigned int playback_mode    : 1;
+#endif
+  uint8_t still_time;
+  uint8_t cell_cmd_nr;
+  dvd_time_t playback_time;
+  uint32_t first_sector;
+  uint32_t first_ilvu_end_sector;
+  uint32_t last_vobu_start_sector;
+  uint32_t last_sector;
+} ATTRIBUTE_PACKED cell_playback_t;
+
+#define BLOCK_TYPE_NONE         0x0
+#define BLOCK_TYPE_ANGLE_BLOCK  0x1
+
+#define BLOCK_MODE_NOT_IN_BLOCK 0x0
+#define BLOCK_MODE_FIRST_CELL   0x1
+#define BLOCK_MODE_IN_BLOCK     0x2
+#define BLOCK_MODE_LAST_CELL    0x3
+
+/**
+ * Cell Position Information.
+ */
+typedef struct {
+  uint16_t vob_id_nr;
+  uint8_t  zero_1;
+  uint8_t  cell_nr;
+} ATTRIBUTE_PACKED cell_position_t;
+
+/**
+ * User Operations.
+ */
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+  unsigned int zero                           : 7; /* 25-31 */
+  unsigned int video_pres_mode_change         : 1; /* 24 */
+  
+  unsigned int karaoke_audio_pres_mode_change : 1; /* 23 */
+  unsigned int angle_change                   : 1;
+  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;
+  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;
+  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 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;
+  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;
+  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;
+
+/**
+ * Program Chain Information.
+ */
+typedef struct {
+  uint16_t zero_1;
+  uint8_t  nr_of_programs;
+  uint8_t  nr_of_cells;
+  dvd_time_t playback_time;
+  user_ops_t prohibited_ops;
+  uint16_t audio_control[8];
+  uint32_t subp_control[32];
+  uint16_t next_pgc_nr;
+  uint16_t prev_pgc_nr;
+  uint16_t goup_pgc_nr;
+  uint8_t  pg_playback_mode;
+  uint8_t  still_time;
+  uint32_t palette[16]; /* New type struct {zero_1, Y, Cr, Cb} ? */
+  uint16_t command_tbl_offset;
+  uint16_t program_map_offset;
+  uint16_t cell_playback_offset;
+  uint16_t cell_position_offset;
+  pgc_command_tbl_t *command_tbl;
+  pgc_program_map_t  *program_map;
+  cell_playback_t *cell_playback;
+  cell_position_t *cell_position;
+} ATTRIBUTE_PACKED pgc_t;
+#define PGC_SIZE 236U
+
+/**
+ * Program Chain Information Search Pointer.
+ */
+typedef struct {
+  uint8_t  entry_id;
+#ifdef WORDS_BIGENDIAN
+  unsigned int block_mode : 2;
+  unsigned int block_type : 2;
+  unsigned int unknown1   : 4;
+#else
+  unsigned int unknown1   : 4;
+  unsigned int block_type : 2;
+  unsigned int block_mode : 2;
+#endif  
+  uint16_t ptl_id_mask;
+  uint32_t pgc_start_byte;
+  pgc_t *pgc;
+} ATTRIBUTE_PACKED pgci_srp_t;
+#define PGCI_SRP_SIZE 8U
+
+/**
+ * Program Chain Information Table.
+ */
+typedef struct {
+  uint16_t nr_of_pgci_srp;
+  uint16_t zero_1;
+  uint32_t last_byte;
+  pgci_srp_t *pgci_srp;
+} ATTRIBUTE_PACKED pgcit_t;
+#define PGCIT_SIZE 8U
+
+/**
+ * Menu PGCI Language Unit.
+ */
+typedef struct {
+  uint16_t lang_code;
+  uint8_t  lang_extension;
+  uint8_t  exists;
+  uint32_t lang_start_byte;
+  pgcit_t *pgcit;
+} ATTRIBUTE_PACKED pgci_lu_t;
+#define PGCI_LU_SIZE 8U
+
+/**
+ * Menu PGCI Unit Table.
+ */
+typedef struct {
+  uint16_t nr_of_lus;
+  uint16_t zero_1;
+  uint32_t last_byte;
+  pgci_lu_t *lu;
+} ATTRIBUTE_PACKED pgci_ut_t;
+#define PGCI_UT_SIZE 8U
+
+/**
+ * Cell Address Information.
+ */
+typedef struct {
+  uint16_t vob_id;
+  uint8_t  cell_id;
+  uint8_t  zero_1;
+  uint32_t start_sector;
+  uint32_t last_sector;
+} ATTRIBUTE_PACKED cell_adr_t;
+
+/**
+ * Cell Address Table.
+ */
+typedef struct {
+  uint16_t nr_of_vobs; /* VOBs */
+  uint16_t zero_1;
+  uint32_t last_byte;
+  cell_adr_t *cell_adr_table;  /* No explicit size given. */
+} ATTRIBUTE_PACKED c_adt_t;
+#define C_ADT_SIZE 8U
+
+/**
+ * VOBU Address Map.
+ */
+typedef struct {
+  uint32_t last_byte;
+  uint32_t *vobu_start_sectors;
+} ATTRIBUTE_PACKED vobu_admap_t;
+#define VOBU_ADMAP_SIZE 4U
+
+
+
+
+/**
+ * VMGI
+ *
+ * The following structures relate to the Video Manager.
+ */
+
+/**
+ * Video Manager Information Management Table.
+ */
+typedef struct {
+  char     vmg_identifier[12];
+  uint32_t vmg_last_sector;
+  uint8_t  zero_1[12];
+  uint32_t vmgi_last_sector;
+  uint8_t  zero_2;
+  uint8_t  specification_version;
+  uint32_t vmg_category;
+  uint16_t vmg_nr_of_volumes;
+  uint16_t vmg_this_volume_nr;
+  uint8_t  disc_side;
+  uint8_t  zero_3[19];
+  uint16_t vmg_nr_of_title_sets;  /* Number of VTSs. */
+  char     provider_identifier[32];
+  uint64_t vmg_pos_code;
+  uint8_t  zero_4[24];
+  uint32_t vmgi_last_byte;
+  uint32_t first_play_pgc;
+  uint8_t  zero_5[56];
+  uint32_t vmgm_vobs;             /* sector */
+  uint32_t tt_srpt;               /* sector */
+  uint32_t vmgm_pgci_ut;          /* sector */
+  uint32_t ptl_mait;              /* sector */
+  uint32_t vts_atrt;              /* sector */
+  uint32_t txtdt_mgi;             /* sector */
+  uint32_t vmgm_c_adt;            /* sector */
+  uint32_t vmgm_vobu_admap;       /* sector */
+  uint8_t  zero_6[32];
+  
+  video_attr_t vmgm_video_attr;
+  uint8_t  zero_7;
+  uint8_t  nr_of_vmgm_audio_streams; /* should be 0 or 1 */
+  audio_attr_t vmgm_audio_attr;
+  audio_attr_t zero_8[7];
+  uint8_t  zero_9[17];
+  uint8_t  nr_of_vmgm_subp_streams; /* should be 0 or 1 */
+  subp_attr_t  vmgm_subp_attr;
+  subp_attr_t  zero_10[27];  /* XXX: how much 'padding' here? */
+} ATTRIBUTE_PACKED vmgi_mat_t;
+
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+  unsigned int zero_1                    : 1;
+  unsigned int multi_or_random_pgc_title : 1; /* 0: one sequential pgc title */
+  unsigned int jlc_exists_in_cell_cmd    : 1;
+  unsigned int jlc_exists_in_prepost_cmd : 1;
+  unsigned int jlc_exists_in_button_cmd  : 1;
+  unsigned int jlc_exists_in_tt_dom      : 1;
+  unsigned int chapter_search_or_play    : 1; /* UOP 1 */
+  unsigned int title_or_time_play        : 1; /* UOP 0 */
+#else
+  unsigned int title_or_time_play        : 1;
+  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;
+  unsigned int zero_1                    : 1;
+#endif
+} ATTRIBUTE_PACKED playback_type_t;
+
+/**
+ * Title Information.
+ */
+typedef struct {
+  playback_type_t pb_ty;
+  uint8_t  nr_of_angles;
+  uint16_t nr_of_ptts;
+  uint16_t parental_id;
+  uint8_t  title_set_nr;
+  uint8_t  vts_ttn;
+  uint32_t title_set_sector;
+} ATTRIBUTE_PACKED title_info_t;
+
+/**
+ * PartOfTitle Search Pointer Table.
+ */
+typedef struct {
+  uint16_t nr_of_srpts;
+  uint16_t zero_1;
+  uint32_t last_byte;
+  title_info_t *title;
+} ATTRIBUTE_PACKED tt_srpt_t;
+#define TT_SRPT_SIZE 8U
+
+
+/**
+ * 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.
+ */
+typedef struct {
+  uint16_t country_code;
+  uint16_t zero_1;
+  uint16_t pf_ptl_mai_start_byte;
+  uint16_t zero_2;
+  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 8U
+
+/**
+ * Parental Management Information Table.
+ */
+typedef struct {
+  uint16_t nr_of_countries;
+  uint16_t nr_of_vtss;
+  uint32_t last_byte;
+  ptl_mait_country_t *countries;
+} ATTRIBUTE_PACKED ptl_mait_t;
+#define PTL_MAIT_SIZE 8U
+
+/**
+ * Video Title Set Attributes.
+ */
+typedef struct {
+  uint32_t last_byte;
+  uint32_t vts_cat;
+  
+  video_attr_t vtsm_vobs_attr;
+  uint8_t  zero_1;
+  uint8_t  nr_of_vtsm_audio_streams; /* should be 0 or 1 */
+  audio_attr_t vtsm_audio_attr;
+  audio_attr_t zero_2[7];  
+  uint8_t  zero_3[16];
+  uint8_t  zero_4;
+  uint8_t  nr_of_vtsm_subp_streams; /* should be 0 or 1 */
+  subp_attr_t vtsm_subp_attr;
+  subp_attr_t zero_5[27];
+  
+  uint8_t  zero_6[2];
+  
+  video_attr_t vtstt_vobs_video_attr;
+  uint8_t  zero_7;
+  uint8_t  nr_of_vtstt_audio_streams;
+  audio_attr_t vtstt_audio_attr[8];
+  uint8_t  zero_8[16];
+  uint8_t  zero_9;
+  uint8_t  nr_of_vtstt_subp_streams;
+  subp_attr_t vtstt_subp_attr[32];
+} ATTRIBUTE_PACKED vts_attributes_t;
+#define VTS_ATTRIBUTES_SIZE 542U
+#define VTS_ATTRIBUTES_MIN_SIZE 356U
+
+/**
+ * Video Title Set Attribute Table.
+ */
+typedef struct {
+  uint16_t nr_of_vtss;
+  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 8U
+
+/**
+ * Text Data. (Incomplete)
+ */
+typedef struct {
+  uint32_t last_byte;    /* offsets are relative here */
+  uint16_t offsets[100]; /* == nr_of_srpts + 1 (first is disc title) */
+#if 0  
+  uint16_t unknown; /* 0x48 ?? 0x48 words (16bit) info following */
+  uint16_t zero_1;
+  
+  uint8_t type_of_info; /* ?? 01 == disc, 02 == Title, 04 == Title part */
+  uint8_t unknown1;
+  uint8_t unknown2;
+  uint8_t unknown3;
+  uint8_t unknown4; /* ?? allways 0x30 language?, text format? */
+  uint8_t unknown5;
+  uint16_t offset; /* from first */
+  
+  char text[12]; /* ended by 0x09 */
+#endif
+} ATTRIBUTE_PACKED txtdt_t;
+
+/**
+ * Text Data Language Unit. (Incomplete)
+ */ 
+typedef struct {
+  uint16_t lang_code;
+  uint16_t unknown;      /* 0x0001, title 1? disc 1? side 1? */
+  uint32_t txtdt_start_byte;  /* prt, rel start of vmg_txtdt_mgi  */
+  txtdt_t  *txtdt;
+} ATTRIBUTE_PACKED txtdt_lu_t;
+#define TXTDT_LU_SIZE 8U
+
+/**
+ * Text Data Manager Information. (Incomplete)
+ */
+typedef struct {
+  char disc_name[14];            /* how many bytes?? */
+  uint16_t nr_of_language_units; /* 32bit??          */
+  uint32_t last_byte;
+  txtdt_lu_t *lu;
+} ATTRIBUTE_PACKED txtdt_mgi_t;
+#define TXTDT_MGI_SIZE 20U
+
+
+/**
+ * VTS
+ *
+ * Structures relating to the Video Title Set (VTS).
+ */
+
+/**
+ * Video Title Set Information Management Table.
+ */
+typedef struct {
+  char vts_identifier[12];
+  uint32_t vts_last_sector;
+  uint8_t  zero_1[12];
+  uint32_t vtsi_last_sector;
+  uint8_t  zero_2;
+  uint8_t  specification_version;
+  uint32_t vts_category;
+  uint16_t zero_3;
+  uint16_t zero_4;
+  uint8_t  zero_5;
+  uint8_t  zero_6[19];
+  uint16_t zero_7;
+  uint8_t  zero_8[32];
+  uint64_t zero_9;
+  uint8_t  zero_10[24];
+  uint32_t vtsi_last_byte;
+  uint32_t zero_11;
+  uint8_t  zero_12[56];
+  uint32_t vtsm_vobs;       /* sector */
+  uint32_t vtstt_vobs;      /* sector */
+  uint32_t vts_ptt_srpt;    /* sector */
+  uint32_t vts_pgcit;       /* sector */
+  uint32_t vtsm_pgci_ut;    /* sector */
+  uint32_t vts_tmapt;       /* sector */
+  uint32_t vtsm_c_adt;      /* sector */
+  uint32_t vtsm_vobu_admap; /* sector */
+  uint32_t vts_c_adt;       /* sector */
+  uint32_t vts_vobu_admap;  /* sector */
+  uint8_t  zero_13[24];
+  
+  video_attr_t vtsm_video_attr;
+  uint8_t  zero_14;
+  uint8_t  nr_of_vtsm_audio_streams; /* should be 0 or 1 */
+  audio_attr_t vtsm_audio_attr;
+  audio_attr_t zero_15[7];
+  uint8_t  zero_16[17];
+  uint8_t  nr_of_vtsm_subp_streams; /* should be 0 or 1 */
+  subp_attr_t vtsm_subp_attr;
+  subp_attr_t zero_17[27];
+  uint8_t  zero_18[2];
+  
+  video_attr_t vts_video_attr;
+  uint8_t  zero_19;
+  uint8_t  nr_of_vts_audio_streams;
+  audio_attr_t vts_audio_attr[8];
+  uint8_t  zero_20[17];
+  uint8_t  nr_of_vts_subp_streams;
+  subp_attr_t vts_subp_attr[32];
+  uint16_t zero_21;
+  multichannel_ext_t vts_mu_audio_attr[8];
+  /* XXX: how much 'padding' here, if any? */
+} ATTRIBUTE_PACKED vtsi_mat_t;
+
+/**
+ * PartOfTitle Unit Information.
+ */
+typedef struct {
+  uint16_t pgcn;
+  uint16_t pgn;
+} ATTRIBUTE_PACKED ptt_info_t;
+
+/**
+ * PartOfTitle Information.
+ */
+typedef struct {
+  uint16_t nr_of_ptts;
+  ptt_info_t *ptt;
+} ATTRIBUTE_PACKED ttu_t;
+
+/**
+ * PartOfTitle Search Pointer Table.
+ */
+typedef struct {
+  uint16_t nr_of_srpts;
+  uint16_t zero_1;
+  uint32_t last_byte;
+  ttu_t  *title;
+  uint32_t *ttu_offset; /* offset table for each ttu */
+} ATTRIBUTE_PACKED vts_ptt_srpt_t;
+#define VTS_PTT_SRPT_SIZE 8U
+
+
+/**
+ * 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 4U
+
+/**
+ * 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 8U
+
+
+#if PRAGMA_PACK
+#pragma pack()
+#endif
+
+
+/**
+ * The following structure defines an IFO file.  The structure is divided into
+ * two parts, the VMGI, or Video Manager Information, which is read from the
+ * VIDEO_TS.[IFO,BUP] file, and the VTSI, or Video Title Set Information, which
+ * is read in from the VTS_XX_0.[IFO,BUP] files.
+ */
+typedef struct {
+  dvd_file_t *file;
+  
+  /* VMGI */
+  vmgi_mat_t     *vmgi_mat;
+  tt_srpt_t      *tt_srpt;
+  pgc_t          *first_play_pgc;    
+  ptl_mait_t     *ptl_mait;
+  vts_atrt_t     *vts_atrt;
+  txtdt_mgi_t    *txtdt_mgi;
+  
+  /* Common */
+  pgci_ut_t      *pgci_ut;
+  c_adt_t        *menu_c_adt;
+  vobu_admap_t   *menu_vobu_admap;
+  
+  /* VTSI */
+  vtsi_mat_t     *vtsi_mat;
+  vts_ptt_srpt_t *vts_ptt_srpt;
+  pgcit_t        *vts_pgcit;
+  vts_tmapt_t    *vts_tmapt;
+  c_adt_t        *vts_c_adt;
+  vobu_admap_t   *vts_vobu_admap;
+} ifo_handle_t;
+
+#endif /* IFO_TYPES_H_INCLUDED */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/libdvdread_changes.diff	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,514 @@
+--- dvdread.orig/dvd_reader.c	2007-08-06 13:34:37.000000000 +0200
++++ dvdread/dvd_reader.c	2007-08-06 13:35:19.000000000 +0200
+@@ -39,9 +43,11 @@
+ 
+ #if defined(__sun)
+ #include <sys/mnttab.h>
++#elif defined(hpux)
++#include </usr/conf/h/mnttab.h>
+ #elif defined(SYS_BSD)
+ #include <fstab.h>
+-#elif defined(__linux__)
++#elif defined(__linux__) || defined(__CYGWIN__)
+ #include <mntent.h>
+ #endif
+ 
+@@ -52,7 +58,7 @@
+ 
+ #include "dvdread_internal.h"
+ 
+-#define DEFAULT_UDF_CACHE_LEVEL 1
++#define DEFAULT_UDF_CACHE_LEVEL 0
+ 
+ struct dvd_reader_s {
+   /* Basic information. */
+@@ -183,19 +189,7 @@
+   dev->align = align;
+ }
+ 
+-#ifdef WIN32 /* replacement gettimeofday implementation */
+-#include <sys/timeb.h>
+-static int gettimeofday( struct timeval *tv, void *tz )
+-{
+-  struct timeb t;
+-  ftime( &t );
+-  tv->tv_sec = t.time;
+-  tv->tv_usec = t.millitm * 1000;
+-  return 0;
+-}
+-#endif
+ 
+-
+ /* Loop over all titles and call dvdcss_title to crack the keys. */
+ static int initAllCSSKeys( dvd_reader_t *dvd )
+ {
+@@ -494,7 +488,7 @@
+     char *path_copy;
+ #if defined(SYS_BSD)
+     struct fstab* fe;
+-#elif defined(__sun) || defined(__linux__)
++#elif defined(__sun) || defined(__linux__) || defined(__CYGWIN__)
+     FILE *mntfile;
+ #endif
+ 
+@@ -598,7 +592,7 @@
+       }
+       fclose( mntfile );
+     }
+-#elif defined(__linux__)
++#elif defined(__linux__) || defined(__CYGWIN__)
+     mntfile = fopen( MOUNTED, "r" );
+     if( mntfile ) {
+       struct mntent *me;
+@@ -623,6 +617,9 @@
+       }
+       fclose( mntfile );
+     }
++#elif defined(__MINGW32__)
++    dev_name = strdup(path);
++    auth_drive = DVDOpenImageFile( path, have_css );
+ #endif
+     if( !dev_name ) {
+       if(verbose >= 1) {
+@@ -841,8 +838,8 @@
+   }
+     
+   if( dvd->css_state == 1 /* Need key init */ ) {
+-    initAllCSSKeys( dvd );
+-    dvd->css_state = 2;
++//    initAllCSSKeys( dvd );
++//    dvd->css_state = 2;
+   }
+   /*    
+         if( dvdinput_title( dvd_file->dvd->dev, (int)start ) < 0 ) {
+
+--- dvdread.orig/dvdread_internal.h	2005-09-12 21:42:12.000000000 +0200
++++ dvdread/dvdread_internal.h	2007-08-06 12:57:08.000000000 +0200
+@@ -3,12 +3,7 @@
+ #define DVDREAD_INTERNAL_H
+ 
+ 
+-#define CHECK_VALUE(arg)                                                \
+-  if(!(arg)) {                                                          \
+-    fprintf(stderr, "\n*** libdvdread: CHECK_VALUE failed in %s:%i ***" \
+-            "\n*** for %s ***\n\n",                                     \
+-            __FILE__, __LINE__, # arg );                                \
+-  }
++#define CHECK_VALUE(arg)
+ 
+ 
+ int get_verbose(void);
+
+--- dvdread.orig/ifo_print.c	2005-09-15 18:54:29.000000000 +0200
++++ dvdread/ifo_print.c	2007-08-06 12:52:32.000000000 +0200
+@@ -793,14 +797,14 @@
+   ifoPrint_USER_OPS(&pgc->prohibited_ops);
+   
+   for(i = 0; i < 8; i++) {
+-    if(pgc->audio_control[i] & 0x8000) { /* The 'is present' bit */
++    if(pgc->audio_control[i].present) { /* The 'is present' bit */
+       printf("Audio stream %i control: %04x\n", 
+              i, pgc->audio_control[i]);
+     }
+   }
+   
+   for(i = 0; i < 32; i++) {
+-    if(pgc->subp_control[i] & 0x80000000) { /* The 'is present' bit */
++    if(pgc->subp_control[i].present) { /* The 'is present' bit */
+       printf("Subpicture stream %2i control: %08x\n", 
+              i, pgc->subp_control[i]);
+     }
+
+--- dvdread.orig/ifo_read.c	2006-01-22 13:19:19.000000000 +0100
++++ dvdread/ifo_read.c	2007-08-06 13:15:55.000000000 +0200
+@@ -110,7 +114,7 @@
+ ifo_handle_t *ifoOpen(dvd_reader_t *dvd, int title) {
+   ifo_handle_t *ifofile;
+ 
+-  ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
++  ifofile = malloc(sizeof(ifo_handle_t));
+   if(!ifofile)
+     return NULL;
+ 
+@@ -240,7 +244,7 @@
+ ifo_handle_t *ifoOpenVMGI(dvd_reader_t *dvd) {
+   ifo_handle_t *ifofile;
+ 
+-  ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
++  ifofile = malloc(sizeof(ifo_handle_t));
+   if(!ifofile)
+     return NULL;
+ 
+@@ -292,7 +296,7 @@
+ ifo_handle_t *ifoOpenVTSI(dvd_reader_t *dvd, int title) {
+   ifo_handle_t *ifofile;
+   
+-  ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
++  ifofile = malloc(sizeof(ifo_handle_t));
+   if(!ifofile)
+     return NULL;
+ 
+@@ -382,7 +386,7 @@
+ static int ifoRead_VMG(ifo_handle_t *ifofile) {
+   vmgi_mat_t *vmgi_mat;
+ 
+-  vmgi_mat = (vmgi_mat_t *)malloc(sizeof(vmgi_mat_t));
++  vmgi_mat = malloc(sizeof(vmgi_mat_t));
+   if(!vmgi_mat)
+     return 0;
+ 
+@@ -473,7 +477,7 @@
+   vtsi_mat_t *vtsi_mat;
+   int i;
+ 
+-  vtsi_mat = (vtsi_mat_t *)malloc(sizeof(vtsi_mat_t));
++  vtsi_mat = malloc(sizeof(vtsi_mat_t));
+   if(!vtsi_mat)
+     return 0;
+   
+@@ -606,7 +610,7 @@
+ 
+   if(cmd_tbl->nr_of_pre != 0) {
+     unsigned int pre_cmds_size  = cmd_tbl->nr_of_pre * COMMAND_DATA_SIZE;
+-    cmd_tbl->pre_cmds = (vm_cmd_t *)malloc(pre_cmds_size);
++    cmd_tbl->pre_cmds = malloc(pre_cmds_size);
+     if(!cmd_tbl->pre_cmds)
+       return 0;
+ 
+@@ -618,7 +622,7 @@
+ 
+   if(cmd_tbl->nr_of_post != 0) {
+     unsigned int post_cmds_size = cmd_tbl->nr_of_post * COMMAND_DATA_SIZE;
+-    cmd_tbl->post_cmds = (vm_cmd_t *)malloc(post_cmds_size);
++    cmd_tbl->post_cmds = malloc(post_cmds_size);
+     if(!cmd_tbl->post_cmds) {
+       if(cmd_tbl->pre_cmds) 
+         free(cmd_tbl->pre_cmds);
+@@ -634,7 +638,7 @@
+ 
+   if(cmd_tbl->nr_of_cell != 0) {
+     unsigned int cell_cmds_size = cmd_tbl->nr_of_cell * COMMAND_DATA_SIZE;
+-    cmd_tbl->cell_cmds = (vm_cmd_t *)malloc(cell_cmds_size);
++    cmd_tbl->cell_cmds = malloc(cell_cmds_size);
+     if(!cmd_tbl->cell_cmds) {
+       if(cmd_tbl->pre_cmds)
+         free(cmd_tbl->pre_cmds);
+@@ -751,10 +755,6 @@
+   B2N_16(pgc->cell_playback_offset);
+   B2N_16(pgc->cell_position_offset);
+ 
+-  for(i = 0; i < 8; i++)
+-    B2N_16(pgc->audio_control[i]);
+-  for(i = 0; i < 32; i++)
+-    B2N_32(pgc->subp_control[i]);
+   for(i = 0; i < 16; i++)
+     B2N_32(pgc->palette[i]);
+   
+@@ -763,10 +763,10 @@
+ 
+   /* verify time (look at print_time) */
+   for(i = 0; i < 8; i++)
+-    if(!pgc->audio_control[i] & 0x8000) /* The 'is present' bit */
++    if(!pgc->audio_control[i].present)
+       CHECK_ZERO(pgc->audio_control[i]);
+   for(i = 0; i < 32; i++)
+-    if(!pgc->subp_control[i] & 0x80000000) /* The 'is present' bit */
++    if(!pgc->subp_control[i].present)
+       CHECK_ZERO(pgc->subp_control[i]);
+   
+   /* Check that time is 0:0:0:0 also if nr_of_programs == 0 */
+@@ -880,7 +880,7 @@
+   if(ifofile->vmgi_mat->first_play_pgc == 0)
+     return 1;
+   
+-  ifofile->first_play_pgc = (pgc_t *)malloc(sizeof(pgc_t));
++  ifofile->first_play_pgc = malloc(sizeof(pgc_t));
+   if(!ifofile->first_play_pgc)
+     return 0;
+   
+@@ -934,7 +934,7 @@
+   if(!DVDFileSeek_(ifofile->file, ifofile->vmgi_mat->tt_srpt * DVD_BLOCK_LEN))
+     return 0;
+ 
+-  tt_srpt = (tt_srpt_t *)malloc(sizeof(tt_srpt_t));
++  tt_srpt = malloc(sizeof(tt_srpt_t));
+   if(!tt_srpt)
+     return 0;
+ 
+@@ -953,7 +953,7 @@
+   
+   info_length = tt_srpt->last_byte + 1 - TT_SRPT_SIZE;
+ 
+-  tt_srpt->title = (title_info_t *)malloc(info_length); 
++  tt_srpt->title = malloc(info_length);
+   if(!tt_srpt->title) {
+     free(tt_srpt);
+     ifofile->tt_srpt = 0;
+@@ -1040,7 +1040,7 @@
+                    ifofile->vtsi_mat->vts_ptt_srpt * DVD_BLOCK_LEN))
+     return 0;
+ 
+-  vts_ptt_srpt = (vts_ptt_srpt_t *)malloc(sizeof(vts_ptt_srpt_t));
++  vts_ptt_srpt = malloc(sizeof(vts_ptt_srpt_t));
+   if(!vts_ptt_srpt)
+     return 0;
+ 
+@@ -1063,7 +1063,7 @@
+   
+   info_length = vts_ptt_srpt->last_byte + 1 - VTS_PTT_SRPT_SIZE;
+   
+-  data = (uint32_t *)malloc(info_length); 
++  data = malloc(info_length);
+   if(!data) {
+     free(vts_ptt_srpt);
+     ifofile->vts_ptt_srpt = 0;
+@@ -1183,7 +1183,7 @@
+   if(!DVDFileSeek_(ifofile->file, ifofile->vmgi_mat->ptl_mait * DVD_BLOCK_LEN))
+     return 0;
+ 
+-  ptl_mait = (ptl_mait_t *)malloc(sizeof(ptl_mait_t));
++  ptl_mait = malloc(sizeof(ptl_mait_t));
+   if(!ptl_mait)
+     return 0;
+ 
+@@ -1207,7 +1207,7 @@
+               <= ptl_mait->last_byte + 1 - PTL_MAIT_SIZE);
+   
+   info_length = ptl_mait->nr_of_countries * sizeof(ptl_mait_country_t);
+-  ptl_mait->countries = (ptl_mait_country_t *)malloc(info_length);
++  ptl_mait->countries = malloc(info_length);
+   if(!ptl_mait->countries) {
+     free(ptl_mait);
+     ifofile->ptl_mait = 0;
+@@ -1252,7 +1252,7 @@
+       return 0;
+     }
+     info_length = (ptl_mait->nr_of_vtss + 1) * sizeof(pf_level_t);
+-    pf_temp = (uint16_t *)malloc(info_length);
++    pf_temp = malloc(info_length);
+     if(!pf_temp) {
+       for(j = 0; j < i ; j++) {
+         free(ptl_mait->countries[j].pf_ptl_mai);
+@@ -1276,7 +1276,7 @@
+     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);
++    ptl_mait->countries[i].pf_ptl_mai = malloc(info_length);
+     if(!ptl_mait->countries[i].pf_ptl_mai) {
+       free(pf_temp);
+       for(j = 0; j < i ; j++) {
+@@ -1340,7 +1340,7 @@
+   if(!DVDFileSeek_(ifofile->file, offset)) 
+     return 0;
+   
+-  vts_tmapt = (vts_tmapt_t *)malloc(sizeof(vts_tmapt_t));
++  vts_tmapt = malloc(sizeof(vts_tmapt_t));
+   if(!vts_tmapt)
+     return 0;
+   
+@@ -1362,7 +1362,7 @@
+   
+   info_length = vts_tmapt->nr_of_tmaps * 4;
+   
+-  vts_tmap_srp = (uint32_t *)malloc(info_length);
++  vts_tmap_srp = malloc(info_length);
+   if(!vts_tmap_srp) {
+     free(vts_tmapt);
+     ifofile->vts_tmapt = NULL;
+@@ -1388,7 +1388,7 @@
+   
+   info_length = vts_tmapt->nr_of_tmaps * sizeof(vts_tmap_t);
+   
+-  vts_tmapt->tmap = (vts_tmap_t *)malloc(info_length);
++  vts_tmapt->tmap = malloc(info_length);
+   if(!vts_tmapt->tmap) {
+     free(vts_tmap_srp);
+     free(vts_tmapt);
+@@ -1422,7 +1422,7 @@
+     
+     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);
++    vts_tmapt->tmap[i].map_ent = malloc(info_length);
+     if(!vts_tmapt->tmap[i].map_ent) {
+       ifoFree_VTS_TMAPT(ifofile);
+       return 0;
+@@ -1472,7 +1472,7 @@
+   if(ifofile->vtsi_mat->vts_c_adt == 0) /* mandatory */
+     return 0;
+ 
+-  ifofile->vts_c_adt = (c_adt_t *)malloc(sizeof(c_adt_t));
++  ifofile->vts_c_adt = malloc(sizeof(c_adt_t));
+   if(!ifofile->vts_c_adt)
+     return 0;
+ 
+@@ -1504,7 +1504,7 @@
+     return 0;
+   }
+   
+-  ifofile->menu_c_adt = (c_adt_t *)malloc(sizeof(c_adt_t));
++  ifofile->menu_c_adt = malloc(sizeof(c_adt_t));
+   if(!ifofile->menu_c_adt)
+     return 0;
+ 
+@@ -1548,7 +1548,7 @@
+     c_adt->nr_of_vobs = info_length / sizeof(cell_adr_t);
+   }
+   
+-  c_adt->cell_adr_table = (cell_adr_t *)malloc(info_length);
++  c_adt->cell_adr_table = malloc(info_length);
+   if(!c_adt->cell_adr_table)
+     return 0;
+ 
+@@ -1608,7 +1608,7 @@
+   if(ifofile->vtsi_mat->vts_vobu_admap == 0) /* mandatory */
+     return 0;
+   
+-  ifofile->vts_vobu_admap = (vobu_admap_t *)malloc(sizeof(vobu_admap_t));
++  ifofile->vts_vobu_admap = malloc(sizeof(vobu_admap_t));
+   if(!ifofile->vts_vobu_admap)
+     return 0;
+ 
+@@ -1640,7 +1640,7 @@
+     return 0;
+   }
+   
+-  ifofile->menu_vobu_admap = (vobu_admap_t *)malloc(sizeof(vobu_admap_t));
++  ifofile->menu_vobu_admap = malloc(sizeof(vobu_admap_t));
+   if(!ifofile->menu_vobu_admap)
+     return 0;
+   
+@@ -1673,7 +1673,7 @@
+      Titles with a VOBS that has no VOBUs. */
+   CHECK_VALUE(info_length % sizeof(uint32_t) == 0);
+   
+-  vobu_admap->vobu_start_sectors = (uint32_t *)malloc(info_length); 
++  vobu_admap->vobu_start_sectors = malloc(info_length);
+   if(!vobu_admap->vobu_start_sectors) {
+     return 0;
+   }
+@@ -1725,7 +1725,7 @@
+   if(ifofile->vtsi_mat->vts_pgcit == 0) /* mandatory */
+     return 0;
+   
+-  ifofile->vts_pgcit = (pgcit_t *)malloc(sizeof(pgcit_t));
++  ifofile->vts_pgcit = malloc(sizeof(pgcit_t));
+   if(!ifofile->vts_pgcit)
+     return 0;
+ 
+@@ -1860,7 +1860,7 @@
+     return 0;
+   }
+   
+-  ifofile->pgci_ut = (pgci_ut_t *)malloc(sizeof(pgci_ut_t));
++  ifofile->pgci_ut = malloc(sizeof(pgci_ut_t));
+   if(!ifofile->pgci_ut)
+     return 0;
+   
+@@ -2050,7 +2050,7 @@
+   if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN))
+     return 0;
+ 
+-  vts_atrt = (vts_atrt_t *)malloc(sizeof(vts_atrt_t));
++  vts_atrt = malloc(sizeof(vts_atrt_t));
+   if(!vts_atrt)
+     return 0;
+ 
+@@ -2072,7 +2072,7 @@
+               VTS_ATRT_SIZE < vts_atrt->last_byte + 1);
+ 
+   info_length = vts_atrt->nr_of_vtss * sizeof(uint32_t);
+-  data = (uint32_t *)malloc(info_length);
++  data = malloc(info_length);
+   if(!data) {
+     free(vts_atrt);
+     ifofile->vts_atrt = 0;
+@@ -2094,7 +2094,7 @@
+   }
+   
+   info_length = vts_atrt->nr_of_vtss * sizeof(vts_attributes_t);
+-  vts_atrt->vts = (vts_attributes_t *)malloc(info_length);
++  vts_atrt->vts = malloc(info_length);
+   if(!vts_atrt->vts) {
+     free(data);
+     free(vts_atrt);
+@@ -2150,7 +2150,7 @@
+                    ifofile->vmgi_mat->txtdt_mgi * DVD_BLOCK_LEN))
+     return 0;
+   
+-  txtdt_mgi = (txtdt_mgi_t *)malloc(sizeof(txtdt_mgi_t));
++  txtdt_mgi = malloc(sizeof(txtdt_mgi_t));
+   if(!txtdt_mgi) {
+     return 0;
+   }
+
+--- dvdread.orig/ifo_types.h	2005-06-23 00:18:54.000000000 +0200
++++ dvdread/ifo_types.h	2005-06-23 00:19:10.000000000 +0200
+@@ -403,6 +407,55 @@
+ } ATTRIBUTE_PACKED user_ops_t;
+ 
+ /**
++ * Subpicture stream mapping for a subtitle
++ */
++typedef struct {
++#ifdef WORDS_BIGENDIAN
++  unsigned int present   : 1;
++  unsigned int zero1     : 2;
++  unsigned int s_4p3     : 5; /* stream for 4:3 on any display */
++
++  unsigned int zero2     : 3;
++  unsigned int s_wide    : 5; /* stream for 16:9 on widescreen display */
++
++  unsigned int zero3     : 3;
++  unsigned int s_lbox    : 5; /* stream for 16:9 on letterboxed 4:3 display */
++
++  unsigned int zero4     : 3;
++  unsigned int s_panscan : 5; /* stream for 16:9 with pan&scan data on 4:3 display */
++#else
++  unsigned int s_4p3     : 5; /* stream for 4:3 on any display */
++  unsigned int zero1     : 2;
++  unsigned int present   : 1;
++
++  unsigned int s_wide    : 5; /* stream for 16:9 on widescreen display */
++  unsigned int zero2     : 3;
++
++  unsigned int s_lbox    : 5; /* stream for 16:9 on letterboxed 4:3 display */
++  unsigned int zero3     : 3;
++
++  unsigned int s_panscan : 5; /* stream for 16:9 with pan&scan data on 4:3 display */
++  unsigned int zero4     : 3;
++#endif
++} ATTRIBUTE_PACKED subp_mapping_t;
++
++/**
++ * Audio stream mapping for a soundtrack
++ */
++typedef struct {
++#ifdef WORDS_BIGENDIAN
++  unsigned int present : 1;
++  unsigned int zero1   : 4;
++  unsigned int s_audio : 3;
++#else
++  unsigned int s_audio : 3;
++  unsigned int zero1   : 4;
++  unsigned int present : 1;
++#endif
++  uint8_t zero2;
++} ATTRIBUTE_PACKED audio_mapping_t;
++
++/**
+  * Program Chain Information.
+  */
+ typedef struct {
+@@ -411,8 +464,8 @@
+   uint8_t  nr_of_cells;
+   dvd_time_t playback_time;
+   user_ops_t prohibited_ops;
+-  uint16_t audio_control[8]; /* New type? */
+-  uint32_t subp_control[32]; /* New type? */
++  audio_mapping_t audio_control[8];
++  subp_mapping_t subp_control[32];
+   uint16_t next_pgc_nr;
+   uint16_t prev_pgc_nr;
+   uint16_t goup_pgc_nr;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/md5.c	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,418 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/* md5.c - Functions to compute MD5 message digest of files or memory blocks
+   according to the definition of MD5 in RFC 1321 from April 1992.
+   Copyright (C) 1995, 1996, 2001 Free Software Foundation, Inc.
+   NOTE: The canonical source of this file is maintained with the GNU C
+   Library.  Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+   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, or (at your option) any
+   later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+
+#if STDC_HEADERS || defined _LIBC
+# include <stdlib.h>
+# include <string.h>
+#else
+# ifndef HAVE_MEMCPY
+#  define memcpy(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#include "md5.h"
+//#include "unlocked-io.h"
+
+#ifdef _LIBC
+# include <endian.h>
+# if __BYTE_ORDER == __BIG_ENDIAN
+#  define WORDS_BIGENDIAN 1
+# endif
+#endif
+
+#ifdef WORDS_BIGENDIAN
+# define SWAP(n)                                                        \
+  (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+#else
+# define SWAP(n) (n)
+#endif
+
+
+/* This array contains the bytes used to pad the buffer to the next
+   64-byte boundary.  (RFC 1321, 3.1: Step 1)  */
+static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ...  */ };
+
+
+/* Initialize structure containing state of computation.
+   (RFC 1321, 3.3: Step 3)  */
+void
+md5_init_ctx (ctx)
+     struct md5_ctx *ctx;
+{
+  ctx->A = 0x67452301;
+  ctx->B = 0xefcdab89;
+  ctx->C = 0x98badcfe;
+  ctx->D = 0x10325476;
+
+  ctx->total[0] = ctx->total[1] = 0;
+  ctx->buflen = 0;
+}
+
+/* Put result from CTX in first 16 bytes following RESBUF.  The result
+   must be in little endian byte order.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+void *
+md5_read_ctx (ctx, resbuf)
+     const struct md5_ctx *ctx;
+     void *resbuf;
+{
+  ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A);
+  ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B);
+  ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C);
+  ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D);
+
+  return resbuf;
+}
+
+/* Process the remaining bytes in the internal buffer and the usual
+   prolog according to the standard and write the result to RESBUF.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+void *
+md5_finish_ctx (ctx, resbuf)
+     struct md5_ctx *ctx;
+     void *resbuf;
+{
+  /* Take yet unprocessed bytes into account.  */
+  md5_uint32 bytes = ctx->buflen;
+  size_t pad;
+
+  /* Now count remaining bytes.  */
+  ctx->total[0] += bytes;
+  if (ctx->total[0] < bytes)
+    ++ctx->total[1];
+
+  pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
+  memcpy (&ctx->buffer[bytes], fillbuf, pad);
+
+  /* Put the 64-bit file length in *bits* at the end of the buffer.  */
+  *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3);
+  *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) |
+                                                        (ctx->total[0] >> 29));
+
+  /* Process last bytes.  */
+  md5_process_block (ctx->buffer, bytes + pad + 8, ctx);
+
+  return md5_read_ctx (ctx, resbuf);
+}
+
+/* Compute MD5 message digest for bytes read from STREAM.  The
+   resulting message digest number will be written into the 16 bytes
+   beginning at RESBLOCK.  */
+int
+md5_stream (stream, resblock)
+     FILE *stream;
+     void *resblock;
+{
+  /* Important: BLOCKSIZE must be a multiple of 64.  */
+#define BLOCKSIZE 4096
+  struct md5_ctx ctx;
+  char buffer[BLOCKSIZE + 72];
+  size_t sum;
+
+  /* Initialize the computation context.  */
+  md5_init_ctx (&ctx);
+
+  /* Iterate over full file contents.  */
+  while (1)
+    {
+      /* We read the file in blocks of BLOCKSIZE bytes.  One call of the
+         computation function processes the whole buffer so that with the
+         next round of the loop another block can be read.  */
+      size_t n;
+      sum = 0;
+
+      /* Read block.  Take care for partial reads.  */
+      do
+        {
+          n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);
+
+          sum += n;
+        }
+      while (sum < BLOCKSIZE && n != 0);
+      if (n == 0 && ferror (stream))
+        return 1;
+
+      /* If end of file is reached, end the loop.  */
+      if (n == 0)
+        break;
+
+      /* Process buffer with BLOCKSIZE bytes.  Note that
+         BLOCKSIZE % 64 == 0
+      */
+      md5_process_block (buffer, BLOCKSIZE, &ctx);
+    }
+
+  /* Add the last bytes if necessary.  */
+  if (sum > 0)
+    md5_process_bytes (buffer, sum, &ctx);
+
+  /* Construct result in desired memory.  */
+  md5_finish_ctx (&ctx, resblock);
+  return 0;
+}
+
+/* Compute MD5 message digest for LEN bytes beginning at BUFFER.  The
+   result is always in little endian byte order, so that a byte-wise
+   output yields to the wanted ASCII representation of the message
+   digest.  */
+void *
+md5_buffer (buffer, len, resblock)
+     const char *buffer;
+     size_t len;
+     void *resblock;
+{
+  struct md5_ctx ctx;
+
+  /* Initialize the computation context.  */
+  md5_init_ctx (&ctx);
+
+  /* Process whole buffer but last len % 64 bytes.  */
+  md5_process_bytes (buffer, len, &ctx);
+
+  /* Put result in desired memory area.  */
+  return md5_finish_ctx (&ctx, resblock);
+}
+
+
+void
+md5_process_bytes (buffer, len, ctx)
+     const void *buffer;
+     size_t len;
+     struct md5_ctx *ctx;
+{
+  /* When we already have some bits in our internal buffer concatenate
+     both inputs first.  */
+  if (ctx->buflen != 0)
+    {
+      size_t left_over = ctx->buflen;
+      size_t add = 128 - left_over > len ? len : 128 - left_over;
+
+      memcpy (&ctx->buffer[left_over], buffer, add);
+      ctx->buflen += add;
+
+      if (left_over + add > 64)
+        {
+          md5_process_block (ctx->buffer, (left_over + add) & ~63, ctx);
+          /* The regions in the following copy operation cannot overlap.  */
+          memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
+                  (left_over + add) & 63);
+          ctx->buflen = (left_over + add) & 63;
+        }
+
+      buffer = (const char *) buffer + add;
+      len -= add;
+    }
+
+  /* Process available complete blocks.  */
+  if (len > 64)
+    {
+      md5_process_block (buffer, len & ~63, ctx);
+      buffer = (const char *) buffer + (len & ~63);
+      len &= 63;
+    }
+
+  /* Move remaining bytes in internal buffer.  */
+  if (len > 0)
+    {
+      memcpy (ctx->buffer, buffer, len);
+      ctx->buflen = len;
+    }
+}
+
+
+/* These are the four functions used in the four steps of the MD5 algorithm
+   and defined in the RFC 1321.  The first function is a little bit optimized
+   (as found in Colin Plumbs public domain implementation).  */
+/* #define FF(b, c, d) ((b & c) | (~b & d)) */
+#define FF(b, c, d) (d ^ (b & (c ^ d)))
+#define FG(b, c, d) FF (d, b, c)
+#define FH(b, c, d) (b ^ c ^ d)
+#define FI(b, c, d) (c ^ (b | ~d))
+
+/* Process LEN bytes of BUFFER, accumulating context into CTX.
+   It is assumed that LEN % 64 == 0.  */
+
+void
+md5_process_block (buffer, len, ctx)
+     const void *buffer;
+     size_t len;
+     struct md5_ctx *ctx;
+{
+  md5_uint32 correct_words[16];
+  const md5_uint32 *words = buffer;
+  size_t nwords = len / sizeof (md5_uint32);
+  const md5_uint32 *endp = words + nwords;
+  md5_uint32 A = ctx->A;
+  md5_uint32 B = ctx->B;
+  md5_uint32 C = ctx->C;
+  md5_uint32 D = ctx->D;
+
+  /* First increment the byte count.  RFC 1321 specifies the possible
+     length of the file up to 2^64 bits.  Here we only compute the
+     number of bytes.  Do a double word increment.  */
+  ctx->total[0] += len;
+  if (ctx->total[0] < len)
+    ++ctx->total[1];
+
+  /* Process all bytes in the buffer with 64 bytes in each round of
+     the loop.  */
+  while (words < endp)
+    {
+      md5_uint32 *cwp = correct_words;
+      md5_uint32 A_save = A;
+      md5_uint32 B_save = B;
+      md5_uint32 C_save = C;
+      md5_uint32 D_save = D;
+
+      /* First round: using the given function, the context and a constant
+         the next context is computed.  Because the algorithms processing
+         unit is a 32-bit word and it is determined to work on words in
+         little endian byte order we perhaps have to change the byte order
+         before the computation.  To reduce the work for the next steps
+         we store the swapped words in the array CORRECT_WORDS.  */
+
+#define OP(a, b, c, d, s, T)                                    \
+      do                                                        \
+        {                                                       \
+          a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T;     \
+          ++words;                                              \
+          a = rol (a, s);                                       \
+          a += b;                                               \
+        }                                                       \
+      while (0)
+
+      /* Before we start, one word to the strange constants.
+         They are defined in RFC 1321 as
+
+         T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64, or
+         perl -e 'foreach(1..64){printf "0x%08x\n", int (4294967296 * abs (sin $_))}'
+      */
+
+      /* Round 1.  */
+      OP (A, B, C, D,  7, 0xd76aa478);
+      OP (D, A, B, C, 12, 0xe8c7b756);
+      OP (C, D, A, B, 17, 0x242070db);
+      OP (B, C, D, A, 22, 0xc1bdceee);
+      OP (A, B, C, D,  7, 0xf57c0faf);
+      OP (D, A, B, C, 12, 0x4787c62a);
+      OP (C, D, A, B, 17, 0xa8304613);
+      OP (B, C, D, A, 22, 0xfd469501);
+      OP (A, B, C, D,  7, 0x698098d8);
+      OP (D, A, B, C, 12, 0x8b44f7af);
+      OP (C, D, A, B, 17, 0xffff5bb1);
+      OP (B, C, D, A, 22, 0x895cd7be);
+      OP (A, B, C, D,  7, 0x6b901122);
+      OP (D, A, B, C, 12, 0xfd987193);
+      OP (C, D, A, B, 17, 0xa679438e);
+      OP (B, C, D, A, 22, 0x49b40821);
+
+      /* For the second to fourth round we have the possibly swapped words
+         in CORRECT_WORDS.  Redefine the macro to take an additional first
+         argument specifying the function to use.  */
+#undef OP
+#define OP(f, a, b, c, d, k, s, T)                      \
+      do                                                \
+        {                                               \
+          a += f (b, c, d) + correct_words[k] + T;      \
+          a = rol (a, s);                               \
+          a += b;                                       \
+        }                                               \
+      while (0)
+
+      /* Round 2.  */
+      OP (FG, A, B, C, D,  1,  5, 0xf61e2562);
+      OP (FG, D, A, B, C,  6,  9, 0xc040b340);
+      OP (FG, C, D, A, B, 11, 14, 0x265e5a51);
+      OP (FG, B, C, D, A,  0, 20, 0xe9b6c7aa);
+      OP (FG, A, B, C, D,  5,  5, 0xd62f105d);
+      OP (FG, D, A, B, C, 10,  9, 0x02441453);
+      OP (FG, C, D, A, B, 15, 14, 0xd8a1e681);
+      OP (FG, B, C, D, A,  4, 20, 0xe7d3fbc8);
+      OP (FG, A, B, C, D,  9,  5, 0x21e1cde6);
+      OP (FG, D, A, B, C, 14,  9, 0xc33707d6);
+      OP (FG, C, D, A, B,  3, 14, 0xf4d50d87);
+      OP (FG, B, C, D, A,  8, 20, 0x455a14ed);
+      OP (FG, A, B, C, D, 13,  5, 0xa9e3e905);
+      OP (FG, D, A, B, C,  2,  9, 0xfcefa3f8);
+      OP (FG, C, D, A, B,  7, 14, 0x676f02d9);
+      OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
+
+      /* Round 3.  */
+      OP (FH, A, B, C, D,  5,  4, 0xfffa3942);
+      OP (FH, D, A, B, C,  8, 11, 0x8771f681);
+      OP (FH, C, D, A, B, 11, 16, 0x6d9d6122);
+      OP (FH, B, C, D, A, 14, 23, 0xfde5380c);
+      OP (FH, A, B, C, D,  1,  4, 0xa4beea44);
+      OP (FH, D, A, B, C,  4, 11, 0x4bdecfa9);
+      OP (FH, C, D, A, B,  7, 16, 0xf6bb4b60);
+      OP (FH, B, C, D, A, 10, 23, 0xbebfbc70);
+      OP (FH, A, B, C, D, 13,  4, 0x289b7ec6);
+      OP (FH, D, A, B, C,  0, 11, 0xeaa127fa);
+      OP (FH, C, D, A, B,  3, 16, 0xd4ef3085);
+      OP (FH, B, C, D, A,  6, 23, 0x04881d05);
+      OP (FH, A, B, C, D,  9,  4, 0xd9d4d039);
+      OP (FH, D, A, B, C, 12, 11, 0xe6db99e5);
+      OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8);
+      OP (FH, B, C, D, A,  2, 23, 0xc4ac5665);
+
+      /* Round 4.  */
+      OP (FI, A, B, C, D,  0,  6, 0xf4292244);
+      OP (FI, D, A, B, C,  7, 10, 0x432aff97);
+      OP (FI, C, D, A, B, 14, 15, 0xab9423a7);
+      OP (FI, B, C, D, A,  5, 21, 0xfc93a039);
+      OP (FI, A, B, C, D, 12,  6, 0x655b59c3);
+      OP (FI, D, A, B, C,  3, 10, 0x8f0ccc92);
+      OP (FI, C, D, A, B, 10, 15, 0xffeff47d);
+      OP (FI, B, C, D, A,  1, 21, 0x85845dd1);
+      OP (FI, A, B, C, D,  8,  6, 0x6fa87e4f);
+      OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
+      OP (FI, C, D, A, B,  6, 15, 0xa3014314);
+      OP (FI, B, C, D, A, 13, 21, 0x4e0811a1);
+      OP (FI, A, B, C, D,  4,  6, 0xf7537e82);
+      OP (FI, D, A, B, C, 11, 10, 0xbd3af235);
+      OP (FI, C, D, A, B,  2, 15, 0x2ad7d2bb);
+      OP (FI, B, C, D, A,  9, 21, 0xeb86d391);
+
+      /* Add the starting values of the context.  */
+      A += A_save;
+      B += B_save;
+      C += C_save;
+      D += D_save;
+    }
+
+  /* Put checksum in context given as argument.  */
+  ctx->A = A;
+  ctx->B = B;
+  ctx->C = C;
+  ctx->D = D;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/md5.h	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,162 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/* md5.h - Declaration of functions and data types used for MD5 sum
+   computing library functions.
+   Copyright (C) 1995, 1996, 1999 Free Software Foundation, Inc.
+   NOTE: The canonical source of this file is maintained with the GNU C
+   Library.  Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+   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, or (at your option) any
+   later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifndef _MD5_H
+#define _MD5_H 1
+
+#include <stdio.h>
+
+#if defined HAVE_LIMITS_H || _LIBC
+# include <limits.h>
+#endif
+
+/* The following contortions are an attempt to use the C preprocessor
+   to determine an unsigned integral type that is 32 bits wide.  An
+   alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but
+   doing that would require that the configure script compile and *run*
+   the resulting executable.  Locally running cross-compiled executables
+   is usually not possible.  */
+
+#ifdef _LIBC
+# include <sys/types.h>
+typedef u_int32_t md5_uint32;
+#else
+# if defined __STDC__ && __STDC__
+#  define UINT_MAX_32_BITS 4294967295U
+# else
+#  define UINT_MAX_32_BITS 0xFFFFFFFF
+# endif
+
+/* If UINT_MAX isn't defined, assume it's a 32-bit type.
+   This should be valid for all systems GNU cares about because
+   that doesn't include 16-bit systems, and only modern systems
+   (that certainly have <limits.h>) have 64+-bit integral types.  */
+
+# ifndef UINT_MAX
+#  define UINT_MAX UINT_MAX_32_BITS
+# endif
+
+# if UINT_MAX == UINT_MAX_32_BITS
+typedef unsigned int md5_uint32;
+# else
+#  if USHRT_MAX == UINT_MAX_32_BITS
+typedef unsigned short md5_uint32;
+#  else
+#   if ULONG_MAX == UINT_MAX_32_BITS
+typedef unsigned long md5_uint32;
+#   else
+/* The following line is intended to evoke an error.
+   Using #error is not portable enough.  */
+"Cannot determine unsigned 32-bit data type."
+#   endif
+#  endif
+# endif
+#endif
+
+#undef __P
+#if defined (__STDC__) && __STDC__
+#define __P(x) x
+#else
+#define __P(x) ()
+#endif
+
+/* Structure to save state of computation between the single steps.  */
+struct md5_ctx
+{
+  md5_uint32 A;
+  md5_uint32 B;
+  md5_uint32 C;
+  md5_uint32 D;
+
+  md5_uint32 total[2];
+  md5_uint32 buflen;
+  char buffer[128];
+};
+
+/*
+ * The following three functions are build up the low level used in
+ * the functions `md5_stream' and `md5_buffer'.
+ */
+
+/* Initialize structure containing state of computation.
+   (RFC 1321, 3.3: Step 3)  */
+extern void md5_init_ctx __P ((struct md5_ctx *ctx));
+
+/* Starting with the result of former calls of this function (or the
+   initialization function update the context for the next LEN bytes
+   starting at BUFFER.
+   It is necessary that LEN is a multiple of 64!!! */
+extern void md5_process_block __P ((const void *buffer, size_t len,
+                                    struct md5_ctx *ctx));
+
+/* Starting with the result of former calls of this function (or the
+   initialization function update the context for the next LEN bytes
+   starting at BUFFER.
+   It is NOT required that LEN is a multiple of 64.  */
+extern void md5_process_bytes __P ((const void *buffer, size_t len,
+                                    struct md5_ctx *ctx));
+
+/* Process the remaining bytes in the buffer and put result from CTX
+   in first 16 bytes following RESBUF.  The result is always in little
+   endian byte order, so that a byte-wise output yields to the wanted
+   ASCII representation of the message digest.
+
+   IMPORTANT: On some systems it is required that RESBUF be correctly
+   aligned for a 32 bits value.  */
+extern void *md5_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf));
+
+
+/* Put result from CTX in first 16 bytes following RESBUF.  The result is
+   always in little endian byte order, so that a byte-wise output yields
+   to the wanted ASCII representation of the message digest.
+
+   IMPORTANT: On some systems it is required that RESBUF is correctly
+   aligned for a 32 bits value.  */
+extern void *md5_read_ctx __P ((const struct md5_ctx *ctx, void *resbuf));
+
+
+/* Compute MD5 message digest for bytes read from STREAM.  The
+   resulting message digest number will be written into the 16 bytes
+   beginning at RESBLOCK.  */
+extern int md5_stream __P ((FILE *stream, void *resblock));
+
+/* Compute MD5 message digest for LEN bytes beginning at BUFFER.  The
+   result is always in little endian byte order, so that a byte-wise
+   output yields to the wanted ASCII representation of the message
+   digest.  */
+extern void *md5_buffer __P ((const char *buffer, size_t len, void *resblock));
+
+/* The following is from gnupg-1.0.2's cipher/bithelp.h.  */
+/* Rotate a 32 bit integer by n bytes */
+#if defined __GNUC__ && defined __i386__
+static inline md5_uint32
+rol(md5_uint32 x, int n)
+{
+  __asm__("roll %%cl,%0"
+          :"=r" (x)
+          :"0" (x),"c" (n));
+  return x;
+}
+#else
+# define rol(x,n) ( ((x) << (n)) | ((x) >> (32-(n))) )
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/nav_print.c	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,286 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2000, 2001, 2002, 2003 Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * Much of the contents in this file is based on VOBDUMP.
+ *
+ * VOBDUMP: a program for examining DVD .VOB filse
+ *
+ * Copyright 1998, 1999 Eric Smith <eric@brouhaha.com>
+ *
+ * VOBDUMP is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.  Note that I am not
+ * granting permission to redistribute or modify VOBDUMP under the
+ * terms of any later version of the General Public License.
+ *
+ * This program is distributed in the hope that it will be useful (or
+ * at least amusing), but WITHOUT ANY WARRANTY; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#if defined(HAVE_INTTYPES_H)
+#include <inttypes.h>
+#elif defined(HAVE_STDINT_H)
+#include <stdint.h>
+#endif
+
+#include "nav_types.h"
+#include "nav_print.h"
+#include "cmd_print.h"
+#include "dvdread_internal.h"
+
+static void print_time(dvd_time_t *dtime) {
+  const char *rate;
+  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,
+         dtime->minute,
+         dtime->second,
+         dtime->frame_u & 0x3f);
+  switch((dtime->frame_u & 0xc0) >> 6) {
+  case 1:
+    rate = "25.00";
+    break;
+  case 3:
+    rate = "29.97";
+    break;
+  default:
+    rate = "(please send a bug report)";
+    break;
+  } 
+  printf(" @ %s fps", rate);
+}
+
+
+static void navPrint_PCI_GI(pci_gi_t *pci_gi) {
+  int i;
+
+  printf("pci_gi:\n");
+  printf("nv_pck_lbn    0x%08x\n", pci_gi->nv_pck_lbn);
+  printf("vobu_cat      0x%04x\n", pci_gi->vobu_cat);
+  printf("vobu_uop_ctl  0x%08x\n", *(uint32_t*)&pci_gi->vobu_uop_ctl);
+  printf("vobu_s_ptm    0x%08x\n", pci_gi->vobu_s_ptm);
+  printf("vobu_e_ptm    0x%08x\n", pci_gi->vobu_e_ptm);
+  printf("vobu_se_e_ptm 0x%08x\n", pci_gi->vobu_se_e_ptm);
+  printf("e_eltm        ");
+  print_time(&pci_gi->e_eltm);
+  printf("\n");
+  
+  printf("vobu_isrc     \"");
+  for(i = 0; i < 32; i++) {
+    char c = pci_gi->vobu_isrc[i];
+    if((c >= ' ') && (c <= '~'))
+      printf("%c", c);
+    else
+      printf(".");
+  }
+  printf("\"\n");
+}
+
+static void navPrint_NSML_AGLI(nsml_agli_t *nsml_agli) {
+  int i, j = 0;
+  
+  for(i = 0; i < 9; i++)
+    j |= nsml_agli->nsml_agl_dsta[i];
+  if(j == 0)
+    return;
+  
+  printf("nsml_agli:\n");
+  for(i = 0; i < 9; i++)
+    if(nsml_agli->nsml_agl_dsta[i])
+      printf("nsml_agl_c%d_dsta  0x%08x\n", i + 1, 
+             nsml_agli->nsml_agl_dsta[i]);
+}
+
+static void navPrint_HL_GI(hl_gi_t *hl_gi, int *btngr_ns, int *btn_ns) {
+  
+  if((hl_gi->hli_ss & 0x03) == 0)
+    return;
+  
+  printf("hl_gi:\n");
+  printf("hli_ss        0x%01x\n", hl_gi->hli_ss & 0x03);
+  printf("hli_s_ptm     0x%08x\n", hl_gi->hli_s_ptm);
+  printf("hli_e_ptm     0x%08x\n", hl_gi->hli_e_ptm);
+  printf("btn_se_e_ptm  0x%08x\n", hl_gi->btn_se_e_ptm);
+
+  *btngr_ns = hl_gi->btngr_ns;
+  printf("btngr_ns      %d\n",  hl_gi->btngr_ns);
+  printf("btngr%d_dsp_ty    0x%02x\n", 1, hl_gi->btngr1_dsp_ty);
+  printf("btngr%d_dsp_ty    0x%02x\n", 2, hl_gi->btngr2_dsp_ty);
+  printf("btngr%d_dsp_ty    0x%02x\n", 3, hl_gi->btngr3_dsp_ty);
+  
+  printf("btn_ofn       %d\n", hl_gi->btn_ofn);
+  *btn_ns = hl_gi->btn_ns;
+  printf("btn_ns        %d\n", hl_gi->btn_ns);
+  printf("nsl_btn_ns    %d\n", hl_gi->nsl_btn_ns);
+  printf("fosl_btnn     %d\n", hl_gi->fosl_btnn);
+  printf("foac_btnn     %d\n", hl_gi->foac_btnn);
+}
+
+static void navPrint_BTN_COLIT(btn_colit_t *btn_colit) {
+  int i, j;
+  
+  j = 0;
+  for(i = 0; i < 6; i++)
+    j |= btn_colit->btn_coli[i/2][i&1];
+  if(j == 0)
+    return;
+  
+  printf("btn_colit:\n");
+  for(i = 0; i < 3; i++)
+    for(j = 0; j < 2; j++)
+      printf("btn_cqoli %d  %s_coli:  %08x\n",
+             i, (j == 0) ? "sl" : "ac",
+             btn_colit->btn_coli[i][j]);
+}
+
+static void navPrint_BTNIT(btni_t *btni_table, int btngr_ns, int btn_ns) {
+  int i, j;
+  
+  printf("btnit:\n");
+  printf("btngr_ns: %i\n", btngr_ns);
+  printf("btn_ns: %i\n", btn_ns);
+  
+  if(btngr_ns == 0)
+    return;
+  
+  for(i = 0; i < btngr_ns; i++) {
+    for(j = 0; j < (36 / btngr_ns); j++) {
+      if(j < btn_ns) {
+        btni_t *btni = &btni_table[(36 / btngr_ns) * i + j];
+        
+        printf("group %d btni %d:  ", i+1, j+1);
+        printf("btn_coln %d, auto_action_mode %d\n",
+               btni->btn_coln, btni->auto_action_mode);
+        printf("coords   (%d, %d) .. (%d, %d)\n",
+               btni->x_start, btni->y_start, btni->x_end, btni->y_end);
+        
+        printf("up %d, ", btni->up);
+        printf("down %d, ", btni->down);
+        printf("left %d, ", btni->left);
+        printf("right %d\n", btni->right);
+        
+        cmdPrint_CMD(0, &btni->cmd);
+        printf("\n");
+      }
+    }
+  }
+}
+
+static void navPrint_HLI(hli_t *hli) {
+  int btngr_ns = 0, btn_ns = 0;
+  
+  printf("hli:\n");
+  navPrint_HL_GI(&hli->hl_gi, & btngr_ns, & btn_ns);
+  navPrint_BTN_COLIT(&hli->btn_colit);
+  navPrint_BTNIT(hli->btnit, btngr_ns, btn_ns);
+}
+
+void navPrint_PCI(pci_t *pci) {
+  printf("pci packet:\n");
+  navPrint_PCI_GI(&pci->pci_gi);
+  navPrint_NSML_AGLI(&pci->nsml_agli);
+  navPrint_HLI(&pci->hli);
+}
+
+static void navPrint_DSI_GI(dsi_gi_t *dsi_gi) {
+  printf("dsi_gi:\n");
+  printf("nv_pck_scr     0x%08x\n", dsi_gi->nv_pck_scr);
+  printf("nv_pck_lbn     0x%08x\n", dsi_gi->nv_pck_lbn );
+  printf("vobu_ea        0x%08x\n", dsi_gi->vobu_ea);
+  printf("vobu_1stref_ea 0x%08x\n", dsi_gi->vobu_1stref_ea);
+  printf("vobu_2ndref_ea 0x%08x\n", dsi_gi->vobu_2ndref_ea);
+  printf("vobu_3rdref_ea 0x%08x\n", dsi_gi->vobu_3rdref_ea);
+  printf("vobu_vob_idn   0x%04x\n", dsi_gi->vobu_vob_idn);
+  printf("vobu_c_idn     0x%02x\n", dsi_gi->vobu_c_idn);
+  printf("c_eltm         ");
+  print_time(&dsi_gi->c_eltm);
+  printf("\n");
+}
+
+static void navPrint_SML_PBI(sml_pbi_t *sml_pbi) {
+  printf("sml_pbi:\n");
+  printf("category 0x%04x\n", sml_pbi->category);
+  if(sml_pbi->category & 0x8000)
+    printf("VOBU is in preunit\n");
+  if(sml_pbi->category & 0x4000)
+    printf("VOBU is in ILVU\n");
+  if(sml_pbi->category & 0x2000)
+    printf("VOBU at the beginning of ILVU\n");
+  if(sml_pbi->category & 0x1000)
+    printf("VOBU at end of PREU of ILVU\n");
+  
+  printf("ilvu_ea       0x%08x\n", sml_pbi->ilvu_ea);
+  printf("nxt_ilvu_sa   0x%08x\n", sml_pbi->ilvu_sa);
+  printf("nxt_ilvu_size 0x%04x\n", sml_pbi->size);
+  
+  printf("vob_v_s_s_ptm 0x%08x\n", sml_pbi->vob_v_s_s_ptm);
+  printf("vob_v_e_e_ptm 0x%08x\n", sml_pbi->vob_v_e_e_ptm);
+  
+  /* $$$ more code needed here */
+}
+
+static void navPrint_SML_AGLI(sml_agli_t *sml_agli) {
+  int i;
+  printf("sml_agli:\n");
+  for(i = 0; i < 9; i++) {
+    printf("agl_c%d address: 0x%08x size 0x%04x\n", i,
+           sml_agli->data[i].address, sml_agli->data[i].size);
+  }
+}
+
+static void navPrint_VOBU_SRI(vobu_sri_t *vobu_sri) {
+  int i;
+  int stime[19] = { 240, 120, 60, 20, 15, 14, 13, 12, 11, 
+                    10,   9,  8,  7,  6,  5,  4,  3,  2, 1};
+  printf("vobu_sri:\n");
+  printf("Next VOBU with Video %08x\n", vobu_sri->next_video);
+  for(i = 0; i < 19; i++) {
+    printf("%3.1f %08x ", stime[i]/2.0, vobu_sri->fwda[i]);
+  }
+  printf("\n");
+  printf("Next VOBU %08x\n", vobu_sri->next_vobu);
+  printf("--\n");
+  printf("Prev VOBU %08x\n", vobu_sri->prev_vobu);
+  for(i = 0; i < 19; i++) {
+    printf("%3.1f %08x ", stime[18 - i]/2.0, vobu_sri->bwda[i]);
+  }
+  printf("\n");
+  printf("Prev VOBU with Video %08x\n", vobu_sri->prev_video);
+}
+
+static void navPrint_SYNCI(synci_t *synci) {
+  int i;
+  
+  printf("synci:\n");
+  /* $$$ more code needed here */
+  for(i = 0; i < 8; i++)
+    printf("%04x ", synci->a_synca[i]);
+  for(i = 0; i < 32; i++)
+    printf("%08x ", synci->sp_synca[i]);
+}
+
+void navPrint_DSI(dsi_t *dsi) {
+  printf("dsi packet:\n");
+  navPrint_DSI_GI(&dsi->dsi_gi);
+  navPrint_SML_PBI(&dsi->sml_pbi);
+  navPrint_SML_AGLI(&dsi->sml_agli);
+  navPrint_VOBU_SRI(&dsi->vobu_sri);
+  navPrint_SYNCI(&dsi->synci);
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/nav_print.h	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,51 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+#ifndef NAV_PRINT_H_INCLUDED
+#define NAV_PRINT_H_INCLUDED
+
+/*
+ * Copyright (C) 2001, 2002 Billy Biggs <vektor@dumbterm.net>,
+ *                          Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <libdvdread/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 *);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* NAV_PRINT_H_INCLUDED */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/nav_read.c	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,331 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+/*
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#if defined(HAVE_INTTYPES_H)
+#include <inttypes.h>
+#elif defined(HAVE_STDINT_H)
+#include <stdint.h>
+#endif
+
+#include "bswap.h"
+#include "nav_types.h"
+#include "nav_read.h"
+#include "dvdread_internal.h"
+
+typedef struct {
+  uint8_t *start;
+  uint32_t byte_position;
+  uint32_t bit_position;
+  uint8_t byte;
+} getbits_state_t;
+
+static int getbits_init(getbits_state_t *state, uint8_t *start) {
+  if ((state == NULL) || (start == NULL)) return 0;
+  state->start = start;
+  state->bit_position = 0;
+  state->byte_position = 0;
+  state->byte = start[0];
+  return 1;
+}
+
+/* Non-optimized getbits. */
+/* This can easily be optimized for particular platforms. */
+static uint32_t getbits(getbits_state_t *state, uint32_t number_of_bits) {
+  uint32_t result=0;
+  uint8_t byte=0;
+  if (number_of_bits > 32) {
+    printf("Number of bits > 32 in getbits\n");
+    abort();
+  }
+
+  if ((state->bit_position) > 0) {  /* Last getbits left us in the middle of a byte. */
+    if (number_of_bits > (8-state->bit_position)) { /* this getbits will span 2 or more bytes. */
+      byte = state->byte;
+      byte = byte >> (state->bit_position);
+      result = byte;
+      number_of_bits -= (8-state->bit_position);
+      state->bit_position = 0;
+      state->byte_position++;
+      state->byte = state->start[state->byte_position];
+    } else {
+      byte=state->byte;
+      state->byte = state->byte << number_of_bits;
+      byte = byte >> (8 - number_of_bits);
+      result = byte;
+      state->bit_position += number_of_bits; /* Here it is impossible for bit_position > 8 */
+      if (state->bit_position == 8) {
+        state->bit_position = 0;
+        state->byte_position++;
+        state->byte = state->start[state->byte_position];
+      }
+      number_of_bits = 0;
+    }
+  }
+  if ((state->bit_position) == 0) {
+    while (number_of_bits > 7) {
+      result = (result << 8) + state->byte;
+      state->byte_position++;
+      state->byte = state->start[state->byte_position];
+      number_of_bits -= 8;
+    }
+    if (number_of_bits > 0) { /* number_of_bits < 8 */
+      byte = state->byte;
+      state->byte = state->byte << number_of_bits;
+      state->bit_position += number_of_bits; /* Here it is impossible for bit_position > 7 */
+      byte = byte >> (8 - number_of_bits);
+      result = (result << number_of_bits) + byte;
+      number_of_bits = 0;
+    }
+  }
+
+  return result;
+}
+
+
+void navRead_PCI(pci_t *pci, unsigned char *buffer) {
+  int i, j;
+  getbits_state_t state;
+  if (!getbits_init(&state, buffer)) abort(); /* Passed NULL pointers */
+
+  /* pci pci_gi */
+  pci->pci_gi.nv_pck_lbn = getbits(&state, 32 );
+  pci->pci_gi.vobu_cat = getbits(&state, 16 );
+  pci->pci_gi.zero1 = getbits(&state, 16 );
+  pci->pci_gi.vobu_uop_ctl.zero = getbits(&state, 7 );
+  pci->pci_gi.vobu_uop_ctl.video_pres_mode_change         = getbits(&state, 1 );
+
+  pci->pci_gi.vobu_uop_ctl.karaoke_audio_pres_mode_change = getbits(&state, 1 ); 
+  pci->pci_gi.vobu_uop_ctl.angle_change                   = getbits(&state, 1 );
+  pci->pci_gi.vobu_uop_ctl.subpic_stream_change           = getbits(&state, 1 );
+  pci->pci_gi.vobu_uop_ctl.audio_stream_change            = getbits(&state, 1 );
+  pci->pci_gi.vobu_uop_ctl.pause_on                       = getbits(&state, 1 );
+  pci->pci_gi.vobu_uop_ctl.still_off                      = getbits(&state, 1 );
+  pci->pci_gi.vobu_uop_ctl.button_select_or_activate      = getbits(&state, 1 );
+  pci->pci_gi.vobu_uop_ctl.resume                         = getbits(&state, 1 );
+
+  pci->pci_gi.vobu_uop_ctl.chapter_menu_call              = getbits(&state, 1 ); 
+  pci->pci_gi.vobu_uop_ctl.angle_menu_call                = getbits(&state, 1 );
+  pci->pci_gi.vobu_uop_ctl.audio_menu_call                = getbits(&state, 1 );
+  pci->pci_gi.vobu_uop_ctl.subpic_menu_call               = getbits(&state, 1 );
+  pci->pci_gi.vobu_uop_ctl.root_menu_call                 = getbits(&state, 1 );
+  pci->pci_gi.vobu_uop_ctl.title_menu_call                = getbits(&state, 1 );
+  pci->pci_gi.vobu_uop_ctl.backward_scan                  = getbits(&state, 1 );
+  pci->pci_gi.vobu_uop_ctl.forward_scan                   = getbits(&state, 1 );
+
+  pci->pci_gi.vobu_uop_ctl.next_pg_search                 = getbits(&state, 1 ); 
+  pci->pci_gi.vobu_uop_ctl.prev_or_top_pg_search          = getbits(&state, 1 );
+  pci->pci_gi.vobu_uop_ctl.time_or_chapter_search         = getbits(&state, 1 );
+  pci->pci_gi.vobu_uop_ctl.go_up                          = getbits(&state, 1 );
+  pci->pci_gi.vobu_uop_ctl.stop                           = getbits(&state, 1 );
+  pci->pci_gi.vobu_uop_ctl.title_play                     = getbits(&state, 1 );
+  pci->pci_gi.vobu_uop_ctl.chapter_search_or_play         = getbits(&state, 1 );
+  pci->pci_gi.vobu_uop_ctl.title_or_time_play             = getbits(&state, 1 );
+  pci->pci_gi.vobu_s_ptm = getbits(&state, 32 ); 
+  pci->pci_gi.vobu_e_ptm = getbits(&state, 32 ); 
+  pci->pci_gi.vobu_se_e_ptm = getbits(&state, 32 ); 
+  pci->pci_gi.e_eltm.hour   = getbits(&state, 8 );
+  pci->pci_gi.e_eltm.minute = getbits(&state, 8 );
+  pci->pci_gi.e_eltm.second = getbits(&state, 8 );
+  pci->pci_gi.e_eltm.frame_u = getbits(&state, 8 );
+  for(i = 0; i < 32; i++)
+    pci->pci_gi.vobu_isrc[i] = getbits(&state, 8 );
+
+  /* pci nsml_agli */
+  for(i = 0; i < 9; i++)
+    pci->nsml_agli.nsml_agl_dsta[i] = getbits(&state, 32 );
+
+  /* pci hli hli_gi */
+  pci->hli.hl_gi.hli_ss = getbits(&state, 16 );
+  pci->hli.hl_gi.hli_s_ptm = getbits(&state, 32 ); 
+  pci->hli.hl_gi.hli_e_ptm = getbits(&state, 32 );
+  pci->hli.hl_gi.btn_se_e_ptm = getbits(&state, 32 );
+  pci->hli.hl_gi.zero1 = getbits(&state, 2 );
+  pci->hli.hl_gi.btngr_ns = getbits(&state, 2 );
+  pci->hli.hl_gi.zero2 = getbits(&state, 1 );
+  pci->hli.hl_gi.btngr1_dsp_ty = getbits(&state, 3 );
+  pci->hli.hl_gi.zero3 = getbits(&state, 1 );
+  pci->hli.hl_gi.btngr2_dsp_ty = getbits(&state, 3 );
+  pci->hli.hl_gi.zero4 = getbits(&state, 1 );
+  pci->hli.hl_gi.btngr3_dsp_ty = getbits(&state, 3 );
+  pci->hli.hl_gi.btn_ofn = getbits(&state, 8 );
+  pci->hli.hl_gi.btn_ns = getbits(&state, 8 );
+  pci->hli.hl_gi.nsl_btn_ns = getbits(&state, 8 ); 
+  pci->hli.hl_gi.zero5 = getbits(&state, 8 );
+  pci->hli.hl_gi.fosl_btnn = getbits(&state, 8 );
+  pci->hli.hl_gi.foac_btnn = getbits(&state, 8 );
+
+  /* pci hli btn_colit */
+  for(i = 0; i < 3; i++)
+    for(j = 0; j < 2; j++)
+      pci->hli.btn_colit.btn_coli[i][j] = getbits(&state, 32 ); 
+
+  /* pci hli btni */
+  for(i = 0; i < 36; i++) {
+    pci->hli.btnit[i].btn_coln = getbits(&state, 2 );
+    pci->hli.btnit[i].x_start = getbits(&state, 10 );
+    pci->hli.btnit[i].zero1 = getbits(&state, 2 );
+    pci->hli.btnit[i].x_end = getbits(&state, 10 );
+
+    pci->hli.btnit[i].auto_action_mode = getbits(&state, 2 );
+    pci->hli.btnit[i].y_start = getbits(&state, 10 );
+    pci->hli.btnit[i].zero2 = getbits(&state, 2 );
+    pci->hli.btnit[i].y_end = getbits(&state, 10 );
+
+    pci->hli.btnit[i].zero3 = getbits(&state, 2 );
+    pci->hli.btnit[i].up = getbits(&state, 6 );
+    pci->hli.btnit[i].zero4 = getbits(&state, 2 );
+    pci->hli.btnit[i].down = getbits(&state, 6 );
+    pci->hli.btnit[i].zero5 = getbits(&state, 2 );
+    pci->hli.btnit[i].left = getbits(&state, 6 );
+    pci->hli.btnit[i].zero6 = getbits(&state, 2 );
+    pci->hli.btnit[i].right = getbits(&state, 6 );
+    /* pci vm_cmd */
+    for(j = 0; j < 8; j++)
+      pci->hli.btnit[i].cmd.bytes[j] = getbits(&state, 8 );
+  }
+
+
+#ifndef NDEBUG
+  /* Asserts */
+
+  /* pci pci gi */ 
+  CHECK_VALUE(pci->pci_gi.zero1 == 0);
+
+  /* pci hli hli_gi */
+  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) {
+    CHECK_VALUE(pci->hli.hl_gi.btn_ns != 0); 
+    CHECK_VALUE(pci->hli.hl_gi.btngr_ns != 0); 
+  } else {
+    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++) {
+      int n = (36 / pci->hli.hl_gi.btngr_ns) * i + j;
+      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) {  
+        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 {
+        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++)
+          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;
+  getbits_state_t state;
+  if (!getbits_init(&state, buffer)) abort(); /* Passed NULL pointers */
+
+  /* dsi dsi gi */
+  dsi->dsi_gi.nv_pck_scr = getbits(&state, 32 );
+  dsi->dsi_gi.nv_pck_lbn = getbits(&state, 32 );
+  dsi->dsi_gi.vobu_ea = getbits(&state, 32 );
+  dsi->dsi_gi.vobu_1stref_ea = getbits(&state, 32 );
+  dsi->dsi_gi.vobu_2ndref_ea = getbits(&state, 32 );
+  dsi->dsi_gi.vobu_3rdref_ea = getbits(&state, 32 );
+  dsi->dsi_gi.vobu_vob_idn = getbits(&state, 16 );
+  dsi->dsi_gi.zero1 = getbits(&state, 8 );
+  dsi->dsi_gi.vobu_c_idn = getbits(&state, 8 );
+  dsi->dsi_gi.c_eltm.hour   = getbits(&state, 8 );
+  dsi->dsi_gi.c_eltm.minute = getbits(&state, 8 );
+  dsi->dsi_gi.c_eltm.second = getbits(&state, 8 );
+  dsi->dsi_gi.c_eltm.frame_u = getbits(&state, 8 );
+
+  /* dsi sml pbi */
+  dsi->sml_pbi.category = getbits(&state, 16 );
+  dsi->sml_pbi.ilvu_ea = getbits(&state, 32 );
+  dsi->sml_pbi.ilvu_sa = getbits(&state, 32 );
+  dsi->sml_pbi.size = getbits(&state, 16 );
+  dsi->sml_pbi.vob_v_s_s_ptm = getbits(&state, 32 );
+  dsi->sml_pbi.vob_v_e_e_ptm = getbits(&state, 32 );
+  for(i = 0; i < 8; i++) {
+    dsi->sml_pbi.vob_a[i].stp_ptm1 = getbits(&state, 32 );
+    dsi->sml_pbi.vob_a[i].stp_ptm2 = getbits(&state, 32 );
+    dsi->sml_pbi.vob_a[i].gap_len1 = getbits(&state, 32 );
+    dsi->sml_pbi.vob_a[i].gap_len2 = getbits(&state, 32 );
+  }
+
+  /* dsi sml agli */
+  for(i = 0; i < 9; i++) {
+    dsi->sml_agli.data[ i ].address = getbits(&state, 32 );
+    dsi->sml_agli.data[ i ].size = getbits(&state, 16 );
+  }
+
+  /* dsi vobu sri */
+  dsi->vobu_sri.next_video = getbits(&state, 32 );
+  for(i = 0; i < 19; i++)
+    dsi->vobu_sri.fwda[i] = getbits(&state, 32 );
+  dsi->vobu_sri.next_vobu = getbits(&state, 32 );
+  dsi->vobu_sri.prev_vobu = getbits(&state, 32 );
+  for(i = 0; i < 19; i++)
+    dsi->vobu_sri.bwda[i] = getbits(&state, 32 );
+  dsi->vobu_sri.prev_video = getbits(&state, 32 );
+
+  /* dsi synci */
+  for(i = 0; i < 8; i++)
+    dsi->synci.a_synca[i] = getbits(&state, 16 );
+  for(i = 0; i < 32; i++)
+    dsi->synci.sp_synca[i] = getbits(&state, 32 );
+
+  
+  /* Asserts */
+
+  /* dsi dsi gi */
+  CHECK_VALUE(dsi->dsi_gi.zero1 == 0);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/nav_read.h	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,52 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+#ifndef NAV_READ_H_INCLUDED
+#define NAV_READ_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001, 2002 Håkan Hjort <d95hjort@dtek.chalmers.se>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <libdvdread/nav_types.h>
+
+/**
+ * Parsing of NAV data, PCI and DSI parts.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * 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 *);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* NAV_READ_H_INCLUDED */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libdvdread/nav_types.h	Sat Aug 30 12:22:21 2008 +0000
@@ -0,0 +1,273 @@
+/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
+#ifndef NAV_TYPES_H_INCLUDED
+#define NAV_TYPES_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001, 2002 Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * The data structures in this file should represent the layout of the
+ * pci and dsi packets as they are stored in the stream.  Information
+ * found by reading the source to VOBDUMP is the base for the structure
+ * and names of these data types.
+ *
+ * VOBDUMP: a program for examining DVD .VOB files.
+ * Copyright 1998, 1999 Eric Smith <eric@brouhaha.com>
+ *
+ * VOBDUMP is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.  Note that I am not
+ * granting permission to redistribute or modify VOBDUMP under the terms
+ * of any later version of the General Public License.
+ *
+ * This program is distributed in the hope that it will be useful (or at
+ * least amusing), but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
+ * USA
+ */
+
+#include <libdvdread/ifo_types.h> /* only dvd_time_t, vm_cmd_t and user_ops_t */
+/* If it's ever removed add a uintX_t test. */
+
+#undef ATTRIBUTE_PACKED
+#undef PRAGMA_PACK_BEGIN 
+#undef PRAGMA_PACK_END
+
+#if defined(__GNUC__)
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
+#define ATTRIBUTE_PACKED __attribute__ ((packed))
+#define PRAGMA_PACK 0
+#endif
+#endif
+
+#if !defined(ATTRIBUTE_PACKED)
+#define ATTRIBUTE_PACKED
+#define PRAGMA_PACK 1
+#endif
+
+
+/* The length including the substream id byte. */
+#define PCI_BYTES 0x3d4
+#define DSI_BYTES 0x3fa
+
+#define PS2_PCI_SUBSTREAM_ID 0x00
+#define PS2_DSI_SUBSTREAM_ID 0x01
+
+/* Remove this */
+#define DSI_START_BYTE 1031
+
+
+#if PRAGMA_PACK
+#pragma pack(1)
+#endif
+
+
+/**
+ * PCI General Information 
+ */
+typedef struct {
+  uint32_t nv_pck_lbn;      /**< 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;
+
+/**
+ * Non Seamless Angle Information
+ */
+typedef struct {
+  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; /**< 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 */
+  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 */
+  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];  /**< [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 {
+  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 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 zero3            : 2;  /**< reserved */
+  unsigned int up               : 6;  /**< button index when pressing up */
+  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 */
+  vm_cmd_t cmd;
+} ATTRIBUTE_PACKED btni_t;
+
+/**
+ * Highlight Information 
+ */
+typedef struct {
+  hl_gi_t     hl_gi;
+  btn_colit_t btn_colit;
+  btni_t      btnit[36];
+} ATTRIBUTE_PACKED hli_t;
+
+/**
+ * PCI packet
+ */
+typedef struct {
+  pci_gi_t    pci_gi;
+  nsml_agli_t nsml_agli;
+  hli_t       hli;
+  uint8_t     zero1[189];
+} ATTRIBUTE_PACKED pci_t;
+
+
+
+
+/**
+ * DSI General Information 
+ */
+typedef struct {
+  uint32_t nv_pck_scr;
+  uint32_t nv_pck_lbn;      /**< 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 */
+  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;
+    uint32_t gap_len1;
+    uint32_t gap_len2;      
+  } vob_a[8];
+} ATTRIBUTE_PACKED sml_pbi_t;
+
+/**
+ * Seamless Angle Infromation for one angle
+ */
+typedef struct {
+  uint32_t address; /**< 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;
+
+/**
+ * Seamless Angle Infromation
+ */
+typedef struct {
+  sml_agl_data_t data[9];
+} ATTRIBUTE_PACKED sml_agli_t;
+
+/**
+ * VOBU Search Information 
+ */
+typedef struct {
+  uint32_t next_video; /**< Next vobu that contains video */
+  uint32_t fwda[19];   /**< Forwards, time */
+  uint32_t next_vobu;
+  uint32_t prev_vobu;
+  uint32_t bwda[19];   /**< Backwards, time */
+  uint32_t prev_video;
+} ATTRIBUTE_PACKED vobu_sri_t;
+
+#define SRI_END_OF_CELL 0x3fffffff
+
+/**
+ * Synchronous Information
+ */ 
+typedef struct {
+  uint16_t a_synca[8];   /**< offset to first audio packet for this VOBU */
+  uint32_t sp_synca[32]; /**< offset to first subpicture packet */
+} ATTRIBUTE_PACKED synci_t;
+
+/**
+ * DSI packet
+ */
+typedef struct {
+  dsi_gi_t   dsi_gi;
+  sml_pbi_t  sml_pbi;
+  sml_agli_t sml_agli;
+  vobu_sri_t vobu_sri;
+  synci_t    synci;
+  uint8_t    zero1[471];
+} ATTRIBUTE_PACKED dsi_t;
+
+
+#if PRAGMA_PACK
+#pragma pack()
+#endif
+
+#endif /* NAV_TYPES_H_INCLUDED */
--- a/stream/stream_dvd.h	Sat Aug 30 11:21:11 2008 +0000
+++ b/stream/stream_dvd.h	Sat Aug 30 12:22:21 2008 +0000
@@ -4,15 +4,15 @@
 #include "config.h"
 #include <stdint.h>
 #ifdef CONFIG_DVDREAD_INTERNAL
-#include "dvdread/dvd_reader.h"
-#include "dvdread/ifo_types.h"
-#include "dvdread/ifo_read.h"
-#include "dvdread/nav_read.h"
+#include "libdvdread/dvd_reader.h"
+#include "libdvdread/ifo_types.h"
+#include "libdvdread/ifo_read.h"
+#include "libdvdread/nav_read.h"
 #else
-#include <libdvdread/dvd_reader.h>
-#include <libdvdread/ifo_types.h>
-#include <libdvdread/ifo_read.h>
-#include <libdvdread/nav_read.h>
+#include <dvdread/dvd_reader.h>
+#include <dvdread/ifo_types.h>
+#include <dvdread/ifo_read.h>
+#include <dvdread/nav_read.h>
 #endif
 #include "stream.h"
 
--- a/stream/stream_dvd_common.c	Sat Aug 30 11:21:11 2008 +0000
+++ b/stream/stream_dvd_common.c	Sat Aug 30 12:22:21 2008 +0000
@@ -1,9 +1,9 @@
 #include "config.h"
 #include <inttypes.h>
 #ifdef CONFIG_DVDREAD_INTERNAL
-#include <dvdread/ifo_types.h>
+#include "libdvdread/ifo_types.h"
 #else
-#include <libdvdread/ifo_types.h>
+#include <dvdread/ifo_types.h>
 #endif
 #include "stream_dvd_common.h"
 
--- a/stream/stream_dvd_common.h	Sat Aug 30 11:21:11 2008 +0000
+++ b/stream/stream_dvd_common.h	Sat Aug 30 12:22:21 2008 +0000
@@ -4,9 +4,9 @@
 #include "config.h"
 #include <inttypes.h>
 #ifdef CONFIG_DVDREAD_INTERNAL
-#include <dvdread/ifo_types.h>
+#include "libdvdread/ifo_types.h"
 #else
-#include <libdvdread/ifo_types.h>
+#include <dvdread/ifo_types.h>
 #endif
 
 int mp_dvdtimetomsec(dvd_time_t *dt);