]> git.sesse.net Git - vlc/commitdiff
Support for libdvdread.
authorStéphane Borel <stef@videolan.org>
Sun, 25 Nov 2001 05:04:38 +0000 (05:04 +0000)
committerStéphane Borel <stef@videolan.org>
Sun, 25 Nov 2001 05:04:38 +0000 (05:04 +0000)
Libdvdread files are in extras/libdvdread. They have not been changed from
those of libdvdread 0.9.2. The changes are in the subdirectory videolan.
They basically consist of a new function in the API that does a readv()
instead fo a read().

The library is statically linked with libdvdcss from extras/libdvdcss, and
the whole is statically linked with the plugin dvdread. Dvdread can only be
compiled as a builtin module for the time being, I think (which means I
haven't tried to compile it as a plugin).

Basically, input_dvdread.c is a copy and paste from input_dvd.c with a few
changes to use libdvdread calls. It is still in very alpha stage, but it
should offer the same features as the current DVD input.

It can be called with either the syntax: "dvdread:/dev/dvd"
or "--input dvdread /dev/dvd". The command line switches (-t, -T, -c, -u)
should work too.

32 files changed:
Makefile
Makefile.opts.in
configure
configure.in
extras/libdvdread/.cvsignore [new file with mode: 0644]
extras/libdvdread/Makefile [new file with mode: 0644]
extras/libdvdread/bswap.h [new file with mode: 0644]
extras/libdvdread/dvd_reader.c [new file with mode: 0644]
extras/libdvdread/dvd_reader.h [new file with mode: 0644]
extras/libdvdread/dvd_udf.c [new file with mode: 0644]
extras/libdvdread/dvd_udf.h [new file with mode: 0644]
extras/libdvdread/dvdcss.h [new file with mode: 0644]
extras/libdvdread/dvdread.c [new file with mode: 0644]
extras/libdvdread/ifo_print.c [new file with mode: 0644]
extras/libdvdread/ifo_print.h [new file with mode: 0644]
extras/libdvdread/ifo_read.c [new file with mode: 0644]
extras/libdvdread/ifo_read.h [new file with mode: 0644]
extras/libdvdread/ifo_types.h [new file with mode: 0644]
extras/libdvdread/nav_print.c [new file with mode: 0644]
extras/libdvdread/nav_print.h [new file with mode: 0644]
extras/libdvdread/nav_read.c [new file with mode: 0644]
extras/libdvdread/nav_read.h [new file with mode: 0644]
extras/libdvdread/nav_types.h [new file with mode: 0644]
extras/libdvdread/videolan/.cvsignore [new file with mode: 0644]
extras/libdvdread/videolan/dvdread.c [new file with mode: 0644]
extras/libdvdread/videolan/dvdread.h [new file with mode: 0644]
plugins/dvdread/.cvsignore [new file with mode: 0644]
plugins/dvdread/Makefile [new file with mode: 0644]
plugins/dvdread/dvdread.c [new file with mode: 0644]
plugins/dvdread/input_dvdread.c [new file with mode: 0644]
plugins/dvdread/input_dvdread.h [new file with mode: 0644]
src/input/input.c

index 5d304367e7fd2c5cdf3d9ac013be6a375a8f1a89..9cbac3fabebf538db1f78c3ef1c69b36f4b625e7 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -23,6 +23,7 @@ PLUGINS_DIR :=        ac3_adec \
                dsp \
                dummy \
                dvd \
+               dvdread \
                esd \
                fb \
                ggi \
@@ -58,6 +59,7 @@ PLUGINS_TARGETS := ac3_adec/ac3_adec \
                dummy/dummy \
                dummy/null \
                dvd/dvd \
+               dvdread/dvdread \
                esd/esd \
                fb/fb \
                ggi/ggi \
@@ -198,7 +200,7 @@ show:
 #
 # Cleaning rules
 #
-clean: libdvdcss-clean plugins-clean vlc-clean
+clean: libdvdcss-clean libdvdread-clean plugins-clean vlc-clean
        rm -f src/*/*.o extras/*/*.o
        rm -f lib/*.so* lib/*.a
        rm -f plugins/*.so plugins/*.a
@@ -207,6 +209,9 @@ clean: libdvdcss-clean plugins-clean vlc-clean
 libdvdcss-clean:
        -cd extras/libdvdcss && $(MAKE) clean
 
+libdvdread-clean:
+       -cd extras/libdvdread && $(MAKE) clean
+
 plugins-clean:
        for dir in $(PLUGINS_DIR) ; do \
                ( cd plugins/$${dir} && $(MAKE) clean ) ; done
@@ -533,3 +538,10 @@ $(BUILTIN_OBJ): FORCE
 #
 libdvdcss: Makefile.opts
        cd extras/libdvdcss && $(MAKE)
+
+#
+# libdvdread target
+#
+libdvdread: Makefile.opts
+       cd extras/libdvdread && $(MAKE)
+
index d0cd58b1594b9110ae96bfd025a2836aeee5377f..4c511eed2f17d9fa3d3672be032c149a5c33e240 100644 (file)
@@ -72,6 +72,8 @@ LIB_BEOS = @LIB_BEOS@
 LIB_DARWIN = @LIB_DARWIN@
 LIB_DVD = @LIB_DVD@
 LIB_DVD_PLUGIN = @LIB_DVD_PLUGIN@
+LIB_DVDREAD = @LIB_DVDREAD@
+LIB_DVDREAD_PLUGIN = @LIB_DVDREAD_PLUGIN@
 LIB_ESD = @LIB_ESD@
 LIB_GGI = @LIB_GGI@
 LIB_GLIDE = @LIB_GLIDE@
@@ -95,6 +97,7 @@ LIB_YUV = @LIB_YUV@
 #
 CFLAGS_ALTIVEC = @CFLAGS_ALTIVEC@
 CFLAGS_DVD = @CFLAGS_DVD@
+CFLAGS_DVDREAD = @CFLAGS_DVDREAD@
 CFLAGS_ARTS = @CFLAGS_ARTS@
 CFLAGS_ESD = @CFLAGS_ESD@
 CFLAGS_LIBDVDCSS = @CFLAGS_LIBDVDCSS@
index 174099bdf188a2b9169e12b9d4fae8567718d03a..16718543cc07f503e136308f903333796ede5f97 100755 (executable)
--- a/configure
+++ b/configure
@@ -39,6 +39,8 @@ ac_help="$ac_help
                           or 'local-static', 'local-shared', or a path to
                           another libdvdcss such as '/usr/local'
                           (default 'local-static')"
+ac_help="$ac_help
+  --enable-dvdread          Enable dvdread support (default enabled)"
 ac_help="$ac_help
   --enable-vcd            VCD support for Linux (default enabled)"
 ac_help="$ac_help
@@ -653,7 +655,7 @@ else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; }
 fi
 
 echo $ac_n "checking host system type""... $ac_c" 1>&6
-echo "configure:657: checking host system type" >&5
+echo "configure:659: checking host system type" >&5
 
 host_alias=$host
 case "$host_alias" in
@@ -674,7 +676,7 @@ host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
 echo "$ac_t""$host" 1>&6
 
 echo $ac_n "checking target system type""... $ac_c" 1>&6
-echo "configure:678: checking target system type" >&5
+echo "configure:680: checking target system type" >&5
 
 target_alias=$target
 case "$target_alias" in
@@ -692,7 +694,7 @@ target_os=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
 echo "$ac_t""$target" 1>&6
 
 echo $ac_n "checking build system type""... $ac_c" 1>&6
-echo "configure:696: checking build system type" >&5
+echo "configure:698: checking build system type" >&5
 
 build_alias=$build
 case "$build_alias" in
@@ -741,7 +743,7 @@ save_CFLAGS="${CFLAGS}"
 save_LDFLAGS="${LDFLAGS}"
 
 echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6
-echo "configure:745: checking whether ${MAKE-make} sets \${MAKE}" >&5
+echo "configure:747: checking whether ${MAKE-make} sets \${MAKE}" >&5
 set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -770,7 +772,7 @@ fi
 # Extract the first word of "gcc", so it can be a program name with args.
 set dummy gcc; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:774: checking for $ac_word" >&5
+echo "configure:776: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -800,7 +802,7 @@ if test -z "$CC"; then
   # Extract the first word of "cc", so it can be a program name with args.
 set dummy cc; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:804: checking for $ac_word" >&5
+echo "configure:806: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -851,7 +853,7 @@ fi
       # Extract the first word of "cl", so it can be a program name with args.
 set dummy cl; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:855: checking for $ac_word" >&5
+echo "configure:857: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -883,7 +885,7 @@ fi
 fi
 
 echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6
-echo "configure:887: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
+echo "configure:889: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5
 
 ac_ext=c
 # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
@@ -894,12 +896,12 @@ cross_compiling=$ac_cv_prog_cc_cross
 
 cat > conftest.$ac_ext << EOF
 
-#line 898 "configure"
+#line 900 "configure"
 #include "confdefs.h"
 
 main(){return(0);}
 EOF
-if { (eval echo configure:903: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:905: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   ac_cv_prog_cc_works=yes
   # If we can't run a trivial program, we are probably using a cross compiler.
   if (./conftest; exit) 2>/dev/null; then
@@ -925,12 +927,12 @@ if test $ac_cv_prog_cc_works = no; then
   { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; }
 fi
 echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6
-echo "configure:929: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
+echo "configure:931: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5
 echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6
 cross_compiling=$ac_cv_prog_cc_cross
 
 echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
-echo "configure:934: checking whether we are using GNU C" >&5
+echo "configure:936: checking whether we are using GNU C" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -939,7 +941,7 @@ else
   yes;
 #endif
 EOF
-if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:943: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:945: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
   ac_cv_prog_gcc=yes
 else
   ac_cv_prog_gcc=no
@@ -958,7 +960,7 @@ ac_test_CFLAGS="${CFLAGS+set}"
 ac_save_CFLAGS="$CFLAGS"
 CFLAGS=
 echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
-echo "configure:962: checking whether ${CC-cc} accepts -g" >&5
+echo "configure:964: checking whether ${CC-cc} accepts -g" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -990,7 +992,7 @@ else
 fi
 
 echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
-echo "configure:994: checking how to run the C preprocessor" >&5
+echo "configure:996: checking how to run the C preprocessor" >&5
 # On Suns, sometimes $CPP names a directory.
 if test -n "$CPP" && test -d "$CPP"; then
   CPP=
@@ -1005,13 +1007,13 @@ else
   # On the NeXT, cc -E runs the code through the compiler's parser,
   # not just through cpp.
   cat > conftest.$ac_ext <<EOF
-#line 1009 "configure"
+#line 1011 "configure"
 #include "confdefs.h"
 #include <assert.h>
 Syntax Error
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:1015: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:1017: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   :
@@ -1022,13 +1024,13 @@ else
   rm -rf conftest*
   CPP="${CC-cc} -E -traditional-cpp"
   cat > conftest.$ac_ext <<EOF
-#line 1026 "configure"
+#line 1028 "configure"
 #include "confdefs.h"
 #include <assert.h>
 Syntax Error
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:1032: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:1034: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   :
@@ -1039,13 +1041,13 @@ else
   rm -rf conftest*
   CPP="${CC-cc} -nologo -E"
   cat > conftest.$ac_ext <<EOF
-#line 1043 "configure"
+#line 1045 "configure"
 #include "confdefs.h"
 #include <assert.h>
 Syntax Error
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:1049: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:1051: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   :
@@ -1078,7 +1080,7 @@ fi
 # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
 set dummy ${ac_tool_prefix}ranlib; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:1082: checking for $ac_word" >&5
+echo "configure:1084: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -1110,7 +1112,7 @@ if test -n "$ac_tool_prefix"; then
   # Extract the first word of "ranlib", so it can be a program name with args.
 set dummy ranlib; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:1114: checking for $ac_word" >&5
+echo "configure:1116: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -1155,7 +1157,7 @@ fi
 # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
 # ./install, which can be erroneously created by make from ./install.sh.
 echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
-echo "configure:1159: checking for a BSD compatible install" >&5
+echo "configure:1161: checking for a BSD compatible install" >&5
 if test -z "$INSTALL"; then
 if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -1210,14 +1212,14 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
 
 if test x${cross_compiling} != xyes; then
   echo $ac_n "checking whether byte ordering is bigendian""... $ac_c" 1>&6
-echo "configure:1214: checking whether byte ordering is bigendian" >&5
+echo "configure:1216: checking whether byte ordering is bigendian" >&5
 if eval "test \"`echo '$''{'ac_cv_c_bigendian'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   ac_cv_c_bigendian=unknown
 # See if sys/param.h defines the BYTE_ORDER macro.
 cat > conftest.$ac_ext <<EOF
-#line 1221 "configure"
+#line 1223 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #include <sys/param.h>
@@ -1228,11 +1230,11 @@ int main() {
 #endif
 ; return 0; }
 EOF
-if { (eval echo configure:1232: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:1234: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   # It does; now see whether it defined to BIG_ENDIAN or not.
 cat > conftest.$ac_ext <<EOF
-#line 1236 "configure"
+#line 1238 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #include <sys/param.h>
@@ -1243,7 +1245,7 @@ int main() {
 #endif
 ; return 0; }
 EOF
-if { (eval echo configure:1247: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:1249: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_c_bigendian=yes
 else
@@ -1263,7 +1265,7 @@ if test "$cross_compiling" = yes; then
     { echo "configure: error: can not run test program while cross compiling" 1>&2; exit 1; }
 else
   cat > conftest.$ac_ext <<EOF
-#line 1267 "configure"
+#line 1269 "configure"
 #include "confdefs.h"
 main () {
   /* Are we little or big endian?  From Harbison&Steele.  */
@@ -1276,7 +1278,7 @@ main () {
   exit (u.c[sizeof (long) - 1] == 1);
 }
 EOF
-if { (eval echo configure:1280: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:1282: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   ac_cv_c_bigendian=no
 else
@@ -1316,7 +1318,7 @@ fi
     *)
                   ac_cv_c_bigendian=unknown
       echo $ac_n "checking what the byte order looks to be""... $ac_c" 1>&6
-echo "configure:1320: checking what the byte order looks to be" >&5
+echo "configure:1322: checking what the byte order looks to be" >&5
         cat >conftest.c <<EOF
         short am[] = { 0x4249, 0x4765, 0x6e44, 0x6961, 0x6e53, 0x7953, 0 };
         short ai[] = { 0x694c, 0x5454, 0x656c, 0x6e45, 0x6944, 0x6e61, 0 };
@@ -1361,12 +1363,12 @@ fi
 for ac_func in gettimeofday select strerror strtod strtol
 do
 echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
-echo "configure:1365: checking for $ac_func" >&5
+echo "configure:1367: checking for $ac_func" >&5
 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 1370 "configure"
+#line 1372 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char $ac_func(); below.  */
@@ -1389,7 +1391,7 @@ $ac_func();
 
 ; return 0; }
 EOF
-if { (eval echo configure:1393: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1395: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_$ac_func=yes"
 else
@@ -1416,12 +1418,12 @@ done
 for ac_func in setenv putenv
 do
 echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
-echo "configure:1420: checking for $ac_func" >&5
+echo "configure:1422: checking for $ac_func" >&5
 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 1425 "configure"
+#line 1427 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char $ac_func(); below.  */
@@ -1444,7 +1446,7 @@ $ac_func();
 
 ; return 0; }
 EOF
-if { (eval echo configure:1448: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1450: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_$ac_func=yes"
 else
@@ -1469,12 +1471,12 @@ fi
 done
 
 echo $ac_n "checking for connect""... $ac_c" 1>&6
-echo "configure:1473: checking for connect" >&5
+echo "configure:1475: checking for connect" >&5
 if eval "test \"`echo '$''{'ac_cv_func_connect'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 1478 "configure"
+#line 1480 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char connect(); below.  */
@@ -1497,7 +1499,7 @@ connect();
 
 ; return 0; }
 EOF
-if { (eval echo configure:1501: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1503: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_connect=yes"
 else
@@ -1516,7 +1518,7 @@ else
   echo "$ac_t""no" 1>&6
 
   echo $ac_n "checking for connect in -lsocket""... $ac_c" 1>&6
-echo "configure:1520: checking for connect in -lsocket" >&5
+echo "configure:1522: checking for connect in -lsocket" >&5
 ac_lib_var=`echo socket'_'connect | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -1524,7 +1526,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lsocket  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 1528 "configure"
+#line 1530 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -1535,7 +1537,7 @@ int main() {
 connect()
 ; return 0; }
 EOF
-if { (eval echo configure:1539: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1541: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -1559,12 +1561,12 @@ fi
 fi
 
 echo $ac_n "checking for gethostbyname""... $ac_c" 1>&6
-echo "configure:1563: checking for gethostbyname" >&5
+echo "configure:1565: checking for gethostbyname" >&5
 if eval "test \"`echo '$''{'ac_cv_func_gethostbyname'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 1568 "configure"
+#line 1570 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char gethostbyname(); below.  */
@@ -1587,7 +1589,7 @@ gethostbyname();
 
 ; return 0; }
 EOF
-if { (eval echo configure:1591: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1593: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_gethostbyname=yes"
 else
@@ -1606,7 +1608,7 @@ else
   echo "$ac_t""no" 1>&6
 
   echo $ac_n "checking for gethostbyname in -lnsl""... $ac_c" 1>&6
-echo "configure:1610: checking for gethostbyname in -lnsl" >&5
+echo "configure:1612: checking for gethostbyname in -lnsl" >&5
 ac_lib_var=`echo nsl'_'gethostbyname | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -1614,7 +1616,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lnsl  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 1618 "configure"
+#line 1620 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -1625,7 +1627,7 @@ int main() {
 gethostbyname()
 ; return 0; }
 EOF
-if { (eval echo configure:1629: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1631: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -1649,12 +1651,12 @@ fi
 fi
 
 echo $ac_n "checking for nanosleep""... $ac_c" 1>&6
-echo "configure:1653: checking for nanosleep" >&5
+echo "configure:1655: checking for nanosleep" >&5
 if eval "test \"`echo '$''{'ac_cv_func_nanosleep'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 1658 "configure"
+#line 1660 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char nanosleep(); below.  */
@@ -1677,7 +1679,7 @@ nanosleep();
 
 ; return 0; }
 EOF
-if { (eval echo configure:1681: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1683: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_nanosleep=yes"
 else
@@ -1696,7 +1698,7 @@ else
   echo "$ac_t""no" 1>&6
 
   echo $ac_n "checking for nanosleep in -lrt""... $ac_c" 1>&6
-echo "configure:1700: checking for nanosleep in -lrt" >&5
+echo "configure:1702: checking for nanosleep in -lrt" >&5
 ac_lib_var=`echo rt'_'nanosleep | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -1704,7 +1706,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lrt  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 1708 "configure"
+#line 1710 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -1715,7 +1717,7 @@ int main() {
 nanosleep()
 ; return 0; }
 EOF
-if { (eval echo configure:1719: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1721: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -1735,7 +1737,7 @@ else
   echo "$ac_t""no" 1>&6
 
     echo $ac_n "checking for nanosleep in -lposix4""... $ac_c" 1>&6
-echo "configure:1739: checking for nanosleep in -lposix4" >&5
+echo "configure:1741: checking for nanosleep in -lposix4" >&5
 ac_lib_var=`echo posix4'_'nanosleep | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -1743,7 +1745,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lposix4  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 1747 "configure"
+#line 1749 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -1754,7 +1756,7 @@ int main() {
 nanosleep()
 ; return 0; }
 EOF
-if { (eval echo configure:1758: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1760: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -1783,12 +1785,12 @@ fi
 for ac_func in usleep
 do
 echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
-echo "configure:1787: checking for $ac_func" >&5
+echo "configure:1789: checking for $ac_func" >&5
 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 1792 "configure"
+#line 1794 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char $ac_func(); below.  */
@@ -1811,7 +1813,7 @@ $ac_func();
 
 ; return 0; }
 EOF
-if { (eval echo configure:1815: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1817: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_$ac_func=yes"
 else
@@ -1836,12 +1838,12 @@ fi
 done
 
 echo $ac_n "checking for inet_aton""... $ac_c" 1>&6
-echo "configure:1840: checking for inet_aton" >&5
+echo "configure:1842: checking for inet_aton" >&5
 if eval "test \"`echo '$''{'ac_cv_func_inet_aton'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 1845 "configure"
+#line 1847 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char inet_aton(); below.  */
@@ -1864,7 +1866,7 @@ inet_aton();
 
 ; return 0; }
 EOF
-if { (eval echo configure:1868: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1870: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_inet_aton=yes"
 else
@@ -1883,7 +1885,7 @@ else
   echo "$ac_t""no" 1>&6
 
   echo $ac_n "checking for inet_aton in -lresolv""... $ac_c" 1>&6
-echo "configure:1887: checking for inet_aton in -lresolv" >&5
+echo "configure:1889: checking for inet_aton in -lresolv" >&5
 ac_lib_var=`echo resolv'_'inet_aton | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -1891,7 +1893,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lresolv  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 1895 "configure"
+#line 1897 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -1902,7 +1904,7 @@ int main() {
 inet_aton()
 ; return 0; }
 EOF
-if { (eval echo configure:1906: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1908: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -1928,12 +1930,12 @@ fi
 for ac_func in vasprintf
 do
 echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
-echo "configure:1932: checking for $ac_func" >&5
+echo "configure:1934: checking for $ac_func" >&5
 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 1937 "configure"
+#line 1939 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char $ac_func(); below.  */
@@ -1956,7 +1958,7 @@ $ac_func();
 
 ; return 0; }
 EOF
-if { (eval echo configure:1960: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:1962: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_$ac_func=yes"
 else
@@ -1983,12 +1985,12 @@ done
 for ac_func in swab
 do
 echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
-echo "configure:1987: checking for $ac_func" >&5
+echo "configure:1989: checking for $ac_func" >&5
 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 1992 "configure"
+#line 1994 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char $ac_func(); below.  */
@@ -2011,7 +2013,7 @@ $ac_func();
 
 ; return 0; }
 EOF
-if { (eval echo configure:2015: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:2017: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_$ac_func=yes"
 else
@@ -2038,12 +2040,12 @@ done
 for ac_func in memalign valloc
 do
 echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
-echo "configure:2042: checking for $ac_func" >&5
+echo "configure:2044: checking for $ac_func" >&5
 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2047 "configure"
+#line 2049 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char $ac_func(); below.  */
@@ -2066,7 +2068,7 @@ $ac_func();
 
 ; return 0; }
 EOF
-if { (eval echo configure:2070: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:2072: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_$ac_func=yes"
 else
@@ -2094,12 +2096,12 @@ done
 for ac_func in sigrelse
 do
 echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
-echo "configure:2098: checking for $ac_func" >&5
+echo "configure:2100: checking for $ac_func" >&5
 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2103 "configure"
+#line 2105 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char $ac_func(); below.  */
@@ -2122,7 +2124,7 @@ $ac_func();
 
 ; return 0; }
 EOF
-if { (eval echo configure:2126: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:2128: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_$ac_func=yes"
 else
@@ -2149,12 +2151,12 @@ done
 
 NEED_GETOPT=0
 echo $ac_n "checking for getopt_long""... $ac_c" 1>&6
-echo "configure:2153: checking for getopt_long" >&5
+echo "configure:2155: checking for getopt_long" >&5
 if eval "test \"`echo '$''{'ac_cv_func_getopt_long'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2158 "configure"
+#line 2160 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char getopt_long(); below.  */
@@ -2177,7 +2179,7 @@ getopt_long();
 
 ; return 0; }
 EOF
-if { (eval echo configure:2181: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:2183: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_getopt_long=yes"
 else
@@ -2199,7 +2201,7 @@ else
   echo "$ac_t""no" 1>&6
  # FreeBSD has a gnugetopt library for this:
   echo $ac_n "checking for getopt_long in -lgnugetopt""... $ac_c" 1>&6
-echo "configure:2203: checking for getopt_long in -lgnugetopt" >&5
+echo "configure:2205: checking for getopt_long in -lgnugetopt" >&5
 ac_lib_var=`echo gnugetopt'_'getopt_long | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -2207,7 +2209,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lgnugetopt  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 2211 "configure"
+#line 2213 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -2218,7 +2220,7 @@ int main() {
 getopt_long()
 ; return 0; }
 EOF
-if { (eval echo configure:2222: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:2224: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -2249,17 +2251,17 @@ for ac_hdr in unistd.h
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:2253: checking for $ac_hdr" >&5
+echo "configure:2255: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2258 "configure"
+#line 2260 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:2263: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:2265: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -2288,12 +2290,12 @@ done
 for ac_func in getpagesize
 do
 echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
-echo "configure:2292: checking for $ac_func" >&5
+echo "configure:2294: checking for $ac_func" >&5
 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2297 "configure"
+#line 2299 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char $ac_func(); below.  */
@@ -2316,7 +2318,7 @@ $ac_func();
 
 ; return 0; }
 EOF
-if { (eval echo configure:2320: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:2322: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_$ac_func=yes"
 else
@@ -2341,7 +2343,7 @@ fi
 done
 
 echo $ac_n "checking for working mmap""... $ac_c" 1>&6
-echo "configure:2345: checking for working mmap" >&5
+echo "configure:2347: checking for working mmap" >&5
 if eval "test \"`echo '$''{'ac_cv_func_mmap_fixed_mapped'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -2349,7 +2351,7 @@ else
   ac_cv_func_mmap_fixed_mapped=no
 else
   cat > conftest.$ac_ext <<EOF
-#line 2353 "configure"
+#line 2355 "configure"
 #include "confdefs.h"
 
 /* Thanks to Mike Haertel and Jim Avera for this test.
@@ -2489,7 +2491,7 @@ main()
 }
 
 EOF
-if { (eval echo configure:2493: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:2495: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   ac_cv_func_mmap_fixed_mapped=yes
 else
@@ -2512,12 +2514,12 @@ EOF
 fi
 
 echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6
-echo "configure:2516: checking return type of signal handlers" >&5
+echo "configure:2518: checking return type of signal handlers" >&5
 if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2521 "configure"
+#line 2523 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #include <signal.h>
@@ -2534,7 +2536,7 @@ int main() {
 int i;
 ; return 0; }
 EOF
-if { (eval echo configure:2538: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:2540: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_type_signal=void
 else
@@ -2553,7 +2555,7 @@ EOF
 
 
 echo $ac_n "checking for dlopen in -ldl""... $ac_c" 1>&6
-echo "configure:2557: checking for dlopen in -ldl" >&5
+echo "configure:2559: checking for dlopen in -ldl" >&5
 ac_lib_var=`echo dl'_'dlopen | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -2561,7 +2563,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-ldl  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 2565 "configure"
+#line 2567 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -2572,7 +2574,7 @@ int main() {
 dlopen()
 ; return 0; }
 EOF
-if { (eval echo configure:2576: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:2578: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -2593,7 +2595,7 @@ else
 fi
 
 echo $ac_n "checking for pow in -lm""... $ac_c" 1>&6
-echo "configure:2597: checking for pow in -lm" >&5
+echo "configure:2599: checking for pow in -lm" >&5
 ac_lib_var=`echo m'_'pow | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -2601,7 +2603,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lm  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 2605 "configure"
+#line 2607 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -2612,7 +2614,7 @@ int main() {
 pow()
 ; return 0; }
 EOF
-if { (eval echo configure:2616: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:2618: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -2636,7 +2638,7 @@ fi
 THREAD_LIB=error
 if test "x${THREAD_LIB}" = xerror; then
   echo $ac_n "checking for pthread_attr_init in -lpthread""... $ac_c" 1>&6
-echo "configure:2640: checking for pthread_attr_init in -lpthread" >&5
+echo "configure:2642: checking for pthread_attr_init in -lpthread" >&5
 ac_lib_var=`echo pthread'_'pthread_attr_init | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -2644,7 +2646,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lpthread  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 2648 "configure"
+#line 2650 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -2655,7 +2657,7 @@ int main() {
 pthread_attr_init()
 ; return 0; }
 EOF
-if { (eval echo configure:2659: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:2661: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -2678,7 +2680,7 @@ fi
 fi
 if test "x${THREAD_LIB}" = xerror; then
   echo $ac_n "checking for pthread_attr_init in -lpthreads""... $ac_c" 1>&6
-echo "configure:2682: checking for pthread_attr_init in -lpthreads" >&5
+echo "configure:2684: checking for pthread_attr_init in -lpthreads" >&5
 ac_lib_var=`echo pthreads'_'pthread_attr_init | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -2686,7 +2688,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lpthreads  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 2690 "configure"
+#line 2692 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -2697,7 +2699,7 @@ int main() {
 pthread_attr_init()
 ; return 0; }
 EOF
-if { (eval echo configure:2701: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:2703: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -2720,7 +2722,7 @@ fi
 fi
 if test "x${THREAD_LIB}" = xerror; then
   echo $ac_n "checking for pthread_attr_init in -lc_r""... $ac_c" 1>&6
-echo "configure:2724: checking for pthread_attr_init in -lc_r" >&5
+echo "configure:2726: checking for pthread_attr_init in -lc_r" >&5
 ac_lib_var=`echo c_r'_'pthread_attr_init | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -2728,7 +2730,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lc_r  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 2732 "configure"
+#line 2734 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -2739,7 +2741,7 @@ int main() {
 pthread_attr_init()
 ; return 0; }
 EOF
-if { (eval echo configure:2743: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:2745: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -2762,12 +2764,12 @@ fi
 fi
 if test "x${THREAD_LIB}" = xerror; then
   echo $ac_n "checking for pthread_attr_init""... $ac_c" 1>&6
-echo "configure:2766: checking for pthread_attr_init" >&5
+echo "configure:2768: checking for pthread_attr_init" >&5
 if eval "test \"`echo '$''{'ac_cv_func_pthread_attr_init'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2771 "configure"
+#line 2773 "configure"
 #include "confdefs.h"
 /* System header to define __stub macros and hopefully few prototypes,
     which can conflict with char pthread_attr_init(); below.  */
@@ -2790,7 +2792,7 @@ pthread_attr_init();
 
 ; return 0; }
 EOF
-if { (eval echo configure:2794: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:2796: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_func_pthread_attr_init=yes"
 else
@@ -2813,7 +2815,7 @@ fi
 fi
 
 echo $ac_n "checking for cthread_fork in -lthreads""... $ac_c" 1>&6
-echo "configure:2817: checking for cthread_fork in -lthreads" >&5
+echo "configure:2819: checking for cthread_fork in -lthreads" >&5
 ac_lib_var=`echo threads'_'cthread_fork | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -2821,7 +2823,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lthreads  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 2825 "configure"
+#line 2827 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -2832,7 +2834,7 @@ int main() {
 cthread_fork()
 ; return 0; }
 EOF
-if { (eval echo configure:2836: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:2838: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -2854,7 +2856,7 @@ fi
 
 
 cat > conftest.$ac_ext <<EOF
-#line 2858 "configure"
+#line 2860 "configure"
 #include "confdefs.h"
 #include <pthread.h>
 EOF
@@ -2870,7 +2872,7 @@ fi
 rm -f conftest*
 
 cat > conftest.$ac_ext <<EOF
-#line 2874 "configure"
+#line 2876 "configure"
 #include "confdefs.h"
 #include <strings.h>
 EOF
@@ -2890,17 +2892,17 @@ for ac_hdr in stddef.h getopt.h strings.h
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:2894: checking for $ac_hdr" >&5
+echo "configure:2896: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2899 "configure"
+#line 2901 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:2904: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:2906: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -2930,17 +2932,17 @@ for ac_hdr in sys/sockio.h fcntl.h sys/time.h sys/times.h
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:2934: checking for $ac_hdr" >&5
+echo "configure:2936: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2939 "configure"
+#line 2941 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:2944: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:2946: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -2970,17 +2972,17 @@ for ac_hdr in sys/soundcard.h machine/soundcard.h
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:2974: checking for $ac_hdr" >&5
+echo "configure:2976: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 2979 "configure"
+#line 2981 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:2984: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:2986: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -3010,17 +3012,17 @@ for ac_hdr in dlfcn.h image.h
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:3014: checking for $ac_hdr" >&5
+echo "configure:3016: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3019 "configure"
+#line 3021 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:3024: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:3026: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -3050,17 +3052,17 @@ for ac_hdr in arpa/inet.h net/if.h netinet/in.h sys/socket.h
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:3054: checking for $ac_hdr" >&5
+echo "configure:3056: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3059 "configure"
+#line 3061 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:3064: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:3066: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -3090,17 +3092,17 @@ for ac_hdr in machine/param.h
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:3094: checking for $ac_hdr" >&5
+echo "configure:3096: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3099 "configure"
+#line 3101 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:3104: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:3106: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -3131,17 +3133,17 @@ for ac_hdr in cthreads.h pthread.h kernel/scheduler.h kernel/OS.h
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:3135: checking for $ac_hdr" >&5
+echo "configure:3137: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3140 "configure"
+#line 3142 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:3145: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:3147: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -3169,20 +3171,20 @@ done
 
 
 echo $ac_n "checking for ntohl in sys/param.h""... $ac_c" 1>&6
-echo "configure:3173: checking for ntohl in sys/param.h" >&5
+echo "configure:3175: checking for ntohl in sys/param.h" >&5
 if eval "test \"`echo '$''{'ac_cv_c_ntohl_sys_param_h'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   CFLAGS="${save_CFLAGS} -Wall -Werror"
      cat > conftest.$ac_ext <<EOF
-#line 3179 "configure"
+#line 3181 "configure"
 #include "confdefs.h"
 #include <sys/param.h>
 int main() {
 void foo() { int meuh; ntohl(meuh); }
 ; return 0; }
 EOF
-if { (eval echo configure:3186: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:3188: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_c_ntohl_sys_param_h=yes
 else
@@ -3203,20 +3205,20 @@ EOF
 fi
 
 echo $ac_n "checking if \$CC accepts -finline-limit""... $ac_c" 1>&6
-echo "configure:3207: checking if \$CC accepts -finline-limit" >&5
+echo "configure:3209: checking if \$CC accepts -finline-limit" >&5
 if eval "test \"`echo '$''{'ac_cv_c_inline_limit'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   CFLAGS="${save_CFLAGS} -finline-limit-30000"
      cat > conftest.$ac_ext <<EOF
-#line 3213 "configure"
+#line 3215 "configure"
 #include "confdefs.h"
 
 int main() {
 
 ; return 0; }
 EOF
-if { (eval echo configure:3220: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:3222: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_c_inline_limit=yes
 else
@@ -3234,20 +3236,20 @@ if test x"$ac_cv_c_inline_limit" != x"no"; then
 fi
 
 echo $ac_n "checking if \$CC accepts -bundle -undefined error""... $ac_c" 1>&6
-echo "configure:3238: checking if \$CC accepts -bundle -undefined error" >&5
+echo "configure:3240: checking if \$CC accepts -bundle -undefined error" >&5
 if eval "test \"`echo '$''{'ac_cv_ld_darwin'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   CFLAGS="${save_CFLAGS} -bundle -undefined error"
      cat > conftest.$ac_ext <<EOF
-#line 3244 "configure"
+#line 3246 "configure"
 #include "confdefs.h"
 
 int main() {
 
 ; return 0; }
 EOF
-if { (eval echo configure:3251: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:3253: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_ld_darwin=yes
 else
@@ -3265,20 +3267,20 @@ if test x"$ac_cv_ld_darwin" != x"no"; then
 fi
 
 echo $ac_n "checking if \$CC accepts -shared""... $ac_c" 1>&6
-echo "configure:3269: checking if \$CC accepts -shared" >&5
+echo "configure:3271: checking if \$CC accepts -shared" >&5
 if eval "test \"`echo '$''{'ac_cv_ld_plugins'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   CFLAGS="${save_CFLAGS} -shared"
      cat > conftest.$ac_ext <<EOF
-#line 3275 "configure"
+#line 3277 "configure"
 #include "confdefs.h"
 
 int main() {
 
 ; return 0; }
 EOF
-if { (eval echo configure:3282: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:3284: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_ld_plugins=yes
 else
@@ -3297,7 +3299,7 @@ fi
         
 if test x"${SOFLAGS}" = x; then
     echo $ac_n "checking for soname setting""... $ac_c" 1>&6
-echo "configure:3301: checking for soname setting" >&5
+echo "configure:3303: checking for soname setting" >&5
 if eval "test \"`echo '$''{'ac_cv_ld_soname'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -3306,14 +3308,14 @@ else
         try_SOFLAGS="-Wl,-soname -Wl,"
         LDFLAGS="${save_LDFLAGS} ${try_SOFLAGS}foo.so.0"
         cat > conftest.$ac_ext <<EOF
-#line 3310 "configure"
+#line 3312 "configure"
 #include "confdefs.h"
 
 int main() {
 
 ; return 0; }
 EOF
-if { (eval echo configure:3317: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:3319: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   ac_cv_ld_soname="${try_SOFLAGS}"
 else
@@ -3325,14 +3327,14 @@ else
             try_SOFLAGS="-Wl,-h -Wl,"
             LDFLAGS="${save_LDFLAGS} ${try_SOFLAGS}foo.so.0"
             cat > conftest.$ac_ext <<EOF
-#line 3329 "configure"
+#line 3331 "configure"
 #include "confdefs.h"
 
 int main() {
 
 ; return 0; }
 EOF
-if { (eval echo configure:3336: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:3338: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   ac_cv_ld_soname="${try_SOFLAGS}"
 else
@@ -3361,7 +3363,7 @@ have problems using libdvdcss.
 fi
 
 echo $ac_n "checking __attribute__ ((aligned ())) support""... $ac_c" 1>&6
-echo "configure:3365: checking __attribute__ ((aligned ())) support" >&5
+echo "configure:3367: checking __attribute__ ((aligned ())) support" >&5
 if eval "test \"`echo '$''{'ac_cv_c_attribute_aligned'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -3369,14 +3371,14 @@ else
        CFLAGS="${save_CFLAGS} -Werror"
     for ac_cv_c_attr_align_try in 2 4 8 16 32 64; do
         cat > conftest.$ac_ext <<EOF
-#line 3373 "configure"
+#line 3375 "configure"
 #include "confdefs.h"
 
 int main() {
 static char c __attribute__ ((aligned($ac_cv_c_attr_align_try))) = 0; return c;
 ; return 0; }
 EOF
-if { (eval echo configure:3380: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:3382: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_c_attribute_aligned=$ac_cv_c_attr_align_try
 else
@@ -3399,19 +3401,19 @@ CFLAGS="${save_CFLAGS}"
 LDFLAGS="${save_LDFLAGS}"
 
 echo $ac_n "checking for boolean_t in sys/types.h""... $ac_c" 1>&6
-echo "configure:3403: checking for boolean_t in sys/types.h" >&5
+echo "configure:3405: checking for boolean_t in sys/types.h" >&5
 if eval "test \"`echo '$''{'ac_cv_c_boolean_t_sys_types_h'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3408 "configure"
+#line 3410 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 int main() {
 boolean_t foo;
 ; return 0; }
 EOF
-if { (eval echo configure:3415: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:3417: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_c_boolean_t_sys_types_h=yes
 else
@@ -3432,19 +3434,19 @@ EOF
 fi
 
 echo $ac_n "checking for boolean_t in pthread.h""... $ac_c" 1>&6
-echo "configure:3436: checking for boolean_t in pthread.h" >&5
+echo "configure:3438: checking for boolean_t in pthread.h" >&5
 if eval "test \"`echo '$''{'ac_cv_c_boolean_t_pthread_h'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3441 "configure"
+#line 3443 "configure"
 #include "confdefs.h"
 #include <pthread.h>
 int main() {
 boolean_t foo;
 ; return 0; }
 EOF
-if { (eval echo configure:3448: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:3450: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_c_boolean_t_pthread_h=yes
 else
@@ -3465,19 +3467,19 @@ EOF
 fi
 
 echo $ac_n "checking for boolean_t in cthreads.h""... $ac_c" 1>&6
-echo "configure:3469: checking for boolean_t in cthreads.h" >&5
+echo "configure:3471: checking for boolean_t in cthreads.h" >&5
 if eval "test \"`echo '$''{'ac_cv_c_boolean_t_cthreads_h'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3474 "configure"
+#line 3476 "configure"
 #include "confdefs.h"
 #include <cthreads.h>
 int main() {
 boolean_t foo;
 ; return 0; }
 EOF
-if { (eval echo configure:3481: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:3483: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_c_boolean_t_cthreads_h=yes
 else
@@ -3498,12 +3500,12 @@ EOF
 fi
 
 echo $ac_n "checking for working const""... $ac_c" 1>&6
-echo "configure:3502: checking for working const" >&5
+echo "configure:3504: checking for working const" >&5
 if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3507 "configure"
+#line 3509 "configure"
 #include "confdefs.h"
 
 int main() {
@@ -3552,7 +3554,7 @@ ccp = (char const *const *) p;
 
 ; return 0; }
 EOF
-if { (eval echo configure:3556: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:3558: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_c_const=yes
 else
@@ -3573,12 +3575,12 @@ EOF
 fi
 
 echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6
-echo "configure:3577: checking for ANSI C header files" >&5
+echo "configure:3579: checking for ANSI C header files" >&5
 if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3582 "configure"
+#line 3584 "configure"
 #include "confdefs.h"
 #include <stdlib.h>
 #include <stdarg.h>
@@ -3586,7 +3588,7 @@ else
 #include <float.h>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:3590: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:3592: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -3603,7 +3605,7 @@ rm -f conftest*
 if test $ac_cv_header_stdc = yes; then
   # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
 cat > conftest.$ac_ext <<EOF
-#line 3607 "configure"
+#line 3609 "configure"
 #include "confdefs.h"
 #include <string.h>
 EOF
@@ -3621,7 +3623,7 @@ fi
 if test $ac_cv_header_stdc = yes; then
   # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
 cat > conftest.$ac_ext <<EOF
-#line 3625 "configure"
+#line 3627 "configure"
 #include "confdefs.h"
 #include <stdlib.h>
 EOF
@@ -3642,7 +3644,7 @@ if test "$cross_compiling" = yes; then
   :
 else
   cat > conftest.$ac_ext <<EOF
-#line 3646 "configure"
+#line 3648 "configure"
 #include "confdefs.h"
 #include <ctype.h>
 #define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
@@ -3653,7 +3655,7 @@ if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
 exit (0); }
 
 EOF
-if { (eval echo configure:3657: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:3659: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   :
 else
@@ -3677,12 +3679,12 @@ EOF
 fi
 
 echo $ac_n "checking for size_t""... $ac_c" 1>&6
-echo "configure:3681: checking for size_t" >&5
+echo "configure:3683: checking for size_t" >&5
 if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3686 "configure"
+#line 3688 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #if STDC_HEADERS
@@ -3710,12 +3712,12 @@ EOF
 fi
 
 echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6
-echo "configure:3714: checking whether time.h and sys/time.h may both be included" >&5
+echo "configure:3716: checking whether time.h and sys/time.h may both be included" >&5
 if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3719 "configure"
+#line 3721 "configure"
 #include "confdefs.h"
 #include <sys/types.h>
 #include <sys/time.h>
@@ -3724,7 +3726,7 @@ int main() {
 struct tm *tp;
 ; return 0; }
 EOF
-if { (eval echo configure:3728: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:3730: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_header_time=yes
 else
@@ -3758,19 +3760,19 @@ SSE_MODULES="imdctsse downmixsse"
 ALTIVEC_MODULES="idctaltivec motionaltivec"
 
 echo $ac_n "checking if \$CC groks MMX inline assembly""... $ac_c" 1>&6
-echo "configure:3762: checking if \$CC groks MMX inline assembly" >&5
+echo "configure:3764: checking if \$CC groks MMX inline assembly" >&5
 if eval "test \"`echo '$''{'ac_cv_mmx_inline'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3767 "configure"
+#line 3769 "configure"
 #include "confdefs.h"
 
 int main() {
 void *p;asm volatile("packuswb %%mm1,%%mm2"::"r"(p));
 ; return 0; }
 EOF
-if { (eval echo configure:3774: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:3776: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_mmx_inline=yes
 else
@@ -3788,19 +3790,19 @@ if test x"$ac_cv_mmx_inline" != x"no"; then
 fi
 
 echo $ac_n "checking if \$CC groks MMX EXT inline assembly""... $ac_c" 1>&6
-echo "configure:3792: checking if \$CC groks MMX EXT inline assembly" >&5
+echo "configure:3794: checking if \$CC groks MMX EXT inline assembly" >&5
 if eval "test \"`echo '$''{'ac_cv_mmxext_inline'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3797 "configure"
+#line 3799 "configure"
 #include "confdefs.h"
 
 int main() {
 void *p;asm volatile("maskmovq %%mm1,%%mm2"::"r"(p));
 ; return 0; }
 EOF
-if { (eval echo configure:3804: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:3806: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_mmxext_inline=yes
 else
@@ -3818,19 +3820,19 @@ if test x"$ac_cv_mmxext_inline" != x"no"; then
 fi
 
 echo $ac_n "checking if \$CC groks 3D Now! inline assembly""... $ac_c" 1>&6
-echo "configure:3822: checking if \$CC groks 3D Now! inline assembly" >&5
+echo "configure:3824: checking if \$CC groks 3D Now! inline assembly" >&5
 if eval "test \"`echo '$''{'ac_cv_3dnow_inline'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3827 "configure"
+#line 3829 "configure"
 #include "confdefs.h"
 
 int main() {
 void *p;asm volatile("pfadd %%mm1,%%mm2"::"r"(p));
 ; return 0; }
 EOF
-if { (eval echo configure:3834: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:3836: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_3dnow_inline=yes
 else
@@ -3852,19 +3854,19 @@ EOF
 fi
 
 echo $ac_n "checking if \$CC groks SSE inline assembly""... $ac_c" 1>&6
-echo "configure:3856: checking if \$CC groks SSE inline assembly" >&5
+echo "configure:3858: checking if \$CC groks SSE inline assembly" >&5
 if eval "test \"`echo '$''{'ac_cv_sse_inline'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3861 "configure"
+#line 3863 "configure"
 #include "confdefs.h"
 
 int main() {
 void *p;asm volatile("xorps %%xmm1,%%xmm2"::"r"(p));
 ; return 0; }
 EOF
-if { (eval echo configure:3868: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:3870: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_sse_inline=yes
 else
@@ -3886,19 +3888,19 @@ EOF
 fi
 
 echo $ac_n "checking if \$CC groks Altivec inline assembly""... $ac_c" 1>&6
-echo "configure:3890: checking if \$CC groks Altivec inline assembly" >&5
+echo "configure:3892: checking if \$CC groks Altivec inline assembly" >&5
 if eval "test \"`echo '$''{'ac_cv_altivec_inline'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 3895 "configure"
+#line 3897 "configure"
 #include "confdefs.h"
 
 int main() {
 asm volatile("vperm 0,1,2,3");
 ; return 0; }
 EOF
-if { (eval echo configure:3902: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:3904: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_altivec_inline=yes
 else
@@ -3908,14 +3910,14 @@ else
   save_CFLAGS=$CFLAGS
           CFLAGS="$CFLAGS -Wa,-m7400"
           cat > conftest.$ac_ext <<EOF
-#line 3912 "configure"
+#line 3914 "configure"
 #include "confdefs.h"
 
 int main() {
 asm volatile("vperm 0,1,2,3");
 ; return 0; }
 EOF
-if { (eval echo configure:3919: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:3921: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_altivec_inline=yes; CFLAGS_ALTIVEC="-Wa,-m7400"
 else
@@ -3941,7 +3943,7 @@ EOF
 fi
 
 echo $ac_n "checking if \$CC groks Altivec C extensions""... $ac_c" 1>&6
-echo "configure:3945: checking if \$CC groks Altivec C extensions" >&5
+echo "configure:3947: checking if \$CC groks Altivec C extensions" >&5
 if eval "test \"`echo '$''{'ac_cv_c_altivec'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -3949,14 +3951,14 @@ else
      CFLAGS="$CFLAGS -faltivec"
      # Darwin test
      cat > conftest.$ac_ext <<EOF
-#line 3953 "configure"
+#line 3955 "configure"
 #include "confdefs.h"
 
 int main() {
 vec_mtvscr((vector unsigned int)(0));
 ; return 0; }
 EOF
-if { (eval echo configure:3960: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:3962: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_c_altivec=-faltivec
 else
@@ -3967,14 +3969,14 @@ else
         # Linux/PPC test
         CFLAGS="$save_CFLAGS $CFLAGS_ALTIVEC -fvec"
         cat > conftest.$ac_ext <<EOF
-#line 3971 "configure"
+#line 3973 "configure"
 #include "confdefs.h"
 
 int main() {
 vec_mtvscr((vector unsigned int)(0));
 ; return 0; }
 EOF
-if { (eval echo configure:3978: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+if { (eval echo configure:3980: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
   rm -rf conftest*
   ac_cv_c_altivec="-fvec"
 else
@@ -4002,21 +4004,21 @@ EOF
 fi
 
 echo $ac_n "checking if linker needs -framework vecLib""... $ac_c" 1>&6
-echo "configure:4006: checking if linker needs -framework vecLib" >&5
+echo "configure:4008: checking if linker needs -framework vecLib" >&5
 if eval "test \"`echo '$''{'ac_cv_ld_altivec'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   save_LDFLAGS=$LDFLAGS
      LDFLAGS="$LDFLAGS -framework vecLib"
      cat > conftest.$ac_ext <<EOF
-#line 4013 "configure"
+#line 4015 "configure"
 #include "confdefs.h"
 
 int main() {
 
 ; return 0; }
 EOF
-if { (eval echo configure:4020: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:4022: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   ac_cv_ld_altivec=yes
 else
@@ -4054,7 +4056,7 @@ if test $SYS = mingw32; then
 # Extract the first word of "${ac_tool_prefix}windres", so it can be a program name with args.
 set dummy ${ac_tool_prefix}windres; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:4058: checking for $ac_word" >&5
+echo "configure:4060: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_WINDRES'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -4086,7 +4088,7 @@ if test -n "$ac_tool_prefix"; then
   # Extract the first word of "windres", so it can be a program name with args.
 set dummy windres; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:4090: checking for $ac_word" >&5
+echo "configure:4092: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_WINDRES'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -4127,17 +4129,17 @@ for ac_hdr in winioctl.h
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:4131: checking for $ac_hdr" >&5
+echo "configure:4133: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 4136 "configure"
+#line 4138 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:4141: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:4143: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -4170,17 +4172,17 @@ for ac_hdr in sys/ioctl.h
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:4174: checking for $ac_hdr" >&5
+echo "configure:4176: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 4179 "configure"
+#line 4181 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:4184: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:4186: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -4206,17 +4208,17 @@ EOF
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:4210: checking for $ac_hdr" >&5
+echo "configure:4212: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 4215 "configure"
+#line 4217 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:4220: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:4222: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -4246,7 +4248,7 @@ done
   LINUX_DVD_STRUCT=0
   OPENBSD_DVD_STRUCT=0
         cat > conftest.$ac_ext <<EOF
-#line 4250 "configure"
+#line 4252 "configure"
 #include "confdefs.h"
 #include <sys/cdio.h>
 EOF
@@ -4259,7 +4261,7 @@ if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
 EOF
 
     cat > conftest.$ac_ext <<EOF
-#line 4263 "configure"
+#line 4265 "configure"
 #include "confdefs.h"
 #include <sys/cdio.h>
 EOF
@@ -4279,7 +4281,7 @@ fi
 rm -f conftest*
 
         cat > conftest.$ac_ext <<EOF
-#line 4283 "configure"
+#line 4285 "configure"
 #include "confdefs.h"
 #include <sys/dvdio.h>
 EOF
@@ -4292,7 +4294,7 @@ if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
 EOF
 
     cat > conftest.$ac_ext <<EOF
-#line 4296 "configure"
+#line 4298 "configure"
 #include "confdefs.h"
 #include <sys/dvdio.h>
 EOF
@@ -4312,7 +4314,7 @@ fi
 rm -f conftest*
 
         cat > conftest.$ac_ext <<EOF
-#line 4316 "configure"
+#line 4318 "configure"
 #include "confdefs.h"
 #include <linux/cdrom.h>
 EOF
@@ -4331,7 +4333,7 @@ rm -f conftest*
 
         NEED_BSDI_LIBDVD=0
   cat > conftest.$ac_ext <<EOF
-#line 4335 "configure"
+#line 4337 "configure"
 #include "confdefs.h"
 #include <dvd.h>
 EOF
@@ -4353,17 +4355,17 @@ else
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:4357: checking for $ac_hdr" >&5
+echo "configure:4359: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 4362 "configure"
+#line 4364 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:4367: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:4369: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -4403,17 +4405,17 @@ rm -f conftest*
 
         ac_safe=`echo "sys/scsi/scsi_types.h" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for sys/scsi/scsi_types.h""... $ac_c" 1>&6
-echo "configure:4407: checking for sys/scsi/scsi_types.h" >&5
+echo "configure:4409: checking for sys/scsi/scsi_types.h" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 4412 "configure"
+#line 4414 "configure"
 #include "confdefs.h"
 #include <sys/scsi/scsi_types.h>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:4417: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:4419: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -4432,17 +4434,17 @@ if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
   
     ac_safe=`echo "sys/scsi/impl/uscsi.h" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for sys/scsi/impl/uscsi.h""... $ac_c" 1>&6
-echo "configure:4436: checking for sys/scsi/impl/uscsi.h" >&5
+echo "configure:4438: checking for sys/scsi/impl/uscsi.h" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 4441 "configure"
+#line 4443 "configure"
 #include "confdefs.h"
 #include <sys/scsi/impl/uscsi.h>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:4446: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:4448: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -4597,7 +4599,7 @@ if test "${enable_pth+set}" = set; then
   enableval="$enable_pth"
    if test x$enableval = xyes; then
     echo $ac_n "checking for pth_init in -lpth""... $ac_c" 1>&6
-echo "configure:4601: checking for pth_init in -lpth" >&5
+echo "configure:4603: checking for pth_init in -lpth" >&5
 ac_lib_var=`echo pth'_'pth_init | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -4605,7 +4607,7 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lpth  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 4609 "configure"
+#line 4611 "configure"
 #include "confdefs.h"
 /* Override any gcc2 internal prototype to avoid an error.  */
 /* We use char because int might match the return type of a gcc2
@@ -4616,7 +4618,7 @@ int main() {
 pth_init()
 ; return 0; }
 EOF
-if { (eval echo configure:4620: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:4622: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -4644,7 +4646,7 @@ else
 fi
 
     cat > conftest.$ac_ext <<EOF
-#line 4648 "configure"
+#line 4650 "configure"
 #include "confdefs.h"
 #include <pth.h>
 EOF
@@ -4741,6 +4743,26 @@ else
 fi
 
 
+# Check whether --enable-dvdread or --disable-dvdread was given.
+if test "${enable_dvdread+set}" = set; then
+  enableval="$enable_dvdread"
+   if test x$enableval = xyes
+  then
+    NEED_LIBDVDCSS=1
+    STATIC_LIBDVDCSS=1
+    BUILTINS="${BUILTINS} dvdread" 
+    CFLAGS_DVDREAD="${CFLAGS_DVDREAD} -I../../extras/libdvdread"
+    LIB_DVDREAD="${LIB_DVDREAD} lib/libdvdread.a lib/libdvdcss.a"
+  fi 
+else
+   NEED_LIBDVDCSS=1
+  STATIC_LIBDVDCSS=1
+  BUILTINS="${BUILTINS} dvdread"
+  CFLAGS_DVDREAD="${CFLAGS_DVDREAD} -I../../extras/libdvdread"
+  LIB_DVDREAD="${LIB_DVDREAD} lib/libdvdread.a lib/libdvdcss.a" 
+fi
+
+
 # Check whether --enable-vcd or --disable-vcd was given.
 if test "${enable_vcd+set}" = set; then
   enableval="$enable_vcd"
@@ -4751,7 +4773,7 @@ fi
 if test x$enable_vcd != xno
 then
   cat > conftest.$ac_ext <<EOF
-#line 4755 "configure"
+#line 4777 "configure"
 #include "confdefs.h"
 #include <linux/cdrom.h>
 EOF
@@ -4839,7 +4861,7 @@ if test "${enable_esd+set}" = set; then
      # Extract the first word of "esd-config", so it can be a program name with args.
 set dummy esd-config; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:4843: checking for $ac_word" >&5
+echo "configure:4865: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_path_ESD_CONFIG'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -4890,7 +4912,7 @@ if test "${enable_arts+set}" = set; then
      # Extract the first word of "artsc-config", so it can be a program name with args.
 set dummy artsc-config; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:4894: checking for $ac_word" >&5
+echo "configure:4916: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_path_ARTS_CONFIG'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -4957,17 +4979,17 @@ else
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:4961: checking for $ac_hdr" >&5
+echo "configure:4983: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 4966 "configure"
+#line 4988 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:4971: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:4993: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -5011,17 +5033,17 @@ fi
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:5015: checking for $ac_hdr" >&5
+echo "configure:5037: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 5020 "configure"
+#line 5042 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:5025: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:5047: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -5099,7 +5121,7 @@ fi
   # Extract the first word of "sdl12-config", so it can be a program name with args.
 set dummy sdl12-config; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:5103: checking for $ac_word" >&5
+echo "configure:5125: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_path_SDL12_CONFIG'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -5139,7 +5161,7 @@ fi
     # Extract the first word of "sdl11-config", so it can be a program name with args.
 set dummy sdl11-config; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:5143: checking for $ac_word" >&5
+echo "configure:5165: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_path_SDL11_CONFIG'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -5180,7 +5202,7 @@ fi
     # Extract the first word of "sdl-config", so it can be a program name with args.
 set dummy sdl-config; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:5184: checking for $ac_word" >&5
+echo "configure:5206: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_path_SDL_CONFIG'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -5226,17 +5248,17 @@ fi
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:5230: checking for $ac_hdr" >&5
+echo "configure:5252: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 5235 "configure"
+#line 5257 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:5240: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:5262: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -5304,17 +5326,17 @@ fi
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:5308: checking for $ac_hdr" >&5
+echo "configure:5330: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 5313 "configure"
+#line 5335 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:5318: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:5340: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -5347,7 +5369,7 @@ done
        fi
       else
         echo $ac_n "checking for directX headers in ${enableval}""... $ac_c" 1>&6
-echo "configure:5351: checking for directX headers in ${enableval}" >&5
+echo "configure:5373: checking for directX headers in ${enableval}" >&5
         if test -f ${enableval}/include/directx.h
         then
           LIB_DIRECTX="-L${enableval}/lib -lgdi32 -ldxguid"
@@ -5366,17 +5388,17 @@ else
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:5370: checking for $ac_hdr" >&5
+echo "configure:5392: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 5375 "configure"
+#line 5397 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:5380: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:5402: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -5476,7 +5498,7 @@ if test "${enable_gnome+set}" = set; then
     # Extract the first word of "gnome-config", so it can be a program name with args.
 set dummy gnome-config; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:5480: checking for $ac_word" >&5
+echo "configure:5502: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_path_GNOME_CONFIG'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -5521,17 +5543,17 @@ fi
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:5525: checking for $ac_hdr" >&5
+echo "configure:5547: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 5530 "configure"
+#line 5552 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:5535: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:5557: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -5591,7 +5613,7 @@ fi
   # Extract the first word of "gtk-config", so it can be a program name with args.
 set dummy gtk-config; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:5595: checking for $ac_word" >&5
+echo "configure:5617: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_path_GTK_CONFIG'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -5636,17 +5658,17 @@ fi
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:5640: checking for $ac_hdr" >&5
+echo "configure:5662: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 5645 "configure"
+#line 5667 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:5650: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:5672: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -5698,17 +5720,17 @@ if test x$enable_x11 != xno &&
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:5702: checking for $ac_hdr" >&5
+echo "configure:5724: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 5707 "configure"
+#line 5729 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:5712: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:5734: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -5761,17 +5783,17 @@ if test x$enable_xvideo != xno &&
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
-echo "configure:5765: checking for $ac_hdr" >&5
+echo "configure:5787: checking for $ac_hdr" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 5770 "configure"
+#line 5792 "configure"
 #include "confdefs.h"
 #include <$ac_hdr>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:5775: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:5797: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -5811,17 +5833,17 @@ if test "${enable_alsa+set}" = set; then
    then
      ac_safe=`echo "sys/asoundlib.h" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for sys/asoundlib.h""... $ac_c" 1>&6
-echo "configure:5815: checking for sys/asoundlib.h" >&5
+echo "configure:5837: checking for sys/asoundlib.h" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 5820 "configure"
+#line 5842 "configure"
 #include "confdefs.h"
 #include <sys/asoundlib.h>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:5825: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:5847: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -5838,7 +5860,7 @@ fi
 if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
   echo "$ac_t""yes" 1>&6
   echo $ac_n "checking for main in -lasound""... $ac_c" 1>&6
-echo "configure:5842: checking for main in -lasound" >&5
+echo "configure:5864: checking for main in -lasound" >&5
 ac_lib_var=`echo asound'_'main | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -5846,14 +5868,14 @@ else
   ac_save_LIBS="$LIBS"
 LIBS="-lasound  $LIBS"
 cat > conftest.$ac_ext <<EOF
-#line 5850 "configure"
+#line 5872 "configure"
 #include "confdefs.h"
 
 int main() {
 main()
 ; return 0; }
 EOF
-if { (eval echo configure:5857: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+if { (eval echo configure:5879: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
   rm -rf conftest*
   eval "ac_cv_lib_$ac_lib_var=yes"
 else
@@ -5962,6 +5984,9 @@ fi
 
 
 
+
+
+
 
 
 
@@ -6160,6 +6185,8 @@ s%@LIB_BEOS@%$LIB_BEOS%g
 s%@LIB_DARWIN@%$LIB_DARWIN%g
 s%@LIB_DVD@%$LIB_DVD%g
 s%@LIB_DVD_PLUGIN@%$LIB_DVD_PLUGIN%g
+s%@LIB_DVDREAD@%$LIB_DVDREAD%g
+s%@LIB_DVDREAD_PLUGIN@%$LIB_DVDREAD_PLUGIN%g
 s%@LIB_ESD@%$LIB_ESD%g
 s%@LIB_GGI@%$LIB_GGI%g
 s%@LIB_GLIDE@%$LIB_GLIDE%g
@@ -6180,6 +6207,7 @@ s%@LIB_YUV@%$LIB_YUV%g
 s%@CFLAGS_VLC@%$CFLAGS_VLC%g
 s%@CFLAGS_ALTIVEC@%$CFLAGS_ALTIVEC%g
 s%@CFLAGS_DVD@%$CFLAGS_DVD%g
+s%@CFLAGS_DVDREAD@%$CFLAGS_DVDREAD%g
 s%@CFLAGS_LIBDVDCSS@%$CFLAGS_LIBDVDCSS%g
 s%@CFLAGS_ARTS@%$CFLAGS_ARTS%g
 s%@CFLAGS_ESD@%$CFLAGS_ESD%g
index 3e98da498ebbe8a3522bc29d3577c946b6e1bd24..4d42cc3f350dd0de6b8522cf26857de92207377d 100644 (file)
@@ -683,6 +683,25 @@ AC_ARG_WITH(dvdcss,
       LIB_DVD_PLUGIN="${LIB_DVD_PLUGIN} -ldl"
     fi ])
 
+dnl
+dnl DVDREAD module: check for libdvdread plugin
+dnl
+AC_ARG_ENABLE(dvdread,
+[  --enable-dvdread          Enable dvdread support (default enabled)],
+[ if test x$enableval = xyes
+  then
+    NEED_LIBDVDCSS=1
+    STATIC_LIBDVDCSS=1
+    BUILTINS="${BUILTINS} dvdread" 
+    CFLAGS_DVDREAD="${CFLAGS_DVDREAD} -I../../extras/libdvdread"
+    LIB_DVDREAD="${LIB_DVDREAD} lib/libdvdread.a lib/libdvdcss.a"
+  fi ],
+[ NEED_LIBDVDCSS=1
+  STATIC_LIBDVDCSS=1
+  BUILTINS="${BUILTINS} dvdread"
+  CFLAGS_DVDREAD="${CFLAGS_DVDREAD} -I../../extras/libdvdread"
+  LIB_DVDREAD="${LIB_DVDREAD} lib/libdvdread.a lib/libdvdcss.a" ])
+
 dnl
 dnl  VCD module
 dnl
@@ -1186,6 +1205,8 @@ AC_SUBST(LIB_BEOS)
 AC_SUBST(LIB_DARWIN)
 AC_SUBST(LIB_DVD)
 AC_SUBST(LIB_DVD_PLUGIN)
+AC_SUBST(LIB_DVDREAD)
+AC_SUBST(LIB_DVDREAD_PLUGIN)
 AC_SUBST(LIB_ESD)
 AC_SUBST(LIB_GGI)
 AC_SUBST(LIB_GLIDE)
@@ -1207,6 +1228,7 @@ AC_SUBST(LIB_YUV)
 AC_SUBST(CFLAGS_VLC)
 AC_SUBST(CFLAGS_ALTIVEC)
 AC_SUBST(CFLAGS_DVD)
+AC_SUBST(CFLAGS_DVDREAD)
 AC_SUBST(CFLAGS_LIBDVDCSS)
 AC_SUBST(CFLAGS_ARTS)
 AC_SUBST(CFLAGS_ESD)
diff --git a/extras/libdvdread/.cvsignore b/extras/libdvdread/.cvsignore
new file mode 100644 (file)
index 0000000..63e7180
--- /dev/null
@@ -0,0 +1 @@
+.dep
diff --git a/extras/libdvdread/Makefile b/extras/libdvdread/Makefile
new file mode 100644 (file)
index 0000000..cdc9b4b
--- /dev/null
@@ -0,0 +1,72 @@
+###############################################################################
+# vlc (VideoLAN Client) dvdread module Makefile
+# (c)2001 VideoLAN
+###############################################################################
+
+LIBDVDREAD_VERSION := 0.9.2
+CFLAGS_LIBDVDREAD := -I. -DHAVE_CONFIG_H
+LIB_LIBDVDREAD := -ldl
+
+BASE_A := libdvdread.a
+BASE_SO := libdvdread.so
+MAJOR_SO := libdvdread.so.$(shell echo $(LIBDVDREAD_VERSION) | cut -f1 -d.)
+FULL_SO := libdvdread.so.$(LIBDVDREAD_VERSION)
+ifneq (,$(SOFLAGS))
+ALL_SOFLAGS := $(SOFLAGS)$(MAJOR_SO)
+SOFLAGS :=
+endif
+
+#
+# Objects
+#
+
+OBJ_C = dvdread.o dvd_udf.o nav_print.o nav_read.o ifo_print.o ifo_read.o
+
+PLUGIN_OBJ = $(OBJ_C) $(OBJ_LIBDVDREAD)
+BUILTIN_OBJ = $(OBJ_C:%.o=DVDREAD_%.o)
+
+ALL_OBJ = $(PLUGIN_OBJ) $(BUILTIN_OBJ)
+
+include ../../Makefile.modules
+
+$(OBJ_C): %.o: .dep/%.d
+$(OBJ_C): %.o: %.c
+       $(CC) $(CFLAGS) $(CFLAGS_LIBDVDREAD) $(PCFLAGS) -c -o $@ $<
+
+$(BUILTIN_OBJ): DVDREAD_%.o: .dep/%.d
+$(BUILTIN_OBJ): DVDREAD_%.o: %.c
+       $(CC) $(CFLAGS) $(CFLAGS_LIBDVDREAD) $(PCFLAGS) -c -o $@ $<
+
+../../lib/$(FULL_SO): $(OBJ_C) $(OBJ_LIBDVDREAD)
+       $(CC) $(PCFLAGS) $(ALL_SOFLAGS) -o $@ $^ $(PLCFLAGS) $(LIB_LIBDVDREAD)
+       rm -f ../../lib/$(BASE_SO) && ln -s $(FULL_SO) ../../lib/$(BASE_SO)
+       rm -f ../../lib/$(MAJOR_SO) && ln -s $(FULL_SO) ../../lib/$(MAJOR_SO)
+
+../../lib/$(BASE_A): libdvdcss $(BUILTIN_OBJ) $(OBJ_LIBDVDREAD)
+       ar r $@ $(BUILTIN_OBJ)
+       $(RANLIB) $@
+
+#
+# Virtual targets
+#
+all: ../../lib/$(BASE_A)
+
+libdvdcss:
+       cd ../../ && $(MAKE) libdvdcss
+
+#install:
+#      mkdir -p $(DESTDIR)$(includedir)/videolan
+#      $(INSTALL) -m 644 videolan/dvdcss.h $(DESTDIR)$(includedir)/videolan
+#      mkdir -p $(DESTDIR)$(libdir)
+#      -$(INSTALL) -m 644 ../../lib/$(BASE_A) $(DESTDIR)$(libdir)
+#      -$(INSTALL) -m 644 ../../lib/$(FULL_SO) $(DESTDIR)$(libdir)
+#      rm -f $(DESTDIR)$(libdir)/$(BASE_SO) && ln -s $(FULL_SO) $(DESTDIR)$(libdir)/$(BASE_SO)
+#      rm -f $(DESTDIR)$(libdir)/$(MAJOR_SO) && ln -s $(FULL_SO) $(DESTDIR)$(libdir)/$(MAJOR_SO)
+#
+#uninstall:
+#      rm -f $(DESTDIR)$(includedir)/videolan/dvdcss.h
+#      rm -f $(DESTDIR)$(libdir)/$(BASE_A)
+#      rm -f $(DESTDIR)$(libdir)/$(BASE_SO)
+#      rm -f $(DESTDIR)$(libdir)/$(MAJOR_SO)
+#      rm -f $(DESTDIR)$(libdir)/$(FULL_SO)
+
diff --git a/extras/libdvdread/bswap.h b/extras/libdvdread/bswap.h
new file mode 100644 (file)
index 0000000..23e2863
--- /dev/null
@@ -0,0 +1,87 @@
+/**
+ * Copyright (C) 2000, 2001 Billy Biggs <vektor@dumbterm.net>,
+ *                          Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BSWAP_H_INCLUDED
+#define BSWAP_H_INCLUDED
+
+#include <config.h>
+
+#if defined(WORDS_BIGENDIAN)
+/* All bigendian systems are fine, just ignore the swaps. */  
+#define B2N_16(x) (void)(x)
+#define B2N_32(x) (void)(x)
+#define B2N_64(x) (void)(x)
+
+#else 
+
+#if defined(__linux__)
+#include <byteswap.h>
+#define B2N_16(x) x = bswap_16(x)
+#define B2N_32(x) x = bswap_32(x)
+#define B2N_64(x) x = bswap_64(x)
+
+#elif defined(__NetBSD__)
+#include <sys/endian.h>
+#define B2N_16(x) BE16TOH(x)
+#define B2N_32(x) BE32TOH(x)
+#define B2N_64(x) BE64TOH(x)
+
+#elif defined(__OpenBSD__)
+#include <sys/endian.h>
+#define B2N_16(x) x = swap16(x)
+#define B2N_32(x) x = swap32(x)
+#define B2N_64(x) x = swap64(x)
+
+/* This is a slow but portable implementation, it has multiple evaluation 
+ * problems so beware.
+ * FreeBSD and Solaris don't have <byteswap.h> or any other such 
+ * functionality! 
+ */
+
+#elif defined(__FreeBSD__) || defined(__sun) || defined(__bsdi__)
+#define B2N_16(x) \
+ x = ((((x) & 0xff00) >> 8) | \
+      (((x) & 0x00ff) << 8))
+#define B2N_32(x) \
+ x = ((((x) & 0xff000000) >> 24) | \
+      (((x) & 0x00ff0000) >>  8) | \
+      (((x) & 0x0000ff00) <<  8) | \
+      (((x) & 0x000000ff) << 24))
+#define B2N_64(x) \
+ x = ((((x) & 0xff00000000000000) >> 56) | \
+      (((x) & 0x00ff000000000000) >> 40) | \
+      (((x) & 0x0000ff0000000000) >> 24) | \
+      (((x) & 0x000000ff00000000) >>  8) | \
+      (((x) & 0x00000000ff000000) <<  8) | \
+      (((x) & 0x0000000000ff0000) << 24) | \
+      (((x) & 0x000000000000ff00) << 40) | \
+      (((x) & 0x00000000000000ff) << 56))
+
+#else
+
+/* If there isn't a header provided with your system with this functionality
+ * add the relevant || define( ) to the portable implementation above.
+ */
+#error "You need to add endian swap macros for you're system"
+
+#endif
+
+#endif /* WORDS_BIGENDIAN */
+
+#endif /* BSWAP_H_INCLUDED */
diff --git a/extras/libdvdread/dvd_reader.c b/extras/libdvdread/dvd_reader.c
new file mode 100644 (file)
index 0000000..5402a6d
--- /dev/null
@@ -0,0 +1,993 @@
+/**
+ * Copyright (C) 2001 Billy Biggs <vektor@dumbterm.net>.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h> /* For the timing of dvdcss_title crack. */
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <dlfcn.h>
+#include <dirent.h>
+
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__)
+#define SYS_BSD 1
+#endif
+
+#if defined(__sun)
+#include <sys/mnttab.h>
+#elif defined(SYS_BSD)
+#include <fstab.h>
+#elif defined(__linux__)
+#include <mntent.h>
+#endif
+
+#if defined(SYS_BSD)
+typedef off_t off64_t;
+#define lseek64 lseek
+#define stat64 stat
+#endif
+
+/* #include "dvdcss.h" */
+typedef struct dvdcss_s* dvdcss_handle;
+#define DVDCSS_NOFLAGS         0
+#define DVDCSS_INIT_QUIET      (1 << 0)
+#define DVDCSS_INIT_DEBUG      (1 << 1)
+#define DVDCSS_READ_DECRYPT    (1 << 0)
+
+#include "dvd_udf.h"
+#include "dvd_reader.h"
+
+/**
+ * Handle to the loaded dvdcss library.
+ */
+void *dvdcss_library = 0;
+
+/**
+ * libdvdcss functions.
+ */
+static dvdcss_handle (*dvdcss_open)  ( char *psz_target,
+                                       int i_flags );
+static int           (*dvdcss_close) ( dvdcss_handle );
+static int           (*dvdcss_title) ( dvdcss_handle,
+                                       int i_block );
+static int           (*dvdcss_seek)  ( dvdcss_handle,
+                                       int i_blocks );
+static int           (*dvdcss_read)  ( dvdcss_handle,
+                                       void *p_buffer,
+                                       int i_blocks,
+                                       int i_title );
+static char *        (*dvdcss_error) ( dvdcss_handle );
+
+struct dvd_reader_s {
+    /* Basic information. */
+    int isImageFile;
+
+    /* Information required for an image file. */
+    dvdcss_handle dev;
+    int init_keys;
+    int fd;
+
+    /* Information required for a directory path drive. */
+    char *path_root;
+};
+
+struct dvd_file_s {
+    /* Basic information. */
+    dvd_reader_t *dvd;
+
+    /* 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 ];
+    int title_fds[ 9 ];
+
+    /* Calculated at open-time, size in blocks. */
+    ssize_t filesize;
+};
+
+static void setupCSS( void )
+{
+    if( !dvdcss_library ) {
+       dvdcss_library = dlopen( "libdvdcss.so.0", RTLD_LAZY );
+
+       if( !dvdcss_library ) {
+            fprintf( stderr, "libdvdread: Can't open libdvdcss: %s.\n",
+                     dlerror() );
+        } else {
+#if defined(__OpenBSD__)
+#define U_S "_"
+#else
+#define U_S
+#endif
+            dvdcss_open = (dvdcss_handle (*)(char*, int))
+                                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))
+                                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" );
+
+            if( dlsym( dvdcss_library, U_S "dvdcss_crack" ) ) {
+                fprintf( stderr, "libdvdread: Old (pre-0.0.2) version of "
+                                 "libdvdcss found.\n"
+                                 "libdvdread: You should get the "
+                                 "latest version from "
+                                 "http://www.videolan.org/\n" );
+                dlclose( dvdcss_library );
+                dvdcss_library = 0;
+            } else if( !dvdcss_open  || !dvdcss_close || !dvdcss_seek ||
+                       !dvdcss_title || !dvdcss_read  || !dvdcss_error ) {
+
+                fprintf( stderr, "libdvdread: Unknown incompatible version "
+                                 "of libdvdcss found.\n"
+                                 "libdvdread: Try to find a "
+                                 "newer version of libdvdread?\n" );
+                dlclose( dvdcss_library );
+                dvdcss_library = 0;
+            }
+        }
+    }
+
+    if( !dvdcss_library ) {
+        fprintf( stderr, "libdvdread: Encrypted DVD support unavailable.\n" );
+    }
+}
+
+
+/* Loop over all titles and call dvdcss_title to crack the keys. */
+static int initAllCSSKeys( dvd_reader_t *dvd )
+{
+    
+    if( dvdcss_library ) {
+        struct timeval all_s, all_e;
+       struct timeval t_s, t_e;
+       char filename[ MAX_UDF_FILE_NAME_LEN ];
+       uint32_t start, len;
+       int title;
+       
+       fprintf( stderr, "\n" );
+       fprintf( stderr, "libdvdread: Attempting to retrieve all CSS keys\n" );
+       fprintf( stderr, "libdvdread: This can take a _long_ time, "
+                "please be patient\n\n" );
+       
+       gettimeofday(&all_s, NULL);
+       
+       for( title = 0; title < 100; title++ ) {
+           gettimeofday( &t_s, NULL );
+           if( title == 0 ) {
+               sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
+           } else {
+               sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 0 );
+           }
+           start = UDFFindFile( dvd, filename, &len );
+           if( start != 0 && len != 0 ) {
+               /* Perform CSS key cracking for this title. */
+               fprintf( stderr, "libdvdread: Get key for %s at 0x%08x\n", 
+                        filename, start );
+               if( dvdcss_title( dvd->dev, (int)start ) < 0 ) {
+                   fprintf( stderr, "libdvdread: Error cracking CSS key!!\n");
+               }
+               gettimeofday( &t_e, NULL );
+               fprintf( stderr, "libdvdread: Elapsed time %ld\n",  
+                        (long int) t_e.tv_sec - t_s.tv_sec );
+           }
+           
+           if( title == 0 ) continue;
+           
+           gettimeofday( &t_s, NULL );
+           sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 1 );
+           start = UDFFindFile( dvd, filename, &len );
+           if( start == 0 || len == 0 ) break;
+           
+           /* Perform CSS key cracking for this title. */
+           fprintf( stderr, "libdvdread: Get key for %s at 0x%08x\n", 
+                    filename, start );
+           if( dvdcss_title( dvd->dev, (int)start ) < 0 ) {
+               fprintf( stderr, "libdvdread: Error cracking CSS key!!\n");
+           }
+           gettimeofday( &t_e, NULL );
+           fprintf( stderr, "libdvdread: Elapsed time %ld\n",  
+                    (long int) t_e.tv_sec - t_s.tv_sec );
+       }
+       title--;
+       
+       fprintf( stderr, "libdvdread: Found %d VTS's\n", title );
+       gettimeofday(&all_e, NULL);
+       fprintf( stderr, "libdvdread: Elapsed time %ld\n",  
+                (long int) all_e.tv_sec - all_s.tv_sec );
+    }
+    
+    return 0;
+}
+
+
+
+/**
+ * Open a DVD image or block device file.
+ */
+static dvd_reader_t *DVDOpenImageFile( const char *location )
+{
+    dvd_reader_t *dvd;
+    dvdcss_handle dev = 0;
+    int fd = -1;
+
+    setupCSS();
+    
+    if( dvdcss_library ) {
+        dev = dvdcss_open( (char *) location, DVDCSS_INIT_DEBUG );
+        if( !dev ) {
+            fprintf( stderr, "libdvdread: Can't open %s for reading.\n",
+                     location );
+            return 0;
+        }
+    } else {
+        fd = open( location, O_RDONLY );
+        if( fd < 0 ) {
+            fprintf( stderr, "libdvdread: Can't open %s for reading.\n",
+                     location );
+            return 0;
+        }
+    }
+
+    dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
+    if( !dvd ) return 0;
+    dvd->isImageFile = 1;
+    dvd->dev = dev;
+    dvd->init_keys = 0;
+    dvd->fd = fd;
+    dvd->path_root = 0;
+    
+    return dvd;
+}
+
+static dvd_reader_t *DVDOpenPath( const char *path_root )
+{
+    dvd_reader_t *dvd;
+
+    dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
+    if( !dvd ) return 0;
+    dvd->isImageFile = 0;
+    dvd->dev = 0;
+    dvd->init_keys = 0;
+    dvd->fd = -1;
+    dvd->path_root = strdup( path_root );
+
+    return dvd;
+}
+
+#if defined(__sun)
+/* /dev/rdsk/c0t6d0s0 (link to /devices/...)
+   /vol/dev/rdsk/c0t6d0/??
+   /vol/rdsk/<name> */
+static char *sun_block2char( const char *path )
+{
+    char *new_path;
+
+    /* Must contain "/dsk/" */ 
+    if( !strstr( path, "/dsk/" ) ) return (char *) strdup( path );
+
+    /* Replace "/dsk/" with "/rdsk/" */
+    new_path = malloc( strlen(path) + 2 );
+    strcpy( new_path, path );
+    strcpy( strstr( new_path, "/dsk/" ), "" );
+    strcat( new_path, "/rdsk/" );
+    strcat( new_path, strstr( path, "/dsk/" ) + strlen( "/dsk/" ) );
+
+    return new_path;
+}
+#endif
+
+#if defined(SYS_BSD)
+/* FreeBSD /dev/(r)(a)cd0 (a is for atapi, should work without r)
+   OpenBSD /dev/rcd0c
+   NetBSD  /dev/rcd0d or /dev/rcd0c (for non x86)
+   BSD/OS  /dev/sr0 (if not mounted) or /dev/rsr0 */
+static char *bsd_block2char( const char *path )
+{
+    char *new_path;
+
+    /* If it doesn't start with "/dev/" or does start with "/dev/r" exit */ 
+    if( !strncmp( path, "/dev/",  5 ) || strncmp( path, "/dev/r", 6 ) ) 
+      return (char *) strdup( path );
+
+    /* Replace "/dev/" with "/dev/r" */
+    new_path = malloc( strlen(path) + 2 );
+    strcpy( new_path, "/dev/r" );
+    strcat( new_path, path + strlen( "/dev/" ) );
+
+    return new_path;
+}
+#endif
+
+dvd_reader_t *DVDOpen( const char *path )
+{
+    struct stat64 fileinfo;
+    int ret;
+
+    if( !path ) return 0;
+
+    ret = stat64( path, &fileinfo );
+    if( ret < 0 ) {
+       /* If we can't stat the file, give up */
+       fprintf( stderr, "libdvdread: Can't stat %s\n", path );
+       perror("");
+       return 0;
+    }
+
+    /* First check if this is a block/char device or a file*/
+    if( S_ISBLK( fileinfo.st_mode ) || 
+       S_ISCHR( fileinfo.st_mode ) || 
+       S_ISREG( fileinfo.st_mode ) ) {
+
+       /**
+        * Block devices and regular files are assumed to be DVD-Video images.
+        */
+#if defined(__sun)
+       return DVDOpenImageFile( sun_block2char( path ) );
+#elif defined(SYS_BSD)
+       return DVDOpenImageFile( bsd_block2char( path ) );
+#else
+       return DVDOpenImageFile( path );
+#endif
+
+    } else if( S_ISDIR( fileinfo.st_mode ) ) {
+       dvd_reader_t *auth_drive = 0;
+       char *path_copy;
+#if defined(SYS_BSD)
+       struct fstab* fe;
+#elif defined(__sun) || defined(__linux__)
+       FILE *mntfile;
+#endif
+
+       /* XXX: We should scream real loud here. */
+       if( !(path_copy = strdup( path ) ) ) return 0;
+
+       /* Resolve any symlinks and get the absolut dir name. */
+       {
+           char *new_path;
+           int cdir = open( ".", O_RDONLY );
+           
+           if( cdir >= 0 ) {
+               chdir( path_copy );
+               new_path = getcwd( NULL, PATH_MAX );
+               fchdir( cdir );
+               close( cdir );
+               if( new_path ) {
+                   free( path_copy );
+                   path_copy = new_path;
+               }
+           }
+       }
+
+       /**
+        * If we're being asked to open a directory, check if that directory
+        * is the mountpoint for a DVD-ROM which we can use instead.
+        */
+
+       if( strlen( path_copy ) > 1 ) {
+           if( path[ strlen( path_copy ) - 1 ] == '/' ) 
+               path_copy[ strlen( path_copy ) - 1 ] = '\0';
+       }
+
+       if( strlen( path_copy ) > 9 ) {
+           if( !strcasecmp( &(path_copy[ strlen( path_copy ) - 9 ]), 
+                            "/video_ts" ) ) {
+             path_copy[ strlen( path_copy ) - 9 ] = '\0';
+           }
+       }
+
+#if defined(SYS_BSD)
+       if( ( fe = getfsfile( path_copy ) ) ) {
+           char *dev_name = bsd_block2char( fe->fs_spec );
+           fprintf( stderr,
+                    "libdvdread: Attempting to use device %s"
+                    " mounted on %s for CSS authentication\n",
+                    dev_name,
+                    fe->fs_file );
+           auth_drive = DVDOpenImageFile( dev_name );
+           free( dev_name );
+       }
+#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 ) ) {
+                   char *dev_name = sun_block2char( mp.mnt_special );
+                   fprintf( stderr, 
+                            "libdvdread: Attempting to use device %s"
+                            " mounted on %s for CSS authentication\n",
+                            dev_name,
+                            mp.mnt_mountp );
+                   auth_drive = DVDOpenImageFile( dev_name );
+                   free( dev_name );
+                   break;
+               }
+           }
+           fclose( mntfile );
+       }
+#elif defined(__linux__)
+        mntfile = fopen( MOUNTED, "r" );
+        if( mntfile ) {
+            struct mntent *me;
+            while( ( me = getmntent( mntfile ) ) ) {
+                if( !strcmp( me->mnt_dir, path_copy ) ) {
+                   fprintf( stderr, 
+                            "libdvdread: Attempting to use device %s"
+                             " mounted on %s for CSS authentication\n",
+                             me->mnt_fsname,
+                            me->mnt_dir );
+                    auth_drive = DVDOpenImageFile( me->mnt_fsname );
+                    break;
+                }
+            }
+            fclose( mntfile );
+       }
+#endif
+       if( !auth_drive ) {
+           fprintf( stderr, "libdvdread: Device inaccessible, "
+                    "CSS authentication not available.\n" );
+       }
+
+       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.
+         */
+       fprintf( stderr, "libdvdread: Using normal filesystem access.\n" );
+        return DVDOpenPath( path );
+    }
+
+    /* If it's none of the above, screw it. */
+    fprintf( stderr, "libdvdread: Could not open %s\n", path );
+    return 0;
+}
+
+void DVDClose( dvd_reader_t *dvd )
+{
+    if( dvd ) {
+        if( dvd->dev ) dvdcss_close( dvd->dev );
+        if( dvd->fd >= 0 ) close( dvd->fd );
+        if( dvd->path_root ) free( dvd->path_root );
+        free( dvd );
+        dvd = 0;
+    }
+}
+
+/**
+ * Open an unencrypted file on a DVD image file.
+ */
+static dvd_file_t *DVDOpenFileUDF( dvd_reader_t *dvd, char *filename )
+{
+    uint32_t start, len;
+    dvd_file_t *dvd_file;
+
+    start = UDFFindFile( dvd, filename, &len );
+    if( !start ) return 0;
+
+    dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
+    if( !dvd_file ) return 0;
+    dvd_file->dvd = dvd;
+    dvd_file->lb_start = start;
+    dvd_file->seek_pos = 0;
+    memset( dvd_file->title_fds, -1, sizeof( dvd_file->title_fds ) );
+    dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
+
+    return dvd_file;
+}
+
+/**
+ * Searches for <file> in directory <path>, ignoring case.
+ * Returns 0 and full filename in <filename>.
+ *     or -1 on file not found.
+ *     or -2 on path not found.
+ */
+static int findDirFile( const char *path, const char *file, char *filename ) 
+{
+    DIR *dir;
+    struct dirent *ent;
+
+    dir = opendir( path );
+    if( !dir ) return -2;
+
+    while( ( ent = readdir( dir ) ) != NULL ) {
+        if( !strcasecmp( ent->d_name, file ) ) {
+            sprintf( filename, "%s%s%s", path,
+                     ( ( path[ strlen( path ) - 1 ] == '/' ) ? "" : "/" ),
+                     ent->d_name );
+            return 0;
+        }
+    }
+
+    return -1;
+}
+
+static int findDVDFile( dvd_reader_t *dvd, const char *file, char *filename )
+{
+    char video_path[ PATH_MAX + 1 ];
+    const char *nodirfile;
+    int ret;
+
+    /* Strip off the directory for our search */
+    if( !strncasecmp( "/VIDEO_TS/", file, 10 ) ) {
+        nodirfile = &(file[ 10 ]);
+    } else {
+        nodirfile = file;
+    }
+
+    ret = findDirFile( dvd->path_root, nodirfile, filename );
+    if( ret < 0 ) {
+        /* Try also with adding the path, just in case. */
+        sprintf( video_path, "%s/VIDEO_TS/", dvd->path_root );
+        ret = findDirFile( video_path, nodirfile, filename );
+        if( ret < 0 ) {
+            /* Try with the path, but in lower case. */
+            sprintf( video_path, "%s/video_ts/", dvd->path_root );
+            ret = findDirFile( video_path, nodirfile, filename );
+            if( ret < 0 ) {
+                return 0;
+            }
+        }
+    }
+
+    return 1;
+}
+
+/**
+ * Open an unencrypted file from a DVD directory tree.
+ */
+static dvd_file_t *DVDOpenFilePath( dvd_reader_t *dvd, char *filename )
+{
+    char full_path[ PATH_MAX + 1 ];
+    dvd_file_t *dvd_file;
+    struct stat fileinfo;
+    int fd;
+
+    /* Get the full path of the file. */
+    if( !findDVDFile( dvd, filename, full_path ) ) return 0;
+
+    fd = open( full_path, O_RDONLY );
+    if( fd < 0 ) 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_fds, -1, sizeof( dvd_file->title_fds ) );
+    dvd_file->filesize = 0;
+
+    if( stat( full_path, &fileinfo ) < 0 ) {
+        fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
+        free( dvd_file );
+        return 0;
+    }
+    dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+    dvd_file->title_fds[ 0 ] = fd;
+    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;
+    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_fds, -1, sizeof( dvd_file->title_fds ) );
+    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;
+        }
+    }
+    
+    /* Hack to crack all the keys on the first open. */
+    if( dvdcss_library ) {
+        if( !dvd_file->dvd->init_keys ) {
+           initAllCSSKeys( dvd_file->dvd );
+           dvd_file->dvd->init_keys = 1;
+       }
+    }
+    
+    /* Perform CSS key cracking for this title. */
+    if( dvdcss_library ) {
+       if( dvdcss_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;
+    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_fds, -1, sizeof( dvd_file->title_fds ) );
+    dvd_file->filesize = 0;
+
+    if( menu ) {
+        int fd;
+
+        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;
+        }
+
+        fd = open( full_path, O_RDONLY );
+        if( fd < 0 ) {
+            free( dvd_file );
+            return 0;
+        }
+
+        if( stat( full_path, &fileinfo ) < 0 ) {
+            fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
+            free( dvd_file );
+            return 0;
+        }
+        dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+        dvd_file->title_fds[ 0 ] = fd;
+        dvd_file->filesize = dvd_file->title_sizes[ 0 ];
+
+    } else {
+        for( i = 0; i < 9; ++i ) {
+
+            sprintf( filename, "VTS_%02i_%i.VOB", title, i + 1 );
+            if( !findDVDFile( dvd, filename, full_path ) ) {
+                break;
+            }
+
+            if( stat( full_path, &fileinfo ) < 0 ) {
+                fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
+                break;
+            }
+
+            dvd_file->title_sizes[ i ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+            dvd_file->title_fds[ i ] = open( full_path, O_RDONLY );
+            dvd_file->filesize += dvd_file->title_sizes[ i ];
+        }
+        if( !(dvd_file->title_sizes[ 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 ];
+
+    switch( domain ) {
+    case DVD_READ_INFO_FILE:
+        if( titlenum == 0 ) {
+            sprintf( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
+        } else {
+            sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );
+        }
+        break;
+    case DVD_READ_INFO_BACKUP_FILE:
+        if( titlenum == 0 ) {
+            sprintf( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
+        } else {
+            sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );
+        }
+        break;
+    case DVD_READ_MENU_VOBS:
+        if( dvd->isImageFile ) {
+            return DVDOpenVOBUDF( dvd, titlenum, 1 );
+        } else {
+            return DVDOpenVOBPath( dvd, titlenum, 1 );
+        }
+        break;
+    case DVD_READ_TITLE_VOBS:
+        if( titlenum == 0 ) return 0;
+        if( dvd->isImageFile ) {
+            return DVDOpenVOBUDF( dvd, titlenum, 0 );
+        } else {
+            return DVDOpenVOBPath( dvd, titlenum, 0 );
+        }
+        break;
+    default:
+        fprintf( stderr, "libdvdread: Invalid domain for file open.\n" );
+        return 0;
+    }
+    
+    if( dvd->isImageFile ) {
+        return DVDOpenFileUDF( dvd, filename );
+    } else {
+        return DVDOpenFilePath( dvd, filename );
+    }
+}
+
+void DVDCloseFile( dvd_file_t *dvd_file )
+{
+    int i;
+
+    if( dvd_file ) {
+        if( !dvd_file->dvd->isImageFile ) {
+            for( i = 0; i < 9; ++i ) {
+                if( dvd_file->title_fds[ i ] >= 0 )
+                   close( dvd_file->title_fds[ i ] );
+            }
+        }
+
+        free( dvd_file );
+        dvd_file = 0;
+    }
+}
+
+int64_t DVDReadLBUDF( dvd_reader_t *device, uint32_t lb_number,
+                     size_t block_count, unsigned char *data, 
+                     int encrypted )
+{
+    if( dvdcss_library ) {
+        int ret;
+
+        if( !device->dev ) {
+            fprintf( stderr, "libdvdread: Fatal error in block read.\n" );
+            return 0;
+        }
+
+        ret = dvdcss_seek( device->dev, (int) lb_number );
+        if( ret != (int) lb_number ) {
+           fprintf( stderr, "libdvdread: Can't seek to block %u\n", 
+                    lb_number );
+            return 0;
+        }
+
+        return (int64_t) ( dvdcss_read( device->dev, (char *) data, 
+                                       (int) block_count, encrypted ) 
+                          * (uint64_t) DVD_VIDEO_LB_LEN );
+    } else {
+        off64_t off;
+
+        if( device->fd < 0) {
+            fprintf( stderr, "libdvdread: Fatal error in block read.\n" );
+            return 0;
+        }
+
+        off = lseek64( device->fd, lb_number * (int64_t) DVD_VIDEO_LB_LEN, 
+                      SEEK_SET );
+        if( off != ( lb_number * (int64_t) DVD_VIDEO_LB_LEN ) ) {
+           fprintf( stderr, "libdvdread: Can't seek to block %u\n", 
+                    lb_number );
+            return 0;
+        }
+        return (int64_t) ( read( device->fd, data, 
+                                block_count * DVD_VIDEO_LB_LEN ) );
+    }
+}
+
+static int64_t DVDReadBlocksUDF( dvd_file_t *dvd_file, uint32_t offset,
+                                size_t block_count, unsigned char *data )
+{
+    return DVDReadLBUDF( dvd_file->dvd, dvd_file->lb_start + offset,
+                         block_count, data, DVDCSS_READ_DECRYPT );
+}
+
+static int64_t DVDReadBlocksPath( dvd_file_t *dvd_file, size_t offset,
+                                 size_t block_count, unsigned char *data )
+{
+    int i;
+    ssize_t ret, ret2;
+    off64_t off;
+
+    ret = 0;
+    ret2 = 0;
+    for( i = 0; i < 9; ++i ) {
+        if( !dvd_file->title_sizes[ i ] ) return 0;
+
+        if( offset < dvd_file->title_sizes[ i ] ) {
+            if( ( offset + block_count ) <= dvd_file->title_sizes[ i ] ) {
+               off = lseek64( dvd_file->title_fds[ i ], 
+                              offset * (int64_t) DVD_VIDEO_LB_LEN, SEEK_SET );
+               if( off != ( offset * (int64_t) DVD_VIDEO_LB_LEN ) ) {
+                   fprintf( stderr, "libdvdread: Can't seek to block %d\n", 
+                            offset );
+                   return 0;
+               }
+                ret = read( dvd_file->title_fds[ i ], data,
+                            block_count * DVD_VIDEO_LB_LEN );
+                break;
+            } else {
+               size_t part1_size 
+                 = ( dvd_file->title_sizes[ i ] - offset ) * DVD_VIDEO_LB_LEN;
+               /* 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 = lseek64( dvd_file->title_fds[ i ], 
+                              offset * (int64_t) DVD_VIDEO_LB_LEN, SEEK_SET );
+               if( off != ( offset * (int64_t) DVD_VIDEO_LB_LEN ) ) {
+                   fprintf( stderr, "libdvdread: Can't seek to block %d\n", 
+                            offset );
+                   return 0;
+               }
+                ret = read( dvd_file->title_fds[ i ], data, part1_size );
+               if( ret < 0 ) return ret;
+               /* FIXME: This is wrong if i is the last file in the set. 
+                         also error from this read will not show in ret. */
+               
+                /* Read part 2 */
+                lseek64( dvd_file->title_fds[ i + 1 ], (off64_t)0, SEEK_SET );
+                ret2 = read( dvd_file->title_fds[ i + 1 ], data + part1_size,
+                             block_count * DVD_VIDEO_LB_LEN - part1_size );
+                if( ret2 < 0 ) return ret2;
+               break;
+            }
+        } else {
+            offset -= dvd_file->title_sizes[ i ];
+        }
+    }
+
+    return ( (int64_t) ret + (int64_t) ret2 );
+}
+
+/* These are broken for some cases reading more than 2Gb at a time. */
+ssize_t DVDReadBlocks( dvd_file_t *dvd_file, int offset, 
+                      size_t block_count, unsigned char *data )
+{
+    int64_t ret;
+  
+    if( dvd_file->dvd->isImageFile ) {
+       ret = DVDReadBlocksUDF( dvd_file, (uint32_t)offset, 
+                               block_count, data );
+    } else {
+       ret = DVDReadBlocksPath( dvd_file, (size_t) offset, 
+                                block_count, data );
+    }
+    if( ret <= 0 ) {
+        return (ssize_t) ret;
+    }
+    {
+      ssize_t sret = (ssize_t) (ret / (int64_t)DVD_VIDEO_LB_LEN );
+      if( sret == 0 ) {
+       fprintf(stderr, "libdvdread: DVDReadBlocks got %d bytes\n", (int)ret );
+      }
+      return sret;
+    }
+}
+
+int32_t DVDFileSeek( dvd_file_t *dvd_file, int32_t offset )
+{
+    if( dvd_file->dvd->isImageFile ) {
+        dvd_file->seek_pos = (uint32_t) offset;
+        return offset;
+    } else {
+        return (int32_t) ( lseek( dvd_file->title_fds[ 0 ], 
+                                 (off_t) offset, SEEK_SET ) );
+    }
+}
+
+static ssize_t DVDReadBytesUDF( dvd_file_t *dvd_file, void *data, 
+                               size_t byte_size )
+{
+    unsigned char *secbuf;
+    unsigned int numsec, seek_sector, seek_byte;
+    int64_t len;
+    
+    seek_sector = dvd_file->seek_pos / DVD_VIDEO_LB_LEN;
+    seek_byte   = dvd_file->seek_pos % DVD_VIDEO_LB_LEN;
+
+    numsec = ( ( seek_byte + byte_size ) / DVD_VIDEO_LB_LEN ) + 1;
+    secbuf = (unsigned char *) malloc( numsec * DVD_VIDEO_LB_LEN );
+    if( !secbuf ) {
+       fprintf( stderr, "libdvdread: Can't allocate memory " 
+                "for file read!\n" );
+        return 0;
+    }
+
+    len = DVDReadLBUDF( dvd_file->dvd, dvd_file->lb_start + seek_sector,
+                        numsec, secbuf, DVDCSS_NOFLAGS );
+    if( len != numsec * (int64_t) DVD_VIDEO_LB_LEN ) {
+        free( secbuf );
+        return 0;
+    }
+
+    dvd_file->seek_pos += byte_size;
+
+    memcpy( data, &(secbuf[ seek_byte ]), byte_size );
+    free( secbuf );
+
+    return byte_size;
+}
+
+static ssize_t DVDReadBytesPath( dvd_file_t *dvd_file, void *data, 
+                                size_t byte_size )
+{
+    return read( dvd_file->title_fds[ 0 ], data, byte_size );
+}
+
+ssize_t DVDReadBytes( dvd_file_t *dvd_file, void *data, size_t byte_size )
+{
+    if( dvd_file->dvd->isImageFile ) {
+        return DVDReadBytesUDF( dvd_file, data, byte_size );
+    } else {
+        return DVDReadBytesPath( dvd_file, data, byte_size );
+    }
+}
+
+ssize_t DVDFileSize( dvd_file_t *dvd_file )
+{
+    return dvd_file->filesize;
+}
+
diff --git a/extras/libdvdread/dvd_reader.h b/extras/libdvdread/dvd_reader.h
new file mode 100644 (file)
index 0000000..3cb4f47
--- /dev/null
@@ -0,0 +1,137 @@
+/**
+ * Copyright (C) 2001 Billy Biggs <vektor@dumbterm.net>,
+ *                    Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef DVD_READER_H_INCLUDED
+#define DVD_READER_H_INCLUDED
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+/**
+ * The length of one Logical Block of a DVD Video.
+ */
+#define DVD_VIDEO_LB_LEN 2048
+
+/**
+ * Maximum length of filenames for UDF.
+ */
+#define MAX_UDF_FILE_NAME_LEN 2048
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct dvd_reader_s dvd_reader_t;
+typedef struct dvd_file_s dvd_file_t;
+
+/**
+ * Opens a block device of a DVD-ROM file, or an image file, or a directory
+ * name for a mounted DVD or HD copy of a DVD.  Returns 0 if we can't get any
+ * of those methods to work.
+ *
+ * If the given file is a block device, or is the mountpoint for a block
+ * device, then that device is used for CSS authentication using libdvdcss.
+ * If no device is available, then no CSS authentication is performed, 
+ * and we hope that the image is decrypted.
+ *
+ * If the path given is a directory, then the files in that directory may be in
+ * any one of these formats:
+ *
+ *   path/VIDEO_TS/VTS_01_1.VOB
+ *   path/video_ts/vts_01_1.vob
+ *   path/VTS_01_1.VOB
+ *   path/vts_01_1.vob
+ */
+dvd_reader_t *DVDOpen( const char *path );
+
+/**
+ * Closes and cleans up the DVD reader object.  You must close all open files
+ * before calling this function.
+ */
+void DVDClose( dvd_reader_t *dvd );
+
+/**
+ * INFO_FILE       : VIDEO_TS.IFO     (manager)
+ *                   VTS_XX_0.IFO     (title)
+ *
+ * INFO_BACKUP_FILE: VIDEO_TS.BUP     (manager)
+ *                   VTS_XX_0.BUP     (title)
+ *
+ * MENU_VOBS       : VIDEO_TS.VOB     (manager)
+ *                   VTS_XX_0.VOB     (title)
+ *
+ * TITLE_VOBS      : VTS_XX_[1-9].VOB (title)
+ *                   All files in the title set are opened and read as a single
+ *                   file.
+ */
+typedef enum {
+    DVD_READ_INFO_FILE,
+    DVD_READ_INFO_BACKUP_FILE,
+    DVD_READ_MENU_VOBS,
+    DVD_READ_TITLE_VOBS
+} dvd_read_domain_t;
+
+/**
+ * Opens a file on the DVD given the title number and domain.  If the title
+ * number is 0, the video manager information is opened
+ * (VIDEO_TS.[IFO,BUP,VOB]).  Returns a file structure which may be used for
+ * reads, or 0 if the file was not found.
+ */
+dvd_file_t *DVDOpenFile( dvd_reader_t *dvd, int titlenum, 
+                        dvd_read_domain_t domain );
+
+/**
+ * Closes a file and frees the associated structure.
+ */
+void DVDCloseFile( dvd_file_t *dvd_file );
+
+/**
+ * Reads block_count number of blocks from the file at the given block offset.
+ * Returns number of blocks read on success, -1 on error.  This call is only
+ * for reading VOB data, and should not be used when reading the IFO files.  
+ * When reading from an encrypted drive, blocks are decrypted using libdvdcss 
+ * where required.
+ */
+ssize_t DVDReadBlocks( dvd_file_t *dvd_file, int offset,
+                      size_t block_count, unsigned char *data );
+
+/**
+ * Seek to the given position in the file.  Returns the resulting position in
+ * bytes from the beginning of the file.  The seek position is only used for
+ * byte reads from the file, the block read call always reads from the given
+ * offset.
+ */
+int DVDFileSeek( dvd_file_t *dvd_file, int offset );
+
+/**
+ * Reads the given number of bytes from the file.  This call can only be used
+ * on the information files, and may not be used for reading from a VOB.  This
+ * reads from and increments the currrent seek position for the file.
+ */
+ssize_t DVDReadBytes( dvd_file_t *dvd_file, void *data, size_t byte_size );
+
+/**
+ * Returns the file size in blocks.
+ */
+ssize_t DVDFileSize( dvd_file_t *dvd_file );
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* DVD_READER_H_INCLUDED */
diff --git a/extras/libdvdread/dvd_udf.c b/extras/libdvdread/dvd_udf.c
new file mode 100644 (file)
index 0000000..e3ec623
--- /dev/null
@@ -0,0 +1,466 @@
+/**
+ * This code is based on dvdudf by:
+ *   Christian Wolff <scarabaeus@convergence.de>.
+ *
+ * Modifications by:
+ *   Billy Biggs <vektor@dumbterm.net>.
+ *
+ * dvdudf: parse and read the UDF volume information of a DVD Video
+ * Copyright (C) 1999 Christian Wolff for convergence integrated media
+ * GmbH The author can be reached at scarabaeus@convergence.de, the
+ * project's page is at http://linuxtv.org/dvd/
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  Or, point your browser to
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "dvd_udf.h"
+
+extern int64_t DVDReadLBUDF( dvd_reader_t *device, uint32_t lb_number,
+                            size_t block_count, unsigned char *data, 
+                            int encrypted );
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+struct Partition {
+    int valid;
+    char VolumeDesc[128];
+    uint16_t Flags;
+    uint16_t Number;
+    char Contents[32];
+    uint32_t AccessType;
+    uint32_t Start;
+    uint32_t Length;
+};
+
+struct AD {
+    uint32_t Location;
+    uint32_t Length;
+    uint8_t  Flags;
+    uint16_t Partition;
+};
+
+/* For direct data access, LSB first */
+#define GETN1(p) ((uint8_t)data[p])
+#define GETN2(p) ((uint16_t)data[p] | ((uint16_t)data[(p) + 1] << 8))
+#define GETN3(p) ((uint32_t)data[p] | ((uint32_t)data[(p) + 1] << 8) \
+                 | ((uint32_t)data[(p) + 2] << 16))
+#define GETN4(p) ((uint32_t)data[p] \
+                 | ((uint32_t)data[(p) + 1] << 8) \
+                 | ((uint32_t)data[(p) + 2] << 16) \
+                 | ((uint32_t)data[(p) + 3] << 24))
+/* This is wrong with regard to endianess */
+#define GETN(p, n, target) memcpy(target, &data[p], n)
+
+static int Unicodedecode( uint8_t *data, int len, char *target ) 
+{
+    int p = 1, i = 0;
+
+    if( ( data[ 0 ] == 8 ) || ( data[ 0 ] == 16 ) ) do {
+        if( data[ 0 ] == 16 ) p++;  /* Ignore MSB of unicode16 */
+        if( p < len ) {
+            target[ i++ ] = data[ p++ ];
+        }
+    } while( p < len );
+
+    target[ i ] = '\0';
+    return 0;
+}
+
+static int UDFDescriptor( uint8_t *data, uint16_t *TagID ) 
+{
+    *TagID = GETN2(0);
+    // TODO: check CRC 'n stuff
+    return 0;
+}
+
+static int UDFExtentAD( uint8_t *data, uint32_t *Length, uint32_t *Location ) 
+{
+    *Length   = GETN4(0);
+    *Location = GETN4(4);
+    return 0;
+}
+
+static int UDFShortAD( uint8_t *data, struct AD *ad, 
+                      struct Partition *partition ) 
+{
+    ad->Length = GETN4(0);
+    ad->Flags = ad->Length >> 30;
+    ad->Length &= 0x3FFFFFFF;
+    ad->Location = GETN4(4);
+    ad->Partition = partition->Number; // use number of current partition
+    return 0;
+}
+
+static int UDFLongAD( uint8_t *data, struct AD *ad )
+{
+    ad->Length = GETN4(0);
+    ad->Flags = ad->Length >> 30;
+    ad->Length &= 0x3FFFFFFF;
+    ad->Location = GETN4(4);
+    ad->Partition = GETN2(8);
+    //GETN(10, 6, Use);
+    return 0;
+}
+
+static int UDFExtAD( uint8_t *data, struct AD *ad )
+{
+    ad->Length = GETN4(0);
+    ad->Flags = ad->Length >> 30;
+    ad->Length &= 0x3FFFFFFF;
+    ad->Location = GETN4(12);
+    ad->Partition = GETN2(16);
+    //GETN(10, 6, Use);
+    return 0;
+}
+
+static int UDFICB( uint8_t *data, uint8_t *FileType, uint16_t *Flags )
+{
+    *FileType = GETN1(11);
+    *Flags = GETN2(18);
+    return 0;
+}
+
+
+static int UDFPartition( uint8_t *data, uint16_t *Flags, uint16_t *Number,
+                        char *Contents, uint32_t *Start, uint32_t *Length )
+{
+    *Flags = GETN2(20);
+    *Number = GETN2(22);
+    GETN(24, 32, Contents);
+    *Start = GETN4(188);
+    *Length = GETN4(192);
+    return 0;
+}
+
+/**
+ * Reads the volume descriptor and checks the parameters.  Returns 0 on OK, 1
+ * on error.
+ */
+static int UDFLogVolume( uint8_t *data, char *VolumeDescriptor )
+{
+    uint32_t lbsize, MT_L, N_PM;
+    Unicodedecode(&data[84], 128, VolumeDescriptor);
+    lbsize = GETN4(212);  // should be 2048
+    MT_L = GETN4(264);    // should be 6
+    N_PM = GETN4(268);    // should be 1
+    if (lbsize != DVD_VIDEO_LB_LEN) return 1;
+    return 0;
+}
+
+static int UDFFileEntry( uint8_t *data, uint8_t *FileType, 
+                        struct Partition *partition, struct AD *ad )
+{
+    uint16_t flags;
+    uint32_t L_EA, L_AD;
+    unsigned int p;
+
+    UDFICB( &data[ 16 ], FileType, &flags );
+   
+    /* Init ad for an empty file (i.e. there isn't a AD, L_AD == 0 ) */
+    ad->Length = GETN4( 60 ); // Really 8 bytes a 56
+    ad->Flags = 0;
+    ad->Location = 0; // what should we put here? 
+    ad->Partition = partition->Number; // use number of current partition
+
+    L_EA = GETN4( 168 );
+    L_AD = GETN4( 172 );
+    p = 176 + L_EA;
+    while( p < 176 + L_EA + L_AD ) {
+        switch( flags & 0x0007 ) {
+            case 0: UDFShortAD( &data[ p ], ad, partition ); p += 8;  break;
+            case 1: UDFLongAD( &data[ p ], ad );  p += 16; break;
+            case 2: UDFExtAD( &data[ p ], ad );   p += 20; break;
+            case 3:
+                switch( L_AD ) {
+                    case 8:  UDFShortAD( &data[ p ], ad, partition ); break;
+                    case 16: UDFLongAD( &data[ p ], ad );  break;
+                    case 20: UDFExtAD( &data[ p ], ad );   break;
+                }
+                p += L_AD;
+                break;
+            default:
+                p += L_AD; break;
+        }
+    }
+    return 0;
+}
+
+static int UDFFileIdentifier( uint8_t *data, uint8_t *FileCharacteristics,
+                             char *FileName, struct AD *FileICB )
+{
+    uint8_t L_FI;
+    uint16_t L_IU;
+
+    *FileCharacteristics = GETN1(18);
+    L_FI = GETN1(19);
+    UDFLongAD(&data[20], FileICB);
+    L_IU = GETN2(36);
+    if (L_FI) Unicodedecode(&data[38 + L_IU], L_FI, FileName);
+    else FileName[0] = '\0';
+    return 4 * ((38 + L_FI + L_IU + 3) / 4);
+}
+
+/**
+ * Maps ICB to FileAD
+ * ICB: Location of ICB of directory to scan
+ * FileType: Type of the file
+ * File: Location of file the ICB is pointing to
+ * return 1 on success, 0 on error;
+ */
+static int UDFMapICB( dvd_reader_t *device, struct AD ICB, uint8_t *FileType,
+                     struct Partition *partition, struct AD *File ) 
+{
+    uint8_t LogBlock[DVD_VIDEO_LB_LEN];
+    uint32_t lbnum;
+    uint16_t TagID;
+
+    lbnum = partition->Start + ICB.Location;
+    do {
+        if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) {
+            TagID = 0;
+        } else {
+            UDFDescriptor( LogBlock, &TagID );
+        }
+
+        if( TagID == 261 ) {
+            UDFFileEntry( LogBlock, FileType, partition, File );
+            return 1;
+        };
+    } while( ( lbnum <= partition->Start + ICB.Location + ( ICB.Length - 1 )
+             / DVD_VIDEO_LB_LEN ) && ( TagID != 261 ) );
+
+    return 0;
+}
+
+/**
+ * Dir: Location of directory to scan
+ * FileName: Name of file to look for
+ * FileICB: Location of ICB of the found file
+ * return 1 on success, 0 on error;
+ */
+static int UDFScanDir( dvd_reader_t *device, struct AD Dir, char *FileName,
+                       struct Partition *partition, struct AD *FileICB ) 
+{
+    char filename[ MAX_UDF_FILE_NAME_LEN ];
+    uint8_t directory[ 2 * DVD_VIDEO_LB_LEN ];
+    uint32_t lbnum;
+    uint16_t TagID;
+    uint8_t filechar;
+    unsigned int p;
+
+    /* Scan dir for ICB of file */
+    lbnum = partition->Start + Dir.Location;
+
+    if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) {
+        return 0;
+    }
+
+    p = 0;
+    while( p < Dir.Length ) {
+        if( p > DVD_VIDEO_LB_LEN ) {
+            ++lbnum;
+            p -= DVD_VIDEO_LB_LEN;
+            Dir.Length -= DVD_VIDEO_LB_LEN;
+            if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) {
+                return 0;
+            }
+        }
+        UDFDescriptor( &directory[ p ], &TagID );
+        if( TagID == 257 ) {
+            p += UDFFileIdentifier( &directory[ p ], &filechar,
+                                    filename, FileICB );
+            if( !strcasecmp( FileName, filename ) ) {
+                return 1;
+            }
+        } else {
+            return 0;
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * Looks for partition on the disc.  Returns 1 if partition found, 0 on error.
+ *   partnum: Number of the partition, starting at 0.
+ *   part: structure to fill with the partition information
+ */
+static int UDFFindPartition( dvd_reader_t *device, int partnum,
+                            struct Partition *part ) 
+{
+    uint8_t LogBlock[ DVD_VIDEO_LB_LEN ], Anchor[ DVD_VIDEO_LB_LEN ];
+    uint32_t lbnum, MVDS_location, MVDS_length;
+    uint16_t TagID;
+    uint32_t lastsector;
+    int i, terminate, volvalid;
+
+    /* Find Anchor */
+    lastsector = 0;
+    lbnum = 256;   /* Try #1, prime anchor */
+    terminate = 0;
+
+    for(;;) {
+        if( DVDReadLBUDF( device, lbnum, 1, Anchor, 0 ) > 0 ) {
+            UDFDescriptor( Anchor, &TagID );
+        } else {
+            TagID = 0;
+        }
+        if (TagID != 2) {
+            /* Not an anchor */
+            if( terminate ) return 0; /* Final try failed */
+
+            if( lastsector ) {
+
+                /* We already found the last sector.  Try #3, alternative
+                 * backup anchor.  If that fails, don't try again.
+                 */
+                lbnum = lastsector;
+                terminate = 1;
+            } else {
+                /* TODO: Find last sector of the disc (this is optional). */
+                if( lastsector ) {
+                    /* Try #2, backup anchor */
+                    lbnum = lastsector - 256;
+                } else {
+                    /* Unable to find last sector */
+                    return 0;
+                }
+            }
+        } else {
+            /* It's an anchor! We can leave */
+            break;
+        }
+    }
+    /* Main volume descriptor */
+    UDFExtentAD( &Anchor[ 16 ], &MVDS_length, &MVDS_location );
+       
+    part->valid = 0;
+    volvalid = 0;
+    part->VolumeDesc[ 0 ] = '\0';
+    i = 1;
+    do {
+        /* Find Volume Descriptor */
+        lbnum = MVDS_location;
+        do {
+
+            if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) {
+                TagID = 0;
+            } else {
+                UDFDescriptor( LogBlock, &TagID );
+            }
+
+            if( ( TagID == 5 ) && ( !part->valid ) ) {
+                /* Partition Descriptor */
+                UDFPartition( LogBlock, &part->Flags, &part->Number,
+                              part->Contents, &part->Start, &part->Length );
+                part->valid = ( partnum == part->Number );
+            } else if( ( TagID == 6 ) && ( !volvalid ) ) {
+                /* Logical Volume Descriptor */
+                if( UDFLogVolume( LogBlock, part->VolumeDesc ) ) {  
+                    /* TODO: sector size wrong! */
+                } else {
+                    volvalid = 1;
+                }
+            }
+
+        } while( ( lbnum <= MVDS_location + ( MVDS_length - 1 )
+                 / DVD_VIDEO_LB_LEN ) && ( TagID != 8 )
+                 && ( ( !part->valid ) || ( !volvalid ) ) );
+
+        if( ( !part->valid) || ( !volvalid ) ) {
+            /* Backup volume descriptor */
+            UDFExtentAD( &Anchor[ 24 ], &MVDS_length, &MVDS_location );
+        }
+    } while( i-- && ( ( !part->valid ) || ( !volvalid ) ) );
+
+    /* We only care for the partition, not the volume */
+    return part->valid;
+}
+
+uint32_t UDFFindFile( dvd_reader_t *device, char *filename,
+                     uint32_t *filesize )
+{
+    uint8_t LogBlock[ DVD_VIDEO_LB_LEN ];
+    uint32_t lbnum;
+    uint16_t TagID;
+    struct Partition partition;
+    struct AD RootICB, File, ICB;
+    char tokenline[ MAX_UDF_FILE_NAME_LEN ];
+    char *token;
+    uint8_t filetype;
+       
+    *filesize = 0;
+    tokenline[0] = '\0';
+    strcat( tokenline, filename );
+
+    /* Find partition, 0 is the standard location for DVD Video.*/
+    if( !UDFFindPartition( device, 0, &partition ) ) return 0;
+
+    /* Find root dir ICB */
+    lbnum = partition.Start;
+    do {
+        if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) {
+            TagID = 0;
+        } else {
+            UDFDescriptor( LogBlock, &TagID );
+        }
+
+        /* File Set Descriptor */
+        if( TagID == 256 ) {  // File Set Descriptor
+            UDFLongAD( &LogBlock[ 400 ], &RootICB );
+        }
+    } while( ( lbnum < partition.Start + partition.Length )
+             && ( TagID != 8 ) && ( TagID != 256 ) );
+
+    /* Sanity checks. */
+    if( TagID != 256 ) return 0;
+    if( RootICB.Partition != 0 ) return 0;
+       
+    /* Find root dir */
+    if( !UDFMapICB( device, RootICB, &filetype, &partition, &File ) ) return 0;
+    if( filetype != 4 ) return 0;  /* Root dir should be dir */
+
+    /* Tokenize filepath */
+    token = strtok(tokenline, "/");
+    while( token != NULL ) {
+        if( !UDFScanDir( device, File, token, &partition, &ICB ) ) return 0;
+        if( !UDFMapICB( device, ICB, &filetype, &partition, &File ) ) return 0;
+        token = strtok( NULL, "/" );
+    }
+    
+    /* Sanity check. */
+    if( File.Partition != 0 ) return 0;
+   
+    *filesize = File.Length;
+    /* Hack to not return partition.Start for empty files. */
+    if( !File.Location )
+      return 0;
+    else
+      return partition.Start + File.Location;
+}
diff --git a/extras/libdvdread/dvd_udf.h b/extras/libdvdread/dvd_udf.h
new file mode 100644 (file)
index 0000000..c45bc38
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * This code is based on dvdudf by:
+ *   Christian Wolff <scarabaeus@convergence.de>.
+ *
+ * Modifications by:
+ *   Billy Biggs <vektor@dumbterm.net>.
+ *
+ * dvdudf: parse and read the UDF volume information of a DVD Video
+ * Copyright (C) 1999 Christian Wolff for convergence integrated media
+ * GmbH The author can be reached at scarabaeus@convergence.de, the
+ * project's page is at http://linuxtv.org/dvd/
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.  Or, point your browser to
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef DVD_UDF_H_INCLUDED
+#define DVD_UDF_H_INCLUDED
+
+#include "dvd_reader.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Looks for a file on the UDF disc/imagefile and returns the block number
+ * where it begins, or 0 if it is not found.  The filename should be an
+ * absolute pathname on the UDF filesystem, starting with '/'.  For example,
+ * '/VIDEO_TS/VTS_01_1.IFO'.  On success, filesize will be set to the size of
+ * the file in bytes.
+ */
+uint32_t UDFFindFile( dvd_reader_t *device, char *filename, uint32_t *size );
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* DVD_UDF_H_INCLUDED */
diff --git a/extras/libdvdread/dvdcss.h b/extras/libdvdread/dvdcss.h
new file mode 100644 (file)
index 0000000..a361a26
--- /dev/null
@@ -0,0 +1,41 @@
+/*****************************************************************************
+ * libdvdcss.h: DVD reading library, exported functions.
+ *****************************************************************************
+ * Copyright (C) 1998-2001 VideoLAN
+ * $Id: dvdcss.h,v 1.1 2001/11/25 05:04:38 stef Exp $
+ *
+ * Authors: Stéphane Borel <stef@via.ecp.fr>
+ *          Samuel Hocevar <sam@zoy.org>
+ *
+ * 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.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * The libdvdcss structure
+ *****************************************************************************/
+typedef struct dvdcss_s* dvdcss_handle;
+
+/*****************************************************************************
+ * Flags
+ *****************************************************************************/
+#define DVDCSS_NOFLAGS         0
+
+#define DVDCSS_INIT_QUIET      (1 << 0)
+#define DVDCSS_INIT_DEBUG      (1 << 1)
+
+#define DVDCSS_READ_DECRYPT    (1 << 0)
+
+#define DVDCSS_BLOCK_SIZE      2048
+
diff --git a/extras/libdvdread/dvdread.c b/extras/libdvdread/dvdread.c
new file mode 100644 (file)
index 0000000..8c7ccce
--- /dev/null
@@ -0,0 +1,867 @@
+/*****************************************************************************
+ * dvdread.c: replacement for dvd_reader.c that always takes dvdcss functions
+ * (hard-linked) and adds a readv call function to tha API.
+ *****************************************************************************
+ * Copyright (C) 2001 Billy Biggs <vektor@dumbterm.net>.
+ * $Id: dvdread.c,v 1.1 2001/11/25 05:04:38 stef Exp $
+ *
+ * Author: Billy Biggs <vektor@dumbterm.net>
+ *         Stéphane Borel <stef@via.ecp.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/uio.h> /* readv() */
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <dlfcn.h>
+#include <dirent.h>
+
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__)
+#define SYS_BSD 1
+#endif
+
+#if defined(__sun)
+#include <sys/mnttab.h>
+#elif defined(SYS_BSD)
+#include <fstab.h>
+#elif defined(__linux__)
+#include <mntent.h>
+#endif
+
+#if defined(SYS_BSD)
+typedef off_t off64_t;
+#define lseek64 lseek
+#define stat64 stat
+#endif
+
+#include "dvd_udf.h"
+#include "dvd_reader.h"
+
+#include "../libdvdcss/videolan/dvdcss.h"
+
+struct dvd_reader_s {
+    /* Basic information. */
+    int isImageFile;
+
+    /* Information required for an image file. */
+    dvdcss_handle dev;
+    int init_keys;
+    int fd;
+
+    /* Information required for a directory path drive. */
+    char *path_root;
+};
+
+struct dvd_file_s {
+    /* Basic information. */
+    dvd_reader_t *dvd;
+
+    /* 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 ];
+    int title_fds[ 9 ];
+
+    /* Calculated at open-time, size in blocks. */
+    ssize_t filesize;
+};
+
+/**
+ * Open a DVD image or block device file.
+ */
+static dvd_reader_t *DVDOpenImageFile( const char *location )
+{
+    dvd_reader_t *dvd;
+    dvdcss_handle dev = 0;
+    int fd = -1;
+
+    dev = dvdcss_open( (char *) location );
+    if( !dev ) {
+        fprintf( stderr, "libdvdread: Can't open %s for reading.\n",
+            location );
+        return 0;
+    }
+
+    dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
+    if( !dvd ) return 0;
+    dvd->isImageFile = 1;
+    dvd->dev = dev;
+    dvd->init_keys = 0;
+    dvd->fd = fd;
+    dvd->path_root = 0;
+    
+    return dvd;
+}
+
+static dvd_reader_t *DVDOpenPath( const char *path_root )
+{
+    dvd_reader_t *dvd;
+
+    dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
+    if( !dvd ) return 0;
+    dvd->isImageFile = 0;
+    dvd->dev = 0;
+    dvd->init_keys = 0;
+    dvd->fd = -1;
+    dvd->path_root = strdup( path_root );
+
+    return dvd;
+}
+
+#if defined(__sun)
+/* /dev/rdsk/c0t6d0s0 (link to /devices/...)
+   /vol/dev/rdsk/c0t6d0/??
+   /vol/rdsk/<name> */
+static char *sun_block2char( const char *path )
+{
+    char *new_path;
+
+    /* Must contain "/dsk/" */ 
+    if( !strstr( path, "/dsk/" ) ) return (char *) strdup( path );
+
+    /* Replace "/dsk/" with "/rdsk/" */
+    new_path = malloc( strlen(path) + 2 );
+    strcpy( new_path, path );
+    strcpy( strstr( new_path, "/dsk/" ), "" );
+    strcat( new_path, "/rdsk/" );
+    strcat( new_path, strstr( path, "/dsk/" ) + strlen( "/dsk/" ) );
+
+    return new_path;
+}
+#endif
+
+#if defined(SYS_BSD)
+/* FreeBSD /dev/(r)(a)cd0 (a is for atapi, should work without r)
+   OpenBSD /dev/rcd0c
+   NetBSD  /dev/rcd0d or /dev/rcd0c (for non x86)
+   BSD/OS  /dev/sr0 (if not mounted) or /dev/rsr0 */
+static char *bsd_block2char( const char *path )
+{
+    char *new_path;
+
+    /* If it doesn't start with "/dev/" or does start with "/dev/r" exit */ 
+    if( !strncmp( path, "/dev/",  5 ) || strncmp( path, "/dev/r", 6 ) ) 
+      return (char *) strdup( path );
+
+    /* Replace "/dev/" with "/dev/r" */
+    new_path = malloc( strlen(path) + 2 );
+    strcpy( new_path, "/dev/r" );
+    strcat( new_path, path + strlen( "/dev/" ) );
+
+    return new_path;
+}
+#endif
+
+dvd_reader_t *DVDOpen( const char *path )
+{
+    struct stat64 fileinfo;
+    int ret;
+
+    if( !path ) return 0;
+
+    ret = stat64( path, &fileinfo );
+    if( ret < 0 ) {
+       /* If we can't stat the file, give up */
+       fprintf( stderr, "libdvdread: Can't stat %s\n", path );
+       perror("");
+       return 0;
+    }
+
+    /* First check if this is a block/char device or a file*/
+    if( S_ISBLK( fileinfo.st_mode ) || 
+       S_ISCHR( fileinfo.st_mode ) || 
+       S_ISREG( fileinfo.st_mode ) ) {
+
+       /**
+        * Block devices and regular files are assumed to be DVD-Video images.
+        */
+#if defined(__sun)
+       return DVDOpenImageFile( sun_block2char( path ) );
+#elif defined(SYS_BSD)
+       return DVDOpenImageFile( bsd_block2char( path ) );
+#else
+       return DVDOpenImageFile( path );
+#endif
+
+    } else if( S_ISDIR( fileinfo.st_mode ) ) {
+       dvd_reader_t *auth_drive = 0;
+       char *path_copy;
+#if defined(SYS_BSD)
+       struct fstab* fe;
+#elif defined(__sun) || defined(__linux__)
+       FILE *mntfile;
+#endif
+
+       /* XXX: We should scream real loud here. */
+       if( !(path_copy = strdup( path ) ) ) return 0;
+
+       /* Resolve any symlinks and get the absolut dir name. */
+       {
+           char *new_path;
+           int cdir = open( ".", O_RDONLY );
+           
+           if( cdir >= 0 ) {
+               chdir( path_copy );
+               new_path = getcwd( NULL, PATH_MAX );
+               fchdir( cdir );
+               close( cdir );
+               if( new_path ) {
+                   free( path_copy );
+                   path_copy = new_path;
+               }
+           }
+       }
+
+       /**
+        * If we're being asked to open a directory, check if that directory
+        * is the mountpoint for a DVD-ROM which we can use instead.
+        */
+
+       if( strlen( path_copy ) > 1 ) {
+           if( path[ strlen( path_copy ) - 1 ] == '/' ) 
+               path_copy[ strlen( path_copy ) - 1 ] = '\0';
+       }
+
+       if( strlen( path_copy ) > 9 ) {
+           if( !strcasecmp( &(path_copy[ strlen( path_copy ) - 9 ]), 
+                            "/video_ts" ) ) {
+             path_copy[ strlen( path_copy ) - 9 ] = '\0';
+           }
+       }
+
+#if defined(SYS_BSD)
+       if( ( fe = getfsfile( path_copy ) ) ) {
+           char *dev_name = bsd_block2char( fe->fs_spec );
+           fprintf( stderr,
+                    "libdvdread: Attempting to use device %s"
+                    " mounted on %s for CSS authentication\n",
+                    dev_name,
+                    fe->fs_file );
+           auth_drive = DVDOpenImageFile( dev_name );
+           free( dev_name );
+       }
+#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 ) ) {
+                   char *dev_name = sun_block2char( mp.mnt_special );
+                   fprintf( stderr, 
+                            "libdvdread: Attempting to use device %s"
+                            " mounted on %s for CSS authentication\n",
+                            dev_name,
+                            mp.mnt_mountp );
+                   auth_drive = DVDOpenImageFile( dev_name );
+                   free( dev_name );
+                   break;
+               }
+           }
+           fclose( mntfile );
+       }
+#elif defined(__linux__)
+        mntfile = fopen( MOUNTED, "r" );
+        if( mntfile ) {
+            struct mntent *me;
+            while( ( me = getmntent( mntfile ) ) ) {
+                if( !strcmp( me->mnt_dir, path_copy ) ) {
+                   fprintf( stderr, 
+                            "libdvdread: Attempting to use device %s"
+                             " mounted on %s for CSS authentication\n",
+                             me->mnt_fsname,
+                            me->mnt_dir );
+                    auth_drive = DVDOpenImageFile( me->mnt_fsname );
+                    break;
+                }
+            }
+            fclose( mntfile );
+       }
+#endif
+       if( !auth_drive ) {
+           fprintf( stderr, "libdvdread: Device inaccessible, "
+                    "CSS authentication not available.\n" );
+       }
+
+       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.
+         */
+       fprintf( stderr, "libdvdread: Using normal filesystem access.\n" );
+        return DVDOpenPath( path );
+    }
+
+    /* If it's none of the above, screw it. */
+    fprintf( stderr, "libdvdread: Could not open %s\n", path );
+    return 0;
+}
+
+void DVDClose( dvd_reader_t *dvd )
+{
+    if( dvd ) {
+        if( dvd->dev ) dvdcss_close( dvd->dev );
+        if( dvd->fd >= 0 ) close( dvd->fd );
+        if( dvd->path_root ) free( dvd->path_root );
+        free( dvd );
+        dvd = 0;
+    }
+}
+
+/**
+ * Open an unencrypted file on a DVD image file.
+ */
+static dvd_file_t *DVDOpenFileUDF( dvd_reader_t *dvd, char *filename )
+{
+    uint32_t start, len;
+    dvd_file_t *dvd_file;
+
+    start = UDFFindFile( dvd, filename, &len );
+    if( !start ) return 0;
+
+    dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
+    if( !dvd_file ) return 0;
+    dvd_file->dvd = dvd;
+    dvd_file->lb_start = start;
+    dvd_file->seek_pos = 0;
+    memset( dvd_file->title_fds, -1, sizeof( dvd_file->title_fds ) );
+    dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
+
+    return dvd_file;
+}
+
+/**
+ * Searches for <file> in directory <path>, ignoring case.
+ * Returns 0 and full filename in <filename>.
+ *     or -1 on file not found.
+ *     or -2 on path not found.
+ */
+static int findDirFile( const char *path, const char *file, char *filename ) 
+{
+    DIR *dir;
+    struct dirent *ent;
+
+    dir = opendir( path );
+    if( !dir ) return -2;
+
+    while( ( ent = readdir( dir ) ) != NULL ) {
+        if( !strcasecmp( ent->d_name, file ) ) {
+            sprintf( filename, "%s%s%s", path,
+                     ( ( path[ strlen( path ) - 1 ] == '/' ) ? "" : "/" ),
+                     ent->d_name );
+            return 0;
+        }
+    }
+
+    return -1;
+}
+
+static int findDVDFile( dvd_reader_t *dvd, const char *file, char *filename )
+{
+    char video_path[ PATH_MAX + 1 ];
+    const char *nodirfile;
+    int ret;
+
+    /* Strip off the directory for our search */
+    if( !strncasecmp( "/VIDEO_TS/", file, 10 ) ) {
+        nodirfile = &(file[ 10 ]);
+    } else {
+        nodirfile = file;
+    }
+
+    ret = findDirFile( dvd->path_root, nodirfile, filename );
+    if( ret < 0 ) {
+        /* Try also with adding the path, just in case. */
+        sprintf( video_path, "%s/VIDEO_TS/", dvd->path_root );
+        ret = findDirFile( video_path, nodirfile, filename );
+        if( ret < 0 ) {
+            /* Try with the path, but in lower case. */
+            sprintf( video_path, "%s/video_ts/", dvd->path_root );
+            ret = findDirFile( video_path, nodirfile, filename );
+            if( ret < 0 ) {
+                return 0;
+            }
+        }
+    }
+
+    return 1;
+}
+
+/**
+ * Open an unencrypted file from a DVD directory tree.
+ */
+static dvd_file_t *DVDOpenFilePath( dvd_reader_t *dvd, char *filename )
+{
+    char full_path[ PATH_MAX + 1 ];
+    dvd_file_t *dvd_file;
+    struct stat fileinfo;
+    int fd;
+
+    /* Get the full path of the file. */
+    if( !findDVDFile( dvd, filename, full_path ) ) return 0;
+
+    fd = open( full_path, O_RDONLY );
+    if( fd < 0 ) 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_fds, -1, sizeof( dvd_file->title_fds ) );
+    dvd_file->filesize = 0;
+
+    if( stat( full_path, &fileinfo ) < 0 ) {
+        fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
+        free( dvd_file );
+        return 0;
+    }
+    dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+    dvd_file->title_fds[ 0 ] = fd;
+    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;
+    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_fds, -1, sizeof( dvd_file->title_fds ) );
+    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( dvdcss_seek( dvd_file->dvd->dev, (int)start, DVDCSS_SEEK_KEY ) < 0 ) {
+            fprintf( stderr, "libdvdread: Error cracking CSS key for %s\n",
+                     filename );
+    }
+
+    return dvd_file;
+}
+
+static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *dvd, 
+                                  int title, int menu )
+{
+    char filename[ MAX_UDF_FILE_NAME_LEN ];
+    char full_path[ PATH_MAX + 1 ];
+    struct stat fileinfo;
+    dvd_file_t *dvd_file;
+    int i;
+
+    dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
+    if( !dvd_file ) return 0;
+    dvd_file->dvd = dvd;
+    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_fds, -1, sizeof( dvd_file->title_fds ) );
+    dvd_file->filesize = 0;
+
+    if( menu ) {
+        int fd;
+
+        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;
+        }
+
+        fd = open( full_path, O_RDONLY );
+        if( fd < 0 ) {
+            free( dvd_file );
+            return 0;
+        }
+
+        if( stat( full_path, &fileinfo ) < 0 ) {
+            fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
+            free( dvd_file );
+            return 0;
+        }
+        dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+        dvd_file->title_fds[ 0 ] = fd;
+        dvd_file->filesize = dvd_file->title_sizes[ 0 ];
+
+    } else {
+        for( i = 0; i < 9; ++i ) {
+
+            sprintf( filename, "VTS_%02i_%i.VOB", title, i + 1 );
+            if( !findDVDFile( dvd, filename, full_path ) ) {
+                break;
+            }
+
+            if( stat( full_path, &fileinfo ) < 0 ) {
+                fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
+                break;
+            }
+
+            dvd_file->title_sizes[ i ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+            dvd_file->title_fds[ i ] = open( full_path, O_RDONLY );
+            dvd_file->filesize += dvd_file->title_sizes[ i ];
+        }
+        if( !(dvd_file->title_sizes[ 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 ];
+
+    switch( domain ) {
+    case DVD_READ_INFO_FILE:
+        if( titlenum == 0 ) {
+            sprintf( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
+        } else {
+            sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );
+        }
+        break;
+    case DVD_READ_INFO_BACKUP_FILE:
+        if( titlenum == 0 ) {
+            sprintf( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
+        } else {
+            sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );
+        }
+        break;
+    case DVD_READ_MENU_VOBS:
+        if( dvd->isImageFile ) {
+            return DVDOpenVOBUDF( dvd, titlenum, 1 );
+        } else {
+            return DVDOpenVOBPath( dvd, titlenum, 1 );
+        }
+        break;
+    case DVD_READ_TITLE_VOBS:
+        if( titlenum == 0 ) return 0;
+        if( dvd->isImageFile ) {
+            return DVDOpenVOBUDF( dvd, titlenum, 0 );
+        } else {
+            return DVDOpenVOBPath( dvd, titlenum, 0 );
+        }
+        break;
+    default:
+        fprintf( stderr, "libdvdread: Invalid domain for file open.\n" );
+        return 0;
+    }
+    
+    if( dvd->isImageFile ) {
+        return DVDOpenFileUDF( dvd, filename );
+    } else {
+        return DVDOpenFilePath( dvd, filename );
+    }
+}
+
+void DVDCloseFile( dvd_file_t *dvd_file )
+{
+    int i;
+
+    if( dvd_file ) {
+        if( !dvd_file->dvd->isImageFile ) {
+            for( i = 0; i < 9; ++i ) {
+                if( dvd_file->title_fds[ i ] >= 0 )
+                   close( dvd_file->title_fds[ i ] );
+            }
+        }
+
+        free( dvd_file );
+        dvd_file = 0;
+    }
+}
+
+int64_t DVDReadLBUDF( dvd_reader_t *device, uint32_t lb_number,
+                     size_t block_count, unsigned char *data, 
+                     int encrypted )
+{
+    int ret;
+
+    if( !device->dev ) {
+        fprintf( stderr, "libdvdread: Fatal error in block read.\n" );
+        return 0;
+    }
+
+    ret = dvdcss_seek( device->dev, (int) lb_number, DVDCSS_NOFLAGS );
+    if( ret != (int) lb_number ) {
+        fprintf( stderr, "libdvdread: Can't seek to block %u\n", 
+                lb_number );
+        return 0;
+    }
+
+    return (int64_t) ( dvdcss_read( device->dev, (char *) data, 
+                           (int) block_count, encrypted ) 
+                  * (uint64_t) DVD_VIDEO_LB_LEN );
+}
+
+static int64_t DVDReadBlocksUDF( dvd_file_t *dvd_file, uint32_t offset,
+                                size_t block_count, unsigned char *data )
+{
+    return DVDReadLBUDF( dvd_file->dvd, dvd_file->lb_start + offset,
+                         block_count, data, DVDCSS_READ_DECRYPT );
+}
+
+static int64_t DVDReadBlocksPath( dvd_file_t *dvd_file, size_t offset,
+                                 size_t block_count, unsigned char *data )
+{
+    int i;
+    ssize_t ret, ret2;
+    off64_t off;
+
+    ret = 0;
+    ret2 = 0;
+    for( i = 0; i < 9; ++i ) {
+        if( !dvd_file->title_sizes[ i ] ) return 0;
+
+        if( offset < dvd_file->title_sizes[ i ] ) {
+            if( ( offset + block_count ) <= dvd_file->title_sizes[ i ] ) {
+               off = lseek64( dvd_file->title_fds[ i ], 
+                              offset * (int64_t) DVD_VIDEO_LB_LEN, SEEK_SET );
+               if( off != ( offset * (int64_t) DVD_VIDEO_LB_LEN ) ) {
+                   fprintf( stderr, "libdvdread: Can't seek to block %d\n", 
+                            offset );
+                   return 0;
+               }
+                ret = read( dvd_file->title_fds[ i ], data,
+                            block_count * DVD_VIDEO_LB_LEN );
+                break;
+            } else {
+               size_t part1_size 
+                 = ( dvd_file->title_sizes[ i ] - offset ) * DVD_VIDEO_LB_LEN;
+               /* 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 = lseek64( dvd_file->title_fds[ i ], 
+                              offset * (int64_t) DVD_VIDEO_LB_LEN, SEEK_SET );
+               if( off != ( offset * (int64_t) DVD_VIDEO_LB_LEN ) ) {
+                   fprintf( stderr, "libdvdread: Can't seek to block %d\n", 
+                            offset );
+                   return 0;
+               }
+                ret = read( dvd_file->title_fds[ i ], data, part1_size );
+               if( ret < 0 ) return ret;
+               /* FIXME: This is wrong if i is the last file in the set. 
+                         also error from this read will not show in ret. */
+               
+                /* Read part 2 */
+                lseek64( dvd_file->title_fds[ i + 1 ], (off64_t)0, SEEK_SET );
+                ret2 = read( dvd_file->title_fds[ i + 1 ], data + part1_size,
+                             block_count * DVD_VIDEO_LB_LEN - part1_size );
+                if( ret2 < 0 ) return ret2;
+               break;
+            }
+        } else {
+            offset -= dvd_file->title_sizes[ i ];
+        }
+    }
+
+    return ( (int64_t) ret + (int64_t) ret2 );
+}
+
+/* These are broken for some cases reading more than 2Gb at a time. */
+ssize_t DVDReadBlocks( dvd_file_t *dvd_file, int offset, 
+                      size_t block_count, unsigned char *data )
+{
+    int64_t ret;
+  
+    if( dvd_file->dvd->isImageFile ) {
+       ret = DVDReadBlocksUDF( dvd_file, (uint32_t)offset, 
+                               block_count, data );
+    } else {
+       ret = DVDReadBlocksPath( dvd_file, (size_t) offset, 
+                                block_count, data );
+    }
+    if( ret <= 0 ) {
+        return (ssize_t) ret;
+    }
+    {
+      ssize_t sret = (ssize_t) (ret / (int64_t)DVD_VIDEO_LB_LEN );
+      if( sret == 0 ) {
+       fprintf(stderr, "libdvdread: DVDReadBlocks got %d bytes\n", (int)ret );
+      }
+      return sret;
+    }
+}
+
+int32_t DVDFileSeek( dvd_file_t *dvd_file, int32_t offset )
+{
+    if( dvd_file->dvd->isImageFile ) {
+        dvd_file->seek_pos = (uint32_t) offset;
+        return offset;
+    } else {
+        return (int32_t) ( lseek( dvd_file->title_fds[ 0 ], 
+                                 (off_t) offset, SEEK_SET ) );
+    }
+}
+
+static ssize_t DVDReadBytesUDF( dvd_file_t *dvd_file, void *data, 
+                               size_t byte_size )
+{
+    unsigned char *secbuf;
+    unsigned int numsec, seek_sector, seek_byte;
+    int64_t len;
+    
+    seek_sector = dvd_file->seek_pos / DVD_VIDEO_LB_LEN;
+    seek_byte   = dvd_file->seek_pos % DVD_VIDEO_LB_LEN;
+
+    numsec = ( ( seek_byte + byte_size ) / DVD_VIDEO_LB_LEN ) + 1;
+    secbuf = (unsigned char *) malloc( numsec * DVD_VIDEO_LB_LEN );
+    if( !secbuf ) {
+       fprintf( stderr, "libdvdread: Can't allocate memory " 
+                "for file read!\n" );
+        return 0;
+    }
+
+    len = DVDReadLBUDF( dvd_file->dvd, dvd_file->lb_start + seek_sector,
+                        numsec, secbuf, DVDCSS_NOFLAGS );
+    if( len != numsec * (int64_t) DVD_VIDEO_LB_LEN ) {
+        free( secbuf );
+        return 0;
+    }
+
+    dvd_file->seek_pos += byte_size;
+
+    memcpy( data, &(secbuf[ seek_byte ]), byte_size );
+    free( secbuf );
+
+    return byte_size;
+}
+
+static ssize_t DVDReadBytesPath( dvd_file_t *dvd_file, void *data, 
+                                size_t byte_size )
+{
+    return read( dvd_file->title_fds[ 0 ], data, byte_size );
+}
+
+ssize_t DVDReadBytes( dvd_file_t *dvd_file, void *data, size_t byte_size )
+{
+    if( dvd_file->dvd->isImageFile ) {
+        return DVDReadBytesUDF( dvd_file, data, byte_size );
+    } else {
+        return DVDReadBytesPath( dvd_file, data, byte_size );
+    }
+}
+
+ssize_t DVDFileSize( dvd_file_t *dvd_file )
+{
+    return dvd_file->filesize;
+}
+
+int64_t DVDReadVLBUDF( dvd_reader_t *device, uint32_t lb_number,
+               size_t block_count, struct iovec * vector,
+              int encrypted )
+{
+    int ret;
+
+    if( !device->dev ) {
+        fprintf( stderr, "libdvdread: Fatal error in block read.\n" );
+        return 0;
+    }
+
+    ret = dvdcss_seek( device->dev, (int) lb_number, 0 );
+    if( ret != (int) lb_number ) {
+        fprintf( stderr, "libdvdread: Can't seek to block %u\n",
+                 lb_number );
+        return 0;
+    }
+
+    return (int64_t) ( dvdcss_readv( device->dev, vector,
+                           (int)block_count, encrypted )
+                   * (uint64_t) DVD_VIDEO_LB_LEN );
+}
+
+static int64_t DVDReadVBlocksUDF( dvd_file_t *dvd_file, uint32_t offset,
+                 size_t block_count, struct iovec *vector )
+{
+    return DVDReadVLBUDF( dvd_file->dvd, dvd_file->lb_start + offset,
+                         block_count, vector, DVDCSS_READ_DECRYPT );
+}
+
+
+ssize_t DVDReadVBlocks( dvd_file_t *dvd_file, int offset,
+               size_t block_count, struct iovec * vector )
+{
+    int64_t ret;
+
+    if( dvd_file->dvd->isImageFile ) {
+    ret = DVDReadVBlocksUDF( dvd_file, (uint32_t)offset,
+                block_count, vector );
+    } else {
+  ret = 0;//= DVDReadVBlocksPath( dvd_file, (size_t) offset, 
+//               block_count, vector );
+    }
+    if( ret <= 0 ) {
+        return (ssize_t) ret;
+    }
+    {
+      ssize_t sret = (ssize_t) (ret / (int64_t)DVD_VIDEO_LB_LEN );
+      if( sret == 0 ) {
+    fprintf(stderr, "libdvdread: DVDReadVBlocks got %d bytes\n", (int)ret );
+      }
+      return sret;
+    }
+
+}
+
diff --git a/extras/libdvdread/ifo_print.c b/extras/libdvdread/ifo_print.c
new file mode 100644 (file)
index 0000000..9b53636
--- /dev/null
@@ -0,0 +1,1044 @@
+/* 
+ * Copyright (C) 2000 Björn Englund <d4bjorn@dtek.chalmers.se>, 
+ *                    Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "config.h" // Needed for WORDS_BIGENDIAN
+#include "ifo_types.h"
+#include "ifo_read.h"
+#include "ifo_print.h"
+
+/* Put this in some other file / package?  It's used in nav_print too. */
+static void ifoPrint_time(int level, dvd_time_t *dtime) {
+  const char *rate;
+  assert((dtime->hour>>4) < 0xa && (dtime->hour&0xf) < 0xa);
+  assert((dtime->minute>>4) < 0x7 && (dtime->minute&0xf) < 0xa);
+  assert((dtime->second>>4) < 0x7 && (dtime->second&0xf) < 0xa);
+  assert((dtime->frame_u&0xf) < 0xa);
+  
+  printf("%02x:%02x:%02x.%02x", 
+        dtime->hour,
+        dtime->minute,
+        dtime->second,
+        dtime->frame_u & 0x3f);
+  switch((dtime->frame_u & 0xc0) >> 6) {
+  case 1:
+    rate = "25.00";
+    break;
+  case 3:
+    rate = "29.97";
+    break;
+  default:
+    if(dtime->hour == 0 && dtime->minute == 0 
+       && dtime->second == 0 && dtime->frame_u == 0)
+      rate = "no";
+    else
+      rate = "(please send a bug report)";
+    break;
+  } 
+  printf(" @ %s fps", rate);
+}
+
+/* Put this in some other file / package?  It's used in nav_print too.
+   Possibly also by the vm / navigator. */
+static void ifoPrint_CMD(int row, vm_cmd_t *command) {
+  int i;
+
+  printf("(%03d) ", row + 1);
+  for(i=0;i<8;i++)
+    printf("%02x ", command->bytes[i]);
+  printf("| ");
+
+  //vmcmd(command);
+  printf("\n");
+}
+
+static void ifoPrint_video_attributes(int level, video_attr_t *attr) {
+  
+  /* The following test is shorter but not correct ISO C,
+     memcmp(attr,my_friendly_zeros, sizeof(video_attr_t)) */
+  if(attr->mpeg_version == 0 
+     && attr->video_format == 0 
+     && attr->display_aspect_ratio == 0 
+     && attr->permitted_df == 0 
+     && attr->unknown1 == 0 
+     && attr->line21_cc_1 == 0 
+     && attr->line21_cc_2 == 0 
+     && attr->video_format == 0 
+     && attr->letterboxed == 0 
+     && attr->film_mode == 0) {
+    printf("-- Unspecified --");
+    return;
+  }
+  
+  switch(attr->mpeg_version) {
+  case 0:
+    printf("mpeg1 ");
+    break;
+  case 1:
+    printf("mpeg2 ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+  
+  switch(attr->video_format) {
+  case 0:
+    printf("ntsc ");
+    break;
+  case 1:
+    printf("pal ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+  
+  switch(attr->display_aspect_ratio) {
+  case 0:
+    printf("4:3 ");
+    break;
+  case 3:
+    printf("16:9 ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+  
+  // Wide is allways allowed..!!!
+  switch(attr->permitted_df) {
+  case 0:
+    printf("pan&scan+letterboxed ");
+    break;
+  case 1:
+    printf("only pan&scan "); //??
+    break;
+  case 2:
+    printf("only letterboxed ");
+    break;
+  case 3:
+    // not specified
+    break;
+  default:
+    printf("(please send a bug report)");
+  }
+  
+  printf("U%x ", attr->unknown1);
+  assert(!attr->unknown1);
+  
+  if(attr->line21_cc_1 || attr->line21_cc_2) {
+    printf("NTSC CC ");
+    if(attr->line21_cc_1)
+      printf("1 ");
+    if(attr->line21_cc_2)
+      printf("2 ");
+  }
+  
+  {
+    int height = 480;
+    if(attr->video_format != 0) 
+      height = 576;
+    switch(attr->picture_size) {
+    case 0:
+      printf("720x%d ", height);
+      break;
+    case 1:
+      printf("704x%d ", height);
+      break;
+    case 2:
+      printf("352x%d ", height);
+      break;
+    case 3:
+      printf("352x%d ", height/2);
+      break;      
+    default:
+      printf("(please send a bug report) ");
+    }
+  }
+
+  if(attr->letterboxed) {
+    printf("letterboxed ");
+  }
+  
+  if(attr->film_mode) {
+    printf("film");
+  } else {
+    printf("video"); //camera
+  }
+}
+
+static void ifoPrint_audio_attributes(int level, audio_attr_t *attr) {
+  
+  if(attr->audio_format == 0
+     && attr->multichannel_extension == 0
+     && attr->lang_type == 0
+     && attr->application_mode == 0
+     && attr->quantization == 0
+     && attr->sample_frequency == 0
+     && attr->channels == 0
+     && attr->lang_extension == 0
+     && attr->unknown1 == 0
+     && attr->unknown1 == 0) {
+    printf("-- Unspecified --");
+    return;
+  }
+  
+  switch(attr->audio_format) {
+  case 0:
+    printf("ac3 ");
+    break;
+  case 1:
+    printf("(please send a bug report) ");
+    break;
+  case 2:
+    printf("mpeg1 ");
+    break;
+  case 3:
+    printf("mpeg2ext ");
+    break;
+  case 4:
+    printf("lpcm ");
+    break;
+  case 5:
+    printf("(please send a bug report) ");
+    break;
+  case 6:
+    printf("dts ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+  
+  if(attr->multichannel_extension)
+    printf("multichannel_extension ");
+  
+  switch(attr->lang_type) {
+  case 0:
+    // not specified
+    assert(attr->lang_code == 0);
+    break;
+  case 1:
+    printf("%c%c ", attr->lang_code>>8, attr->lang_code & 0xff);
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+
+  switch(attr->application_mode) {
+  case 0:
+    // not specified
+    break;
+  case 1:
+    printf("karaoke mode ");
+    break;
+  case 2:
+    printf("surround sound mode ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+  
+  switch(attr->quantization) {
+  case 0:
+    printf("16bit ");
+    break;
+  case 1:
+    printf("20bit ");
+    break;
+  case 2:
+    printf("24bit ");
+    break;
+  case 3:
+    printf("drc ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+  
+  switch(attr->sample_frequency) {
+  case 0:
+    printf("48kHz ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+  
+  printf("%dCh ", attr->channels + 1);
+  
+  switch(attr->lang_extension) {
+  case 0:
+    printf("Not specified ");
+    break;
+  case 1: // Normal audio
+    printf("Normal Caption ");
+    break;
+  case 2: // visually imparied
+    printf("Audio for visually impaired ");
+    break;
+  case 3: // Directors 1
+    printf("Director's comments 1 ");
+    break;
+  case 4: // Directors 2
+    printf("Director's comments 2 ");
+    break;
+    //case 4: // Music score ?    
+  default:
+    printf("(please send a bug report) ");
+  }
+    
+  printf("%d ", attr->unknown1);
+  printf("%d ", attr->unknown2);
+}
+
+static void ifoPrint_subp_attributes(int level, subp_attr_t *attr) {
+  
+  if(attr->type == 0
+     && attr->lang_code == 0
+     && attr->zero1 == 0
+     && attr->zero2 == 0
+     && attr->lang_extension== 0) {
+    printf("-- Unspecified --");
+    return;
+  }
+  
+  printf("type %02x ", attr->type);
+  
+  if(isalpha((int)(attr->lang_code >> 8))
+     && isalpha((int)(attr->lang_code & 0xff))) {
+    printf("%c%c ", attr->lang_code >> 8, attr->lang_code & 0xff);
+  } else {
+    printf("%02x%02x ", 0xff & (unsigned)(attr->lang_code >> 8), 
+          0xff & (unsigned)(attr->lang_code & 0xff));
+  }
+  
+  printf("%d ", attr->zero1);
+  printf("%d ", attr->zero2);
+
+  switch(attr->lang_extension) {
+  case 0:
+    printf("Not specified ");
+    break;
+  case 1:
+    printf("Caption with normal size character ");
+    break;
+  case 2:
+    printf("Caption with bigger size character ");
+    break;
+  case 3: 
+    printf("Caption for children ");
+    break;
+  case 4:
+    printf("reserved ");
+    break;
+  case 5:
+    printf("Closed Caption with normal size character ");
+    break;
+  case 6:
+    printf("Closed Caption with bigger size character ");
+    break;
+  case 7:
+    printf("Closed Caption for children ");
+    break;
+  case 8:
+    printf("reserved ");
+    break;
+  case 9:
+    printf("Forced Caption");
+    break;
+  case 10:
+    printf("reserved ");
+    break;
+  case 11:
+    printf("reserved ");
+    break;
+  case 12:
+    printf("reserved ");
+    break;
+  case 13:
+    printf("Director's comments with normal size character ");
+    break;
+  case 14:
+    printf("Director's comments with bigger size character ");
+    break;
+  case 15:
+    printf("Director's comments for children ");
+    break;
+  default:
+    printf("(please send a bug report) ");
+  }
+
+}
+
+
+void ifoPrint_USER_OPS(user_ops_t *user_ops) {
+  uint32_t uops;
+  unsigned char *ptr = (unsigned char *)user_ops;
+  
+  uops  = (*ptr++ << 24);
+  uops |= (*ptr++ << 16);
+  uops |= (*ptr++ << 8);
+  uops |= (*ptr++);
+  
+  if(uops == 0) {
+    printf("None\n");
+  } else if(uops == 0x01ffffff) {
+    printf("All\n");
+  } else {
+    if(user_ops->title_or_time_play)
+      printf("Title or Time Play, ");
+    if(user_ops->chapter_search_or_play)
+      printf("Chapter Search or Play, ");
+    if(user_ops->title_play)
+      printf("Title Play, ");
+    if(user_ops->stop)
+      printf("Stop, ");
+    if(user_ops->go_up)
+      printf("Go Up, ");
+    if(user_ops->time_or_chapter_search)
+      printf("Time or Chapter Search, ");
+    if(user_ops->prev_or_top_pg_search)
+      printf("Prev or Top PG Search, ");
+    if(user_ops->next_pg_search)
+      printf("Next PG Search, ");
+    if(user_ops->forward_scan)
+      printf("Forward Scan, ");
+    if(user_ops->backward_scan)
+      printf("Backward Scan, ");
+    if(user_ops->title_menu_call)
+      printf("Title Menu Call, ");
+    if(user_ops->root_menu_call)
+      printf("Root Menu Call, ");
+    if(user_ops->subpic_menu_call)
+      printf("SubPic Menu Call, ");
+    if(user_ops->audio_menu_call)
+      printf("Audio Menu Call, ");
+    if(user_ops->angle_menu_call)
+      printf("Angle Menu Call, ");
+    if(user_ops->chapter_menu_call)
+      printf("Chapter Menu Call, ");
+    if(user_ops->resume)
+      printf("Resume, ");
+    if(user_ops->button_select_or_activate)
+      printf("Button Select or Activate, ");
+    if(user_ops->still_off)
+      printf("Still Off, ");
+    if(user_ops->pause_on)
+      printf("Pause On, ");
+    if(user_ops->audio_stream_change)
+      printf("Audio Stream Change, ");
+    if(user_ops->subpic_stream_change)
+      printf("SubPic Stream Change, ");
+    if(user_ops->angle_change)
+      printf("Angle Change, ");
+    if(user_ops->karaoke_audio_pres_mode_change)
+      printf("Karaoke Audio Pres Mode Change, ");
+    if(user_ops->video_pres_mode_change)
+      printf("Video Pres Mode Change, ");
+    printf("\n");
+  }
+}
+
+
+void ifoPrint_VMGI_MAT(vmgi_mat_t *vmgi_mat) {
+  
+  printf("VMG Identifier: %.12s\n", vmgi_mat->vmg_identifier);
+  printf("Last Sector of VMG: %08x\n", vmgi_mat->vmg_last_sector);
+  printf("Last Sector of VMGI: %08x\n", vmgi_mat->vmgi_last_sector);
+  printf("Specification version number: %01x.%01x\n", 
+        vmgi_mat->specification_version >> 4, 
+        vmgi_mat->specification_version & 0xf);
+  /* Byte 2 of 'VMG Category' (00xx0000) is the Region Code */
+  printf("VMG Category: %08x\n", vmgi_mat->vmg_category);
+  printf("VMG Number of Volumes: %i\n", vmgi_mat->vmg_nr_of_volumes);
+  printf("VMG This Volume: %i\n", vmgi_mat->vmg_this_volume_nr);
+  printf("Disc side %i\n", vmgi_mat->disc_side);
+  printf("VMG Number of Title Sets %i\n", vmgi_mat->vmg_nr_of_title_sets);
+  printf("Provider ID: %.32s\n", vmgi_mat->provider_identifier);
+  printf("VMG POS Code: %08x", (uint32_t)(vmgi_mat->vmg_pos_code >> 32));
+  printf("%08x\n", (uint32_t)vmgi_mat->vmg_pos_code);
+  printf("End byte of VMGI_MAT: %08x\n", vmgi_mat->vmgi_last_byte);
+  printf("Start byte of First Play PGC FP PGC: %08x\n", 
+        vmgi_mat->first_play_pgc);
+  printf("Start sector of VMGM_VOBS: %08x\n", vmgi_mat->vmgm_vobs);
+  printf("Start sector of TT_SRPT: %08x\n", vmgi_mat->tt_srpt);
+  printf("Start sector of VMGM_PGCI_UT: %08x\n", vmgi_mat->vmgm_pgci_ut);
+  printf("Start sector of PTL_MAIT: %08x\n", vmgi_mat->ptl_mait);
+  printf("Start sector of VTS_ATRT: %08x\n", vmgi_mat->vts_atrt);
+  printf("Start sector of TXTDT_MG: %08x\n", vmgi_mat->txtdt_mgi);
+  printf("Start sector of VMGM_C_ADT: %08x\n", vmgi_mat->vmgm_c_adt);
+  printf("Start sector of VMGM_VOBU_ADMAP: %08x\n", 
+        vmgi_mat->vmgm_vobu_admap);
+  printf("Video attributes of VMGM_VOBS: ");
+  ifoPrint_video_attributes(5, &vmgi_mat->vmgm_video_attr);
+  printf("\n");
+  printf("VMGM Number of Audio attributes: %i\n", 
+        vmgi_mat->nr_of_vmgm_audio_streams);
+  if(vmgi_mat->nr_of_vmgm_audio_streams > 0) {
+    printf("\tstream %i status: ", 1);
+    ifoPrint_audio_attributes(5, &vmgi_mat->vmgm_audio_attr);
+    printf("\n");
+  }
+  printf("VMGM Number of Sub-picture attributes: %i\n", 
+        vmgi_mat->nr_of_vmgm_subp_streams);
+  if(vmgi_mat->nr_of_vmgm_subp_streams > 0) {
+    printf("\tstream %2i status: ", 1);
+    ifoPrint_subp_attributes(5, &vmgi_mat->vmgm_subp_attr);
+    printf("\n");
+  }
+}
+
+
+void ifoPrint_VTSI_MAT(vtsi_mat_t *vtsi_mat) {
+  int i;
+
+  printf("VTS Identifier: %.12s\n", vtsi_mat->vts_identifier);
+  printf("Last Sector of VTS: %08x\n", vtsi_mat->vts_last_sector);
+  printf("Last Sector of VTSI: %08x\n", vtsi_mat->vtsi_last_sector);
+  printf("Specification version number: %01x.%01x\n", 
+        vtsi_mat->specification_version>>4, 
+        vtsi_mat->specification_version&0xf);
+  printf("VTS Category: %08x\n", vtsi_mat->vts_category);
+  printf("End byte of VTSI_MAT: %08x\n", vtsi_mat->vtsi_last_byte);
+  printf("Start sector of VTSM_VOBS:  %08x\n", vtsi_mat->vtsm_vobs);
+  printf("Start sector of VTSTT_VOBS: %08x\n", vtsi_mat->vtstt_vobs);
+  printf("Start sector of VTS_PTT_SRPT: %08x\n", vtsi_mat->vts_ptt_srpt);
+  printf("Start sector of VTS_PGCIT:    %08x\n", vtsi_mat->vts_pgcit);
+  printf("Start sector of VTSM_PGCI_UT: %08x\n", vtsi_mat->vtsm_pgci_ut);
+  printf("Start sector of VTS_TMAPT:    %08x\n", vtsi_mat->vts_tmapt);
+  printf("Start sector of VTSM_C_ADT:      %08x\n", vtsi_mat->vtsm_c_adt);
+  printf("Start sector of VTSM_VOBU_ADMAP: %08x\n",vtsi_mat->vtsm_vobu_admap);
+  printf("Start sector of VTS_C_ADT:       %08x\n", vtsi_mat->vts_c_adt);
+  printf("Start sector of VTS_VOBU_ADMAP:  %08x\n", vtsi_mat->vts_vobu_admap);
+
+  printf("Video attributes of VTSM_VOBS: ");
+  ifoPrint_video_attributes(5, &vtsi_mat->vtsm_video_attr);
+  printf("\n");
+  
+  printf("VTSM Number of Audio attributes: %i\n", 
+        vtsi_mat->nr_of_vtsm_audio_streams);
+  if(vtsi_mat->nr_of_vtsm_audio_streams > 0) {
+    printf("\tstream %i status: ", 1);
+    ifoPrint_audio_attributes(5, &vtsi_mat->vtsm_audio_attr);
+    printf("\n");
+  }
+  
+  printf("VTSM Number of Sub-picture attributes: %i\n", 
+        vtsi_mat->nr_of_vtsm_subp_streams);
+  if(vtsi_mat->nr_of_vtsm_subp_streams > 0) {
+    printf("\tstream %2i status: ", 1);
+    ifoPrint_subp_attributes(5, &vtsi_mat->vtsm_subp_attr);
+    printf("\n");
+  }
+  
+  printf("Video attributes of VTS_VOBS: ");
+  ifoPrint_video_attributes(5, &vtsi_mat->vts_video_attr);
+  printf("\n");
+  
+  printf("VTS Number of Audio attributes: %i\n", 
+        vtsi_mat->nr_of_vts_audio_streams);
+  for(i = 0; i < vtsi_mat->nr_of_vts_audio_streams; i++) {
+    printf("\tstream %i status: ", i);
+    ifoPrint_audio_attributes(5, &vtsi_mat->vts_audio_attr[i]);
+    printf("\n");
+  }
+  
+  printf("VTS Number of Subpicture attributes: %i\n", 
+        vtsi_mat->nr_of_vts_subp_streams);
+  for(i = 0; i < vtsi_mat->nr_of_vts_subp_streams; i++) {
+    printf("\tstream %2i status: ", i);
+    ifoPrint_subp_attributes(5, &vtsi_mat->vts_subp_attr[i]);
+    printf("\n");
+  }
+}
+
+
+static void ifoPrint_PGC_COMMAND_TBL(pgc_command_tbl_t *cmd_tbl) {
+  int i;
+  
+  if(cmd_tbl == NULL) {
+    printf("No Command table present\n");
+    return;
+  }
+  
+  printf("Number of Pre commands: %i\n", cmd_tbl->nr_of_pre);
+  for(i = 0; i < cmd_tbl->nr_of_pre; i++) {
+    ifoPrint_CMD(i, &cmd_tbl->pre_cmds[i]);
+  }
+
+  printf("Number of Post commands: %i\n", cmd_tbl->nr_of_post);
+  for(i = 0; i < cmd_tbl->nr_of_post; i++) {
+    ifoPrint_CMD(i, &cmd_tbl->post_cmds[i]);
+  }
+
+  printf("Number of Cell commands: %i\n", cmd_tbl->nr_of_cell);
+  for(i = 0; i < cmd_tbl->nr_of_cell; i++) {
+    ifoPrint_CMD(i, &cmd_tbl->cell_cmds[i]);
+  }
+}
+
+
+static void ifoPrint_PGC_PROGRAM_MAP(pgc_program_map_t *program_map, int nr) {
+  int i;
+  
+  if(program_map == NULL) {
+    printf("No Program map present\n");
+    return;
+  }
+  
+  for(i = 0; i < nr; i++) {
+    printf("Program %3i Entry Cell: %3i\n", i + 1, program_map[i]);
+  }
+}
+
+
+static void ifoPrint_CELL_PLAYBACK(cell_playback_t *cell_playback, int nr) {
+  int i;
+  
+  if(cell_playback == NULL) {
+    printf("No Cell Playback info present\n");
+    return;
+  }
+  
+  for(i=0;i<nr;i++) {
+    printf("Cell: %3i ", i + 1);
+
+    ifoPrint_time(5, &cell_playback[i].playback_time);
+    printf("\t");
+
+    if(cell_playback[i].block_mode || cell_playback[i].block_type) {
+      const char *s;
+      switch(cell_playback[i].block_mode) {
+      case 0:
+       s = "not a"; break;
+      case 1:
+       s = "the first"; break;
+      case 2:
+      default:
+       s = ""; break;
+      case 3:
+       s = "last"; break;
+      }
+      printf("%s cell in the block ", s);
+      
+      switch(cell_playback[i].block_type) {
+      case 0:
+       printf("not part of the block ");
+       break;
+      case 1:
+       printf("angle block ");
+       break;
+      case 2:
+      case 3:
+       printf("(send bug repport) ");
+       break;
+      }
+    }
+    if(cell_playback[i].seamless_play)
+      printf("presented seamlessly ");
+    if(cell_playback[i].interleaved)
+      printf("cell is interleaved ");
+    if(cell_playback[i].stc_discontinuity)
+      printf("STC_discontinuty ");
+    if(cell_playback[i].seamless_angle)
+      printf("only seamless angle ");
+    if(cell_playback[i].restricted)
+      printf("restricted cell ");
+    
+    if(cell_playback[i].still_time)
+      printf("still time %d ", cell_playback[i].still_time);
+    if(cell_playback[i].cell_cmd_nr)
+      printf("cell command %d", cell_playback[i].cell_cmd_nr);
+    
+    printf("\n\tStart sector: %08x\tFirst ILVU end  sector: %08x\n", 
+          cell_playback[i].first_sector, 
+          cell_playback[i].first_ilvu_end_sector);
+    printf("\tEnd   sector: %08x\tLast VOBU start sector: %08x\n", 
+          cell_playback[i].last_sector, 
+          cell_playback[i].last_vobu_start_sector);
+  }
+}
+
+static void ifoPrint_CELL_POSITION(cell_position_t *cell_position, int nr) {
+  int i;
+  
+  if(cell_position == NULL) {
+    printf("No Cell Position info present\n");
+    return;
+  }
+  
+  for(i=0;i<nr;i++) {
+    printf("Cell: %3i has VOB ID: %3i, Cell ID: %3i\n", i + 1, 
+          cell_position[i].vob_id_nr, cell_position[i].cell_nr);
+  }
+}
+
+
+void ifoPrint_PGC(pgc_t *pgc) {
+  int i;
+  
+  printf("Number of Programs: %i\n", pgc->nr_of_programs);
+  printf("Number of Cells: %i\n", pgc->nr_of_cells);
+  /* Check that time is 0:0:0:0 also if nr_of_programs==0 */
+  printf("Playback time: ");
+  ifoPrint_time(5, &pgc->playback_time); printf("\n");
+
+  /* If no programs/no time then does this mean anything? */
+  printf("Prohibited user operations: ");
+  ifoPrint_USER_OPS(&pgc->prohibited_ops);
+  
+    for(i = 0; i < 8; i++) {
+      if(pgc->audio_control[i] & 0x8000) { /* The 'is present' bit */
+       printf("Audio stream %i control: %04x\n", 
+              i, pgc->audio_control[i]);
+      }
+    }
+  
+  for(i = 0; i < 32; i++) {
+    if(pgc->subp_control[i] & 0x80000000) { /* The 'is present' bit */
+      printf("Subpicture stream %2i control: %08x\n", 
+            i, pgc->subp_control[i]);
+    }
+  }
+  
+  printf("Next PGC number: %i\n", pgc->next_pgc_nr);
+  printf("Prev PGC number: %i\n", pgc->prev_pgc_nr);
+  printf("GoUp PGC number: %i\n", pgc->goup_pgc_nr);
+  if(pgc->nr_of_programs != 0) {
+    printf("Still time: %i seconds (255=inf)\n", pgc->still_time);
+    printf("PG Playback mode %02x\n", pgc->pg_playback_mode);
+  }
+  
+  if(pgc->nr_of_programs != 0) {
+    for(i = 0; i < 16; i++) {
+      printf("Color %2i: %08x\n", i, pgc->palette[i]);
+    }
+  }
+  
+  /* Memmory offsets to div. tables. */
+  ifoPrint_PGC_COMMAND_TBL(pgc->command_tbl);
+  ifoPrint_PGC_PROGRAM_MAP(pgc->program_map, pgc->nr_of_programs);
+  ifoPrint_CELL_PLAYBACK(pgc->cell_playback, pgc->nr_of_cells);
+  ifoPrint_CELL_POSITION(pgc->cell_position, pgc->nr_of_cells);
+}
+
+
+void ifoPrint_TT_SRPT(tt_srpt_t *tt_srpt) {
+  int i;
+  
+  printf("Number of TitleTrack search pointers: %i\n",
+        tt_srpt->nr_of_srpts);
+  for(i=0;i<tt_srpt->nr_of_srpts;i++) {
+    printf("Title Track index %i\n", i + 1);
+    printf("\tTitle set number (VTS): %i", 
+          tt_srpt->title[i].title_set_nr);
+    printf("\tVTS_TTN: %i\n", tt_srpt->title[i].vts_ttn);
+    printf("\tNumber of PTTs: %i\n", tt_srpt->title[i].nr_of_ptts);
+    printf("\tNumber of angles: %i\n", 
+          tt_srpt->title[i].nr_of_angles);
+    printf("\tTitle playback type: %02x\n",     /* XXX: TODO FIXME */
+          *(uint8_t *)&(tt_srpt->title[i].pb_ty));
+    printf("\tParental ID field: %04x\n",
+          tt_srpt->title[i].parental_id);
+    printf("\tTitle set starting sector %08x\n", 
+          tt_srpt->title[i].title_set_sector);
+  }
+}
+
+
+void ifoPrint_VTS_PTT_SRPT(vts_ptt_srpt_t *vts_ptt_srpt) {
+  int i, j;
+  printf(" nr_of_srpts %i last byte %i\n", 
+        vts_ptt_srpt->nr_of_srpts, 
+        vts_ptt_srpt->last_byte);
+  for(i=0;i<vts_ptt_srpt->nr_of_srpts;i++) {
+    for(j=0;j<vts_ptt_srpt->title[i].nr_of_ptts;j++) {
+      printf("VTS_PTT_SRPT - Title %3i part %3i: PGC: %3i PG: %3i\n",
+            i + 1, j + 1, 
+            vts_ptt_srpt->title[i].ptt[j].pgcn,
+            vts_ptt_srpt->title[i].ptt[j].pgn );
+    }
+  }
+}
+
+
+static void hexdump(uint8_t *ptr, int len) {
+  while(len--)
+    printf("%02x ", *ptr++);
+}
+
+void ifoPrint_PTL_MAIT(ptl_mait_t *ptl_mait) {
+  int i, j;
+  
+  printf("Number of Countries: %i\n", ptl_mait->nr_of_countries);
+  printf("Number of VTSs: %i\n", ptl_mait->nr_of_vtss);
+  //printf("Last byte: %i\n", ptl_mait->last_byte);
+  
+  for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+    printf("Country code: %c%c\n", 
+          ptl_mait->countries[i].country_code >> 8,
+          ptl_mait->countries[i].country_code & 0xff);
+    /*
+      printf("Start byte: %04x %i\n", 
+      ptl_mait->countries[i].pf_ptl_mai_start_byte, 
+      ptl_mait->countries[i].pf_ptl_mai_start_byte);
+    */
+    /* This seems to be pointing at a array with 8 2byte fields per VTS
+       ? and one extra for the menu? always an odd number of VTSs on
+       all the dics I tested so it might be padding to even also.
+       If it is for the menu it probably the first entry.  */
+    for(j=0;j<8;j++) {
+      hexdump( (uint8_t *)ptl_mait->countries - PTL_MAIT_COUNTRY_SIZE 
+              + ptl_mait->countries[i].pf_ptl_mai_start_byte
+              + j*(ptl_mait->nr_of_vtss+1)*2, (ptl_mait->nr_of_vtss+1)*2);
+      printf("\n");
+    }
+  }
+}
+
+
+void ifoPrint_C_ADT(c_adt_t *c_adt) {
+  int i, entries;
+  
+  printf("Number of VOBs in this VOBS: %i\n", c_adt->nr_of_vobs);
+  entries = (c_adt->last_byte + 1 - C_ADT_SIZE)/sizeof(c_adt_t);
+  
+  for(i = 0; i < entries; i++) {
+    printf("VOB ID: %3i, Cell ID: %3i   ", 
+          c_adt->cell_adr_table[i].vob_id, c_adt->cell_adr_table[i].cell_id);
+    printf("Sector (first): 0x%08x   (last): 0x%08x\n",
+          c_adt->cell_adr_table[i].start_sector, 
+          c_adt->cell_adr_table[i].last_sector);
+  }
+}
+
+
+void ifoPrint_VOBU_ADMAP(vobu_admap_t *vobu_admap) {
+  int i, entries;
+  
+  entries = (vobu_admap->last_byte + 1 - VOBU_ADMAP_SIZE)/4;
+  for(i = 0; i < entries; i++) {
+    printf("VOBU %5i  First sector: 0x%08x\n", i + 1,
+          vobu_admap->vobu_start_sectors[i]);
+  }
+}
+
+
+void ifoPrint_PGCIT(pgcit_t *pgcit) {
+  int i;
+  
+  for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+    printf("\nProgram (PGC): %3i\t", i + 1);
+    printf("PGC Category: Entry id 0x%02x, ", pgcit->pgci_srp[i].entry_id);
+    printf("Parental ID mask 0x%04x\n", pgcit->pgci_srp[i].ptl_id_mask);
+    ifoPrint_PGC(pgcit->pgci_srp[i].pgc);
+  }
+}
+
+
+void ifoPrint_PGCI_UT(pgci_ut_t *pgci_ut) {
+  int i;
+  
+  printf("Number of Menu Language Units (PGCI_LU): %3i\n", pgci_ut->nr_of_lus);
+  for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+    printf("\nMenu Language Code: %c%c\n",
+          pgci_ut->lu[i].lang_code >> 8,
+          pgci_ut->lu[i].lang_code & 0xff);
+    printf("Menu Existence: %02x\n", pgci_ut->lu[i].exists);
+    ifoPrint_PGCIT(pgci_ut->lu[i].pgcit);
+  }
+}
+
+
+void ifoPrint_VTS_ATTRIBUTES(vts_attributes_t *vts_attributes) {
+  int i;
+  
+  printf("VTS_CAT Application type: %08x\n", vts_attributes->vts_cat);
+  printf("Video attributes of VTSM_VOBS: ");
+  ifoPrint_video_attributes(5, &vts_attributes->vtsm_vobs_attr);
+  printf("\n");
+  printf("Number of Audio streams: %i\n", 
+        vts_attributes->nr_of_vtsm_audio_streams);
+  if(vts_attributes->nr_of_vtsm_audio_streams > 0) {
+    printf("\tstream %i attributes: ", 1);
+    ifoPrint_audio_attributes(5, &vts_attributes->vtsm_audio_attr);
+    printf("\n");
+  }
+  printf("Number of Subpicture streams: %i\n", 
+        vts_attributes->nr_of_vtsm_subp_streams);
+  if(vts_attributes->nr_of_vtsm_subp_streams > 0) {
+    printf("\tstream %2i attributes: ", 1);
+    ifoPrint_subp_attributes(5, &vts_attributes->vtsm_subp_attr);
+    printf("\n");
+  }
+   
+  printf("Video attributes of VTSTT_VOBS: ");
+  ifoPrint_video_attributes(5, &vts_attributes->vtstt_vobs_video_attr);
+  printf("\n");
+  printf("Number of Audio streams: %i\n", 
+        vts_attributes->nr_of_vtstt_audio_streams);
+  for(i = 0; i < vts_attributes->nr_of_vtstt_audio_streams; i++) {
+    printf("\tstream %i attributes: ", i);
+    ifoPrint_audio_attributes(5, &vts_attributes->vtstt_audio_attr[i]);
+    printf("\n");
+  }
+  
+  printf("Number of Subpicture streams: %i\n", 
+        vts_attributes->nr_of_vtstt_subp_streams);
+  for(i = 0; i < vts_attributes->nr_of_vtstt_subp_streams; i++) {
+    printf("\tstream %2i attributes: ", i);    
+    ifoPrint_subp_attributes(5, &vts_attributes->vtstt_subp_attr[i]);
+    printf("\n");
+  }
+}
+
+
+void ifoPrint_VTS_ATRT(vts_atrt_t *vts_atrt) {
+  int i;
+  
+  printf("Number of Video Title Sets: %3i\n", vts_atrt->nr_of_vtss);
+  for(i = 0; i < vts_atrt->nr_of_vtss; i++) {
+    printf("\nVideo Title Set %i\n", i + 1);
+    ifoPrint_VTS_ATTRIBUTES(&vts_atrt->vts[i]);
+  }
+}
+
+
+void ifoPrint(dvd_reader_t *dvd, int title) {
+  ifo_handle_t *ifohandle;
+
+  ifohandle = ifoOpen(dvd, title);
+  if(!ifohandle) {
+    fprintf(stderr, "Can't open info file for title %d\n", title);
+    return;
+  }
+  
+  
+  if(ifohandle->vmgi_mat) {
+
+    printf("VMG top level\n-------------\n");
+    ifoPrint_VMGI_MAT(ifohandle->vmgi_mat);
+
+    printf("\nFirst Play PGC\n--------------\n");
+    ifoPrint_PGC(ifohandle->first_play_pgc);
+
+    printf("\nTitle Track search pointer table\n");
+    printf(  "------------------------------------------------\n");
+    ifoPrint_TT_SRPT(ifohandle->tt_srpt);
+
+    printf("\nMenu PGCI Unit table\n");
+    printf(  "--------------------\n");
+    if(ifohandle->pgci_ut) {
+      ifoPrint_PGCI_UT(ifohandle->pgci_ut);
+    } else {
+      printf("No PGCI Unit table present\n");
+    }
+
+    printf("\nParental Manegment Information table\n");
+    printf(  "------------------------------------\n");
+    if(ifohandle->ptl_mait) {
+      ifoPrint_PTL_MAIT(ifohandle->ptl_mait);
+    } else {
+      printf("No Parental Management Information present\n");
+    }
+
+    printf("\nVideo Title Set Attribute Table\n");
+    printf(  "-------------------------------\n");
+    ifoPrint_VTS_ATRT(ifohandle->vts_atrt);
+    
+    printf("\nText Data Manager Information\n");
+    printf(  "-----------------------------\n");
+    if(ifohandle->txtdt_mgi) {
+      //ifoPrint_TXTDT_MGI(&(vmgi->txtdt_mgi));
+    } else {
+      printf("No Text Data Manager Information present\n");
+    }
+
+    printf("\nMenu Cell Adress table\n");
+    printf(  "-----------------\n");
+    if(ifohandle->menu_c_adt) {
+      ifoPrint_C_ADT(ifohandle->menu_c_adt);
+    } else {
+      printf("No Menu Cell Adress table present\n");
+    }
+
+    printf("\nVideo Manager Menu VOBU address map\n");
+    printf(  "-----------------\n");
+    if(ifohandle->menu_vobu_admap) {
+      ifoPrint_VOBU_ADMAP(ifohandle->menu_vobu_admap);
+    } else {
+      printf("No Menu VOBU address map present\n");   
+    }
+  }
+
+
+  if(ifohandle->vtsi_mat) {
+
+    printf("VTS top level\n-------------\n");
+    ifoPrint_VTSI_MAT(ifohandle->vtsi_mat);
+
+    printf("\nPart of Title Track search pointer table\n");
+    printf(  "----------------------------------------------\n");
+    ifoPrint_VTS_PTT_SRPT(ifohandle->vts_ptt_srpt);
+
+    printf("\nPGCI Unit table\n");
+    printf(  "--------------------\n");
+    ifoPrint_PGCIT(ifohandle->vts_pgcit);
+
+    printf("\nMenu PGCI Unit table\n");
+    printf(  "--------------------\n");
+    if(ifohandle->pgci_ut) {
+      ifoPrint_PGCI_UT(ifohandle->pgci_ut);
+    } else {
+      printf("No Menu PGCI Unit table present\n");
+    }
+
+    printf("\nMenu Cell Adress table\n");
+    printf(  "-----------------\n");
+    if(ifohandle->menu_c_adt) {
+      ifoPrint_C_ADT(ifohandle->menu_c_adt);
+    } else {
+      printf("No Cell Adress table present\n");
+    }
+
+    printf("\nVideo Title Set Menu VOBU address map\n");
+    printf(  "-----------------\n");
+    if(ifohandle->menu_vobu_admap) {
+      ifoPrint_VOBU_ADMAP(ifohandle->menu_vobu_admap);
+    } else {
+      printf("No Menu VOBU address map present\n");
+    }
+
+    printf("\nCell Adress table\n");
+    printf(  "-----------------\n");
+    ifoPrint_C_ADT(ifohandle->vts_c_adt);
+
+    printf("\nVideo Title Set VOBU address map\n");
+    printf(  "-----------------\n");
+    ifoPrint_VOBU_ADMAP(ifohandle->vts_vobu_admap);
+  } 
+
+  ifoClose(ifohandle);
+}
+
diff --git a/extras/libdvdread/ifo_print.h b/extras/libdvdread/ifo_print.h
new file mode 100644 (file)
index 0000000..97470a8
--- /dev/null
@@ -0,0 +1,56 @@
+/**
+ * Copyright (C) 2000 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
+ */
+
+#ifndef IFO_PRINT_H_INCLUDED
+#define IFO_PRINT_H_INCLUDED
+
+#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.
+ */
+void ifoPrint(dvd_reader_t *dvd, int title);
+
+void ifoPrint_VMGI_MAT(vmgi_mat_t *vmgi_mat);
+void ifoPrint_VTSI_MAT(vtsi_mat_t *vtsi_mat);
+
+void ifoPrint_PTL_MAIT(ptl_mait_t *ptl_mait);
+void ifoPrint_VTS_ATRT(vts_atrt_t *vts_atrt);
+void ifoPrint_TT_SRPT(tt_srpt_t *vmg_ptt_srpt);
+void ifoPrint_VTS_PTT_SRPT(vts_ptt_srpt_t *vts_ptt_srpt);
+void ifoPrint_PGC(pgc_t *pgc);
+void ifoPrint_PGCIT(pgcit_t *pgcit);
+void ifoPrint_PGCI_UT(pgci_ut_t *pgci_ut);
+void ifoPrint_C_ADT(c_adt_t *c_adt);
+void ifoPrint_VOBU_ADMAP(vobu_admap_t *vobu_admap);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* IFO_PRINT_H_INCLUDED */
diff --git a/extras/libdvdread/ifo_read.c b/extras/libdvdread/ifo_read.c
new file mode 100644 (file)
index 0000000..63cc1a3
--- /dev/null
@@ -0,0 +1,1777 @@
+/*
+ * Copyright (C) 2000 Björn Englund <d4bjorn@dtek.chalmers.se>, 
+ *                    Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <string.h>
+#include <assert.h>
+
+#include "dvd_reader.h"
+
+#include "config.h" // Needed for WORDS_BIGENDIAN
+#include "bswap.h"
+#include "ifo_types.h"
+#include "ifo_read.h"
+
+#ifndef DVD_BLOCK_LEN
+#define DVD_BLOCK_LEN 2048
+#endif
+
+#ifndef NDEBUG
+#define CHECK_ZERO(arg) \
+  if(memcmp(my_friendly_zeros, &arg, sizeof(arg))) { \
+    unsigned int i_CZ; \
+    fprintf(stderr, "*** Zero check failed in %s:%i\n    for %s = 0x", \
+            __FILE__, __LINE__, # arg ); \
+    for(i_CZ = 0; i_CZ < sizeof(arg); i_CZ++) \
+      fprintf(stderr, "%02x", *((uint8_t *)&arg + i_CZ)); \
+    fprintf(stderr, "\n"); \
+  }
+static const uint8_t my_friendly_zeros[2048];
+#else
+#define CHECK_ZERO(arg) (void)(arg)
+#endif
+
+
+/* Prototypes for internal functions */
+static int ifoRead_VMG(ifo_handle_t *ifofile);
+static int ifoRead_VTS(ifo_handle_t *ifofile);
+static int ifoRead_PGC(ifo_handle_t *ifofile, pgc_t *pgc, unsigned int offset);
+static int ifoRead_PGC_COMMAND_TBL(ifo_handle_t *ifofile, 
+                                   pgc_command_tbl_t *cmd_tbl, 
+                                  unsigned int offset);
+static int ifoRead_PGC_PROGRAM_MAP(ifo_handle_t *ifofile, 
+                                   pgc_program_map_t *program_map, 
+                                   unsigned int nr, unsigned int offset);
+static int ifoRead_CELL_PLAYBACK_TBL(ifo_handle_t *ifofile, 
+                                     cell_playback_t *cell_playback, 
+                                     unsigned int nr, unsigned int offset);
+static int ifoRead_CELL_POSITION_TBL(ifo_handle_t *ifofile, 
+                                     cell_position_t *cell_position, 
+                                     unsigned int nr, unsigned int offset);
+static int ifoRead_VTS_ATTRIBUTES(ifo_handle_t *ifofile, 
+                                  vts_attributes_t *vts_attributes, 
+                                  unsigned int offset);
+static int ifoRead_C_ADT_internal(ifo_handle_t *ifofile, c_adt_t *c_adt, 
+                                  unsigned int sector);
+static int ifoRead_VOBU_ADMAP_internal(ifo_handle_t *ifofile, 
+                                       vobu_admap_t *vobu_admap, 
+                                      unsigned int sector);
+static int ifoRead_PGCIT_internal(ifo_handle_t *ifofile, pgcit_t *pgcit, 
+                                  unsigned int offset);
+
+static void ifoFree_PGC(pgc_t *pgc);
+static void ifoFree_PGC_COMMAND_TBL(pgc_command_tbl_t *cmd_tbl);
+static void ifoFree_PGCIT_internal(pgcit_t *pgcit);
+
+
+static int DVDFileSeek_( dvd_file_t *dvd_file, uint32_t offset ) {
+  return (DVDFileSeek(dvd_file, (int)offset) == (int)offset);
+}
+
+
+ifo_handle_t *ifoOpen(dvd_reader_t *dvd, int title) {
+  ifo_handle_t *ifofile;
+
+  ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
+  if(!ifofile)
+    return 0;
+
+  memset(ifofile, 0, sizeof(ifo_handle_t));
+
+  ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_FILE);
+  if(!ifofile->file) {
+    if(title) {
+      fprintf(stderr, "libdvdread: Can't open file VTS_%02d_0.IFO.\n", title);
+    } else {
+      fprintf(stderr, "libdvdread: Can't open file VIDEO_TS.IFO.\n");
+    }
+    free(ifofile);
+    return 0;
+  }
+
+  /* First check if this is a VMGI file. */
+  if(ifoRead_VMG(ifofile)) {
+
+    /* These are both mandatory. */
+    if(!ifoRead_FP_PGC(ifofile) || !ifoRead_TT_SRPT(ifofile)) {
+      fprintf(stderr, "libdvdread: Invalid main menu IFO (VIDEO_TS.IFO).\n");
+      ifoClose(ifofile);
+      return 0;
+    }
+
+    ifoRead_PGCI_UT(ifofile);
+    ifoRead_PTL_MAIT(ifofile);
+
+    /* This is also mandatory. */
+    if(!ifoRead_VTS_ATRT(ifofile)) {
+      fprintf(stderr, "libdvdread: Invalid main menu IFO (VIDEO_TS.IFO).\n");
+      ifoClose(ifofile);
+      return 0;
+    }
+
+    ifoRead_TXTDT_MGI(ifofile);
+    ifoRead_C_ADT(ifofile);
+    ifoRead_VOBU_ADMAP(ifofile);
+
+    return ifofile;
+  }
+
+  if(ifoRead_VTS(ifofile)) {
+
+    if(!ifoRead_VTS_PTT_SRPT(ifofile) || !ifoRead_PGCIT(ifofile)) {
+      fprintf(stderr, "libdvdread: Invalid title IFO (VTS_%02d_0.IFO).\n",
+              title);
+      ifoClose(ifofile);
+      return 0;
+    }
+
+
+    ifoRead_PGCI_UT(ifofile);
+    ifoRead_C_ADT(ifofile);
+    ifoRead_VOBU_ADMAP(ifofile);
+
+    if(!ifoRead_TITLE_C_ADT(ifofile) || !ifoRead_TITLE_VOBU_ADMAP(ifofile)) {
+      fprintf(stderr, "libdvdread: Invalid title IFO (VTS_%02d_0.IFO).\n",
+              title);
+      ifoClose(ifofile);
+      return 0;
+    }
+
+    return ifofile;
+  }
+
+  fprintf(stderr, "libdvdread: Invalid IFO for title %d (VTS_%02d_0.IFO).\n",
+          title, title);
+  ifoClose(ifofile);
+  return 0;
+}
+
+
+ifo_handle_t *ifoOpenVMGI(dvd_reader_t *dvd) {
+  ifo_handle_t *ifofile;
+
+  ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
+  if(!ifofile)
+    return 0;
+
+  memset(ifofile, 0, sizeof(ifo_handle_t));
+
+  ifofile->file = DVDOpenFile(dvd, 0, DVD_READ_INFO_FILE);
+  if(!ifofile->file) {
+    fprintf(stderr, "libdvdread: Can't open file VIDEO_TS.IFO.\n");
+    free(ifofile);
+    return 0;
+  }
+
+  if(ifoRead_VMG(ifofile))
+    return ifofile;
+
+  fprintf(stderr, "libdvdread: Invalid main menu IFO (VIDEO_TS.IFO).\n");
+  ifoClose(ifofile);
+  return 0;
+}
+
+
+ifo_handle_t *ifoOpenVTSI(dvd_reader_t *dvd, int title) {
+  ifo_handle_t *ifofile;
+  
+  ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
+  if(!ifofile)
+    return 0;
+
+  memset(ifofile, 0, sizeof(ifo_handle_t));
+
+  ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_FILE);
+  if(!ifofile->file) {
+    fprintf(stderr, "libdvdread: Can't open file VTS_%02d_0.IFO.\n", title);
+    free(ifofile);
+    return 0;
+  }
+
+  ifoRead_VTS(ifofile);
+  if(ifofile->vtsi_mat)
+    return ifofile;
+
+  fprintf(stderr, "libdvdread: Invalid IFO for title %d (VTS_%02d_0.IFO).\n",
+          title, title);
+  ifoClose(ifofile);
+  return 0;
+}
+
+
+void ifoClose(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  ifoFree_VOBU_ADMAP(ifofile);
+  ifoFree_TITLE_VOBU_ADMAP(ifofile);
+  ifoFree_C_ADT(ifofile);
+  ifoFree_TITLE_C_ADT(ifofile);
+  ifoFree_TXTDT_MGI(ifofile);
+  ifoFree_VTS_ATRT(ifofile);
+  ifoFree_PTL_MAIT(ifofile);
+  ifoFree_PGCI_UT(ifofile);
+  ifoFree_TT_SRPT(ifofile);
+  ifoFree_FP_PGC(ifofile);
+  ifoFree_PGCIT(ifofile);
+  ifoFree_VTS_PTT_SRPT(ifofile);
+
+  if(ifofile->vmgi_mat)
+    free(ifofile->vmgi_mat);
+
+  if(ifofile->vtsi_mat)
+    free(ifofile->vtsi_mat);
+
+  DVDCloseFile(ifofile->file);
+  ifofile->file = 0;
+  free(ifofile);
+  ifofile = 0;
+}
+
+
+static int ifoRead_VMG(ifo_handle_t *ifofile) {
+  vmgi_mat_t *vmgi_mat;
+
+  vmgi_mat = (vmgi_mat_t *)malloc(sizeof(vmgi_mat_t));
+  if(!vmgi_mat)
+    return 0;
+
+  ifofile->vmgi_mat = vmgi_mat;
+
+  if(!DVDFileSeek_(ifofile->file, 0)) {
+    free(ifofile->vmgi_mat);
+    ifofile->vmgi_mat = 0;
+    return 0;
+  }
+
+  if(!DVDReadBytes(ifofile->file, vmgi_mat, sizeof(vmgi_mat_t))) {
+    free(ifofile->vmgi_mat);
+    ifofile->vmgi_mat = 0;
+    return 0;
+  }
+
+  if(strncmp("DVDVIDEO-VMG", vmgi_mat->vmg_identifier, 12) != 0) {
+    free(ifofile->vmgi_mat);
+    ifofile->vmgi_mat = 0;
+    return 0;
+  }
+  
+  B2N_32(vmgi_mat->vmg_last_sector);
+  B2N_32(vmgi_mat->vmgi_last_sector);
+  B2N_32(vmgi_mat->vmg_category);
+  B2N_16(vmgi_mat->vmg_nr_of_volumes);
+  B2N_16(vmgi_mat->vmg_this_volume_nr);
+  B2N_16(vmgi_mat->vmg_nr_of_title_sets);
+  B2N_64(vmgi_mat->vmg_pos_code);
+  B2N_32(vmgi_mat->vmgi_last_byte);
+  B2N_32(vmgi_mat->first_play_pgc);
+  B2N_32(vmgi_mat->vmgm_vobs);
+  B2N_32(vmgi_mat->tt_srpt);
+  B2N_32(vmgi_mat->vmgm_pgci_ut);
+  B2N_32(vmgi_mat->ptl_mait);
+  B2N_32(vmgi_mat->vts_atrt);
+  B2N_32(vmgi_mat->txtdt_mgi);
+  B2N_32(vmgi_mat->vmgm_c_adt);
+  B2N_32(vmgi_mat->vmgm_vobu_admap);
+  B2N_16(vmgi_mat->vmgm_audio_attr.lang_code);
+  B2N_16(vmgi_mat->vmgm_subp_attr.lang_code);
+
+
+  CHECK_ZERO(vmgi_mat->zero_1);
+  CHECK_ZERO(vmgi_mat->zero_2);
+  CHECK_ZERO(vmgi_mat->zero_3);
+  CHECK_ZERO(vmgi_mat->zero_4);
+  CHECK_ZERO(vmgi_mat->zero_5);
+  CHECK_ZERO(vmgi_mat->zero_6);
+  CHECK_ZERO(vmgi_mat->zero_7);
+  CHECK_ZERO(vmgi_mat->zero_8);
+  CHECK_ZERO(vmgi_mat->zero_9);
+  CHECK_ZERO(vmgi_mat->zero_10);  
+  assert(vmgi_mat->vmg_last_sector != 0);
+  assert(vmgi_mat->vmgi_last_sector != 0);
+  assert(vmgi_mat->vmgi_last_sector * 2 <= vmgi_mat->vmg_last_sector);
+  assert(vmgi_mat->vmg_nr_of_volumes != 0);
+  assert(vmgi_mat->vmg_this_volume_nr != 0);
+  assert(vmgi_mat->vmg_this_volume_nr <= vmgi_mat->vmg_nr_of_volumes);
+  assert(vmgi_mat->disc_side == 1 || vmgi_mat->disc_side == 2);
+  assert(vmgi_mat->vmg_nr_of_title_sets != 0);
+  assert(vmgi_mat->vmgi_last_byte >= 341);
+  assert(vmgi_mat->vmgi_last_byte / DVD_BLOCK_LEN <= 
+         vmgi_mat->vmgi_last_sector);
+  assert(vmgi_mat->first_play_pgc != 0 && 
+         vmgi_mat->first_play_pgc < vmgi_mat->vmgi_last_byte);
+  assert(vmgi_mat->vmgm_vobs == 0 || 
+        (vmgi_mat->vmgm_vobs > vmgi_mat->vmgi_last_sector &&
+         vmgi_mat->vmgm_vobs < vmgi_mat->vmg_last_sector));
+  assert(vmgi_mat->tt_srpt <= vmgi_mat->vmgi_last_sector);
+  assert(vmgi_mat->vmgm_pgci_ut <= vmgi_mat->vmgi_last_sector);
+  assert(vmgi_mat->ptl_mait <= vmgi_mat->vmgi_last_sector);
+  assert(vmgi_mat->vts_atrt <= vmgi_mat->vmgi_last_sector);
+  assert(vmgi_mat->txtdt_mgi <= vmgi_mat->vmgi_last_sector);
+  assert(vmgi_mat->vmgm_c_adt <= vmgi_mat->vmgi_last_sector);
+  assert(vmgi_mat->vmgm_vobu_admap <= vmgi_mat->vmgi_last_sector);
+
+  assert(vmgi_mat->nr_of_vmgm_audio_streams <= 1);
+  assert(vmgi_mat->nr_of_vmgm_subp_streams <= 1);
+
+  return 1;
+}
+
+
+static int ifoRead_VTS(ifo_handle_t *ifofile) {
+  vtsi_mat_t *vtsi_mat;
+  int i;
+
+  vtsi_mat = (vtsi_mat_t *)malloc(sizeof(vtsi_mat_t));
+  if(!vtsi_mat)
+    return 0;
+  
+  ifofile->vtsi_mat = vtsi_mat;
+
+  if(!DVDFileSeek_(ifofile->file, 0)) {
+    free(ifofile->vtsi_mat);
+    ifofile->vtsi_mat = 0;
+    return 0;
+  }
+
+  if(!(DVDReadBytes(ifofile->file, vtsi_mat, sizeof(vtsi_mat_t)))) {
+    free(ifofile->vtsi_mat);
+    ifofile->vtsi_mat = 0;
+    return 0;
+  }
+
+  if(strncmp("DVDVIDEO-VTS", vtsi_mat->vts_identifier, 12) != 0) {
+    free(ifofile->vtsi_mat);
+    ifofile->vtsi_mat = 0;
+    return 0;
+  }
+
+  B2N_32(vtsi_mat->vts_last_sector);
+  B2N_32(vtsi_mat->vtsi_last_sector);
+  B2N_32(vtsi_mat->vts_category);
+  B2N_32(vtsi_mat->vtsi_last_byte);
+  B2N_32(vtsi_mat->vtsm_vobs);
+  B2N_32(vtsi_mat->vtstt_vobs);
+  B2N_32(vtsi_mat->vts_ptt_srpt);
+  B2N_32(vtsi_mat->vts_pgcit);
+  B2N_32(vtsi_mat->vtsm_pgci_ut);
+  B2N_32(vtsi_mat->vts_tmapt);
+  B2N_32(vtsi_mat->vtsm_c_adt);
+  B2N_32(vtsi_mat->vtsm_vobu_admap);
+  B2N_32(vtsi_mat->vts_c_adt);
+  B2N_32(vtsi_mat->vts_vobu_admap);
+  B2N_16(vtsi_mat->vtsm_audio_attr.lang_code);
+  B2N_16(vtsi_mat->vtsm_subp_attr.lang_code);
+  for(i = 0; i < 8; i++)
+    B2N_16(vtsi_mat->vts_audio_attr[i].lang_code);
+  for(i = 0; i < 32; i++)
+    B2N_16(vtsi_mat->vts_subp_attr[i].lang_code);
+
+
+  CHECK_ZERO(vtsi_mat->zero_1);
+  CHECK_ZERO(vtsi_mat->zero_2);
+  CHECK_ZERO(vtsi_mat->zero_3);
+  CHECK_ZERO(vtsi_mat->zero_4);
+  CHECK_ZERO(vtsi_mat->zero_5);
+  CHECK_ZERO(vtsi_mat->zero_6);
+  CHECK_ZERO(vtsi_mat->zero_7);
+  CHECK_ZERO(vtsi_mat->zero_8);
+  CHECK_ZERO(vtsi_mat->zero_9);
+  CHECK_ZERO(vtsi_mat->zero_10);
+  CHECK_ZERO(vtsi_mat->zero_11);
+  CHECK_ZERO(vtsi_mat->zero_12);
+  CHECK_ZERO(vtsi_mat->zero_13);
+  CHECK_ZERO(vtsi_mat->zero_14);
+  CHECK_ZERO(vtsi_mat->zero_15);
+  CHECK_ZERO(vtsi_mat->zero_16);
+  CHECK_ZERO(vtsi_mat->zero_17);
+  CHECK_ZERO(vtsi_mat->zero_18);
+  CHECK_ZERO(vtsi_mat->zero_19);
+  CHECK_ZERO(vtsi_mat->zero_20);
+  assert(vtsi_mat->vtsi_last_sector*2 <= vtsi_mat->vts_last_sector);
+  assert(vtsi_mat->vtsi_last_byte/DVD_BLOCK_LEN <= vtsi_mat->vtsi_last_sector);
+  assert(vtsi_mat->vtsm_vobs == 0 || 
+        (vtsi_mat->vtsm_vobs > vtsi_mat->vtsi_last_sector &&
+         vtsi_mat->vtsm_vobs < vtsi_mat->vts_last_sector));
+  assert(vtsi_mat->vtstt_vobs == 0 || 
+        (vtsi_mat->vtstt_vobs > vtsi_mat->vtsi_last_sector &&
+         vtsi_mat->vtstt_vobs < vtsi_mat->vts_last_sector));
+  assert(vtsi_mat->vts_ptt_srpt <= vtsi_mat->vtsi_last_sector);
+  assert(vtsi_mat->vts_pgcit <= vtsi_mat->vtsi_last_sector);
+  assert(vtsi_mat->vtsm_pgci_ut <= vtsi_mat->vtsi_last_sector);
+  assert(vtsi_mat->vts_tmapt <= vtsi_mat->vtsi_last_sector);
+  assert(vtsi_mat->vtsm_c_adt <= vtsi_mat->vtsi_last_sector);
+  assert(vtsi_mat->vtsm_vobu_admap <= vtsi_mat->vtsi_last_sector);
+  assert(vtsi_mat->vts_c_adt <= vtsi_mat->vtsi_last_sector);
+  assert(vtsi_mat->vts_vobu_admap <= vtsi_mat->vtsi_last_sector);
+  
+  assert(vtsi_mat->nr_of_vtsm_audio_streams <= 1);
+  assert(vtsi_mat->nr_of_vtsm_subp_streams <= 1);
+
+  assert(vtsi_mat->nr_of_vts_audio_streams <= 8);
+  for(i = vtsi_mat->nr_of_vts_audio_streams; i < 8; i++)
+    CHECK_ZERO(vtsi_mat->vts_audio_attr[i]);
+
+  assert(vtsi_mat->nr_of_vts_subp_streams <= 32);
+  for(i = vtsi_mat->nr_of_vts_subp_streams; i < 32; i++)
+    CHECK_ZERO(vtsi_mat->vts_subp_attr[i]);      
+
+  return 1;
+}
+
+
+static int ifoRead_PGC_COMMAND_TBL(ifo_handle_t *ifofile, 
+                                   pgc_command_tbl_t *cmd_tbl, 
+                                  unsigned int offset) {
+  
+  memset(cmd_tbl, 0, sizeof(pgc_command_tbl_t));
+  
+  if(!DVDFileSeek_(ifofile->file, offset))
+    return 0;
+
+  if(!(DVDReadBytes(ifofile->file, cmd_tbl, PGC_COMMAND_TBL_SIZE)))
+    return 0;
+
+  B2N_16(cmd_tbl->nr_of_pre);
+  B2N_16(cmd_tbl->nr_of_post);
+  B2N_16(cmd_tbl->nr_of_cell);
+
+  assert(cmd_tbl->nr_of_pre + cmd_tbl->nr_of_post + cmd_tbl->nr_of_cell<= 128);
+
+  if(cmd_tbl->nr_of_pre != 0) {
+    unsigned int pre_cmds_size  = cmd_tbl->nr_of_pre * COMMAND_DATA_SIZE;
+    cmd_tbl->pre_cmds = (vm_cmd_t *)malloc(pre_cmds_size);
+    if(!cmd_tbl->pre_cmds)
+      return 0;
+
+    if(!(DVDReadBytes(ifofile->file, cmd_tbl->pre_cmds, pre_cmds_size))) {
+      free(cmd_tbl->pre_cmds);
+      return 0;
+    }
+  }
+  
+  if(cmd_tbl->nr_of_post != 0) {
+    unsigned int post_cmds_size = cmd_tbl->nr_of_post * COMMAND_DATA_SIZE;
+    cmd_tbl->post_cmds = (vm_cmd_t *)malloc(post_cmds_size);
+    if(!cmd_tbl->post_cmds) {
+      if(cmd_tbl->pre_cmds) 
+       free(cmd_tbl->pre_cmds);
+      return 0;
+    }
+    if(!(DVDReadBytes(ifofile->file, cmd_tbl->post_cmds, post_cmds_size))) {
+      if(cmd_tbl->pre_cmds) 
+       free(cmd_tbl->pre_cmds);
+      free(cmd_tbl->post_cmds);
+      return 0;
+    }
+  }
+
+  if(cmd_tbl->nr_of_cell != 0) {
+    unsigned int cell_cmds_size = cmd_tbl->nr_of_cell * COMMAND_DATA_SIZE;
+    cmd_tbl->cell_cmds = (vm_cmd_t *)malloc(cell_cmds_size);
+    if(!cmd_tbl->cell_cmds) {
+      if(cmd_tbl->pre_cmds)
+       free(cmd_tbl->pre_cmds);
+      if(cmd_tbl->post_cmds)
+       free(cmd_tbl->post_cmds);
+      return 0;
+    }
+    if(!(DVDReadBytes(ifofile->file, cmd_tbl->cell_cmds, cell_cmds_size))) {
+      if(cmd_tbl->pre_cmds) 
+       free(cmd_tbl->pre_cmds);
+      if(cmd_tbl->post_cmds) 
+       free(cmd_tbl->post_cmds);
+      free(cmd_tbl->cell_cmds);
+      return 0;
+    }
+  }
+  
+  /* 
+   * Make a run over all the commands and see that we can interpret them all?
+   */
+  return 1;
+}
+
+
+static void ifoFree_PGC_COMMAND_TBL(pgc_command_tbl_t *cmd_tbl) {
+  if(cmd_tbl) {
+    if(cmd_tbl->nr_of_pre && cmd_tbl->pre_cmds)
+      free(cmd_tbl->pre_cmds);
+    if(cmd_tbl->nr_of_post && cmd_tbl->post_cmds)
+      free(cmd_tbl->post_cmds);
+    if(cmd_tbl->nr_of_cell && cmd_tbl->cell_cmds)
+      free(cmd_tbl->cell_cmds);
+    free(cmd_tbl);
+  }
+}
+
+static int ifoRead_PGC_PROGRAM_MAP(ifo_handle_t *ifofile, 
+                                   pgc_program_map_t *program_map, 
+                                  unsigned int nr, unsigned int offset) {
+  unsigned int size = nr * sizeof(pgc_program_map_t);
+
+  if(!DVDFileSeek_(ifofile->file, offset))
+    return 0;
+  if(!(DVDReadBytes(ifofile->file, program_map, size)))
+    return 0;
+
+  return 1;
+}
+
+static int ifoRead_CELL_PLAYBACK_TBL(ifo_handle_t *ifofile, 
+                                     cell_playback_t *cell_playback,
+                                     unsigned int nr, unsigned int offset) {
+  unsigned int i;
+  unsigned int size = nr * sizeof(cell_playback_t);
+
+  if(!DVDFileSeek_(ifofile->file, offset))
+    return 0;
+
+  if(!(DVDReadBytes(ifofile->file, cell_playback, size)))
+    return 0;
+
+  for(i = 0; i < nr; i++) {
+    B2N_32(cell_playback[i].first_sector);
+    B2N_32(cell_playback[i].first_ilvu_end_sector);
+    B2N_32(cell_playback[i].last_vobu_start_sector);
+    B2N_32(cell_playback[i].last_sector);
+    
+    /* Changed < to <= because this was false in the movie 'Pi'. */
+    assert(cell_playback[i].last_vobu_start_sector <= 
+           cell_playback[i].last_sector);
+    assert(cell_playback[i].first_sector <= 
+           cell_playback[i].last_vobu_start_sector);
+  }
+
+  return 1;
+}
+
+
+static int ifoRead_CELL_POSITION_TBL(ifo_handle_t *ifofile, 
+                                     cell_position_t *cell_position, 
+                                     unsigned int nr, unsigned int offset) {
+  unsigned int i;
+  unsigned int size = nr * sizeof(cell_position_t);
+
+  if(!DVDFileSeek_(ifofile->file, offset))
+    return 0;
+
+  if(!(DVDReadBytes(ifofile->file, cell_position, size)))
+    return 0;
+
+  for(i = 0; i < nr; i++) {
+    B2N_16(cell_position[i].vob_id_nr);
+    CHECK_ZERO(cell_position[i].zero_1);
+  }
+
+  return 1;
+}
+
+static int ifoRead_PGC(ifo_handle_t *ifofile, pgc_t *pgc, unsigned int offset) {
+  unsigned int i;
+
+  if(!DVDFileSeek_(ifofile->file, offset))
+    return 0;
+  if(!(DVDReadBytes(ifofile->file, pgc, PGC_SIZE)))
+    return 0;
+
+  B2N_16(pgc->next_pgc_nr);
+  B2N_16(pgc->prev_pgc_nr);
+  B2N_16(pgc->goup_pgc_nr);
+  B2N_16(pgc->command_tbl_offset);
+  B2N_16(pgc->program_map_offset);
+  B2N_16(pgc->cell_playback_offset);
+  B2N_16(pgc->cell_position_offset);
+
+  for(i = 0; i < 8; i++)
+    B2N_16(pgc->audio_control[i]);
+  for(i = 0; i < 32; i++)
+    B2N_32(pgc->subp_control[i]);
+  for(i = 0; i < 16; i++)
+    B2N_32(pgc->palette[i]);
+  
+  CHECK_ZERO(pgc->zero_1);
+  assert(pgc->nr_of_programs <= pgc->nr_of_cells);
+
+  /* verify time (look at print_time) */
+  for(i = 0; i < 8; i++)
+    if(!pgc->audio_control[i] & 0x8000) /* The 'is present' bit */
+      CHECK_ZERO(pgc->audio_control[i]);
+  for(i = 0; i < 32; i++)
+    if(!pgc->subp_control[i] & 0x80000000) /* The 'is present' bit */
+      CHECK_ZERO(pgc->subp_control[i]);
+  
+  /* Check that time is 0:0:0:0 also if nr_of_programs == 0 */
+  if(pgc->nr_of_programs == 0) {
+    CHECK_ZERO(pgc->still_time);
+    CHECK_ZERO(pgc->pg_playback_mode); // ??
+    assert(pgc->program_map_offset == 0);
+    assert(pgc->cell_playback_offset == 0);
+    assert(pgc->cell_position_offset == 0);
+  } else {
+    assert(pgc->program_map_offset != 0);
+    assert(pgc->cell_playback_offset != 0);
+    assert(pgc->cell_position_offset != 0);
+  }
+  
+  if(pgc->command_tbl_offset != 0) {
+    pgc->command_tbl = malloc(sizeof(pgc_command_tbl_t));
+    if(!pgc->command_tbl)
+      return 0;
+
+    if(!ifoRead_PGC_COMMAND_TBL(ifofile, pgc->command_tbl, 
+                                offset + pgc->command_tbl_offset)) {
+      free(pgc->command_tbl);
+      return 0;
+    }
+  } else {
+    pgc->command_tbl = NULL;
+  }
+  
+  if(pgc->program_map_offset != 0) {
+    pgc->program_map = malloc(pgc->nr_of_programs * sizeof(pgc_program_map_t));
+    if(!pgc->program_map) {
+      ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+      return 0;
+    }
+    if(!ifoRead_PGC_PROGRAM_MAP(ifofile, pgc->program_map,pgc->nr_of_programs,
+                                offset + pgc->program_map_offset)) {
+      ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+      free(pgc->program_map);
+      return 0;
+    }
+  } else {
+    pgc->program_map = NULL;
+  }
+  
+  if(pgc->cell_playback_offset != 0) {
+    pgc->cell_playback = malloc(pgc->nr_of_cells * sizeof(cell_playback_t));
+    if(!pgc->cell_playback) {
+      ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+      if(pgc->program_map)
+       free(pgc->program_map);
+      return 0;
+    }
+    if(!ifoRead_CELL_PLAYBACK_TBL(ifofile, pgc->cell_playback, 
+                                 pgc->nr_of_cells,
+                                  offset + pgc->cell_playback_offset)) {
+      ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+      if(pgc->program_map)
+       free(pgc->program_map);
+      free(pgc->cell_playback);
+      return 0;
+    }
+  } else {
+    pgc->cell_playback = NULL;
+  }
+  
+  if(pgc->cell_position_offset != 0) {
+    pgc->cell_position = malloc(pgc->nr_of_cells * sizeof(cell_position_t));
+    if(!pgc->cell_position) {
+      ifoFree_PGC(pgc);
+      return 0;
+    }
+    if(!ifoRead_CELL_POSITION_TBL(ifofile, pgc->cell_position, 
+                                 pgc->nr_of_cells,
+                                  offset + pgc->cell_position_offset)) {
+      ifoFree_PGC(pgc);
+      return 0;
+    }
+  } else {
+    pgc->cell_position = NULL;
+  }
+
+  return 1;
+}
+
+int ifoRead_FP_PGC(ifo_handle_t *ifofile) {
+
+  if(!ifofile)
+    return 0;
+
+  if(!ifofile->vmgi_mat)
+    return 0;
+  if(ifofile->vmgi_mat->first_play_pgc == 0) /* mandatory */
+    return 0;
+  
+  ifofile->first_play_pgc = (pgc_t *)malloc(sizeof(pgc_t));
+  if(!ifofile->first_play_pgc)
+    return 0;
+  
+  if(!ifoRead_PGC(ifofile, ifofile->first_play_pgc, 
+                  ifofile->vmgi_mat->first_play_pgc)) {
+    free(ifofile->first_play_pgc);
+    ifofile->first_play_pgc = 0;
+    return 0;
+  }
+
+  return 1;
+}
+
+static void ifoFree_PGC(pgc_t *pgc) {
+  if(pgc) {
+    ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+    if(pgc->program_map)
+      free(pgc->program_map);
+    if(pgc->cell_playback)
+      free(pgc->cell_playback);
+    if(pgc->cell_position)
+      free(pgc->cell_position);
+  }
+}
+
+void ifoFree_FP_PGC(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  if(ifofile->first_play_pgc) {
+    ifoFree_PGC(ifofile->first_play_pgc);
+    free(ifofile->first_play_pgc);
+    ifofile->first_play_pgc = 0;
+  }
+}
+
+
+int ifoRead_TT_SRPT(ifo_handle_t *ifofile) {
+  tt_srpt_t *tt_srpt;
+  int i, info_length;
+
+  if(!ifofile)
+    return 0;
+
+  if(!ifofile->vmgi_mat)
+    return 0;
+
+  if(ifofile->vmgi_mat->tt_srpt == 0) /* mandatory */
+    return 0;
+
+  if(!DVDFileSeek_(ifofile->file, ifofile->vmgi_mat->tt_srpt * DVD_BLOCK_LEN))
+    return 0;
+
+  tt_srpt = (tt_srpt_t *)malloc(sizeof(tt_srpt_t));
+  if(!tt_srpt)
+    return 0;
+
+  ifofile->tt_srpt = tt_srpt;
+  
+  if(!(DVDReadBytes(ifofile->file, tt_srpt, TT_SRPT_SIZE))) {
+    fprintf(stderr, "libdvdread: Unable to read read TT_SRPT.\n");
+    free(tt_srpt);
+    return 0;
+  }
+
+  B2N_16(tt_srpt->nr_of_srpts);
+  B2N_32(tt_srpt->last_byte);
+  
+  info_length = tt_srpt->last_byte + 1 - TT_SRPT_SIZE;
+
+  tt_srpt->title = (title_info_t *)malloc(info_length); 
+  if(!tt_srpt->title) {
+    free(tt_srpt);
+    ifofile->tt_srpt = 0;
+    return 0;
+  }
+  if(!(DVDReadBytes(ifofile->file, tt_srpt->title, info_length))) {
+    fprintf(stderr, "libdvdread: Unable to read read TT_SRPT.\n");
+    ifoFree_TT_SRPT(ifofile);
+    return 0;
+  }
+
+  for(i =  0; i < tt_srpt->nr_of_srpts; i++) {
+    B2N_16(tt_srpt->title[i].nr_of_ptts);
+    B2N_16(tt_srpt->title[i].parental_id);
+    B2N_32(tt_srpt->title[i].title_set_sector);
+  }
+  
+
+  CHECK_ZERO(tt_srpt->zero_1);
+  assert(tt_srpt->nr_of_srpts != 0);
+  assert(tt_srpt->nr_of_srpts < 100); // ??
+  assert((int)tt_srpt->nr_of_srpts * sizeof(title_info_t) <= info_length);
+  
+  for(i = 0; i < tt_srpt->nr_of_srpts; i++) {
+    assert(tt_srpt->title[i].pb_ty.zero_1 == 0);
+    assert(tt_srpt->title[i].nr_of_angles != 0);
+    assert(tt_srpt->title[i].nr_of_angles < 10);
+    //assert(tt_srpt->title[i].nr_of_ptts != 0);
+    // XXX: this assertion breaks Ghostbusters:
+    assert(tt_srpt->title[i].nr_of_ptts < 1000); // ??
+    assert(tt_srpt->title[i].title_set_nr != 0);
+    assert(tt_srpt->title[i].title_set_nr < 100); // ??
+    assert(tt_srpt->title[i].vts_ttn != 0);
+    assert(tt_srpt->title[i].vts_ttn < 100); // ??
+    //assert(tt_srpt->title[i].title_set_sector != 0);
+  }
+  
+  // Make this a function
+#if 0
+  if(memcmp((uint8_t *)tt_srpt->title + 
+            tt_srpt->nr_of_srpts * sizeof(title_info_t), 
+            my_friendly_zeros, 
+            info_length - tt_srpt->nr_of_srpts * sizeof(title_info_t))) {
+    fprintf(stderr, "VMG_PTT_SRPT slack is != 0, ");
+    hexdump((uint8_t *)tt_srpt->title + 
+            tt_srpt->nr_of_srpts * sizeof(title_info_t), 
+            info_length - tt_srpt->nr_of_srpts * sizeof(title_info_t));
+  }
+#endif
+
+  return 1;
+}
+
+
+void ifoFree_TT_SRPT(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  if(ifofile->tt_srpt) {
+    free(ifofile->tt_srpt->title);
+    free(ifofile->tt_srpt);
+    ifofile->tt_srpt = 0;
+  }
+}
+
+
+int ifoRead_VTS_PTT_SRPT(ifo_handle_t *ifofile) {
+  vts_ptt_srpt_t *vts_ptt_srpt;
+  int info_length, i, j;
+  uint32_t *data;
+
+  if(!ifofile)
+    return 0;
+  
+  if(!ifofile->vtsi_mat)
+    return 0;
+
+  if(ifofile->vtsi_mat->vts_ptt_srpt == 0) /* mandatory */
+    return 0;
+    
+  if(!DVDFileSeek_(ifofile->file,
+                  ifofile->vtsi_mat->vts_ptt_srpt * DVD_BLOCK_LEN))
+    return 0;
+
+  vts_ptt_srpt = (vts_ptt_srpt_t *)malloc(sizeof(vts_ptt_srpt_t));
+  if(!vts_ptt_srpt)
+    return 0;
+
+  ifofile->vts_ptt_srpt = vts_ptt_srpt;
+
+  if(!(DVDReadBytes(ifofile->file, vts_ptt_srpt, VTS_PTT_SRPT_SIZE))) {
+    fprintf(stderr, "libdvdread: Unable to read PTT search table.\n");
+    free(vts_ptt_srpt);
+    return 0;
+  }
+
+  B2N_16(vts_ptt_srpt->nr_of_srpts);
+  B2N_32(vts_ptt_srpt->last_byte);
+
+  CHECK_ZERO(vts_ptt_srpt->zero_1);
+  assert(vts_ptt_srpt->nr_of_srpts != 0);
+  assert(vts_ptt_srpt->nr_of_srpts < 100); // ??
+  
+  info_length = vts_ptt_srpt->last_byte + 1 - VTS_PTT_SRPT_SIZE;
+  
+  data = (uint32_t *)malloc(info_length); 
+  if(!data) {
+    free(vts_ptt_srpt);
+    ifofile->vts_ptt_srpt = 0;
+    return 0;
+  }
+  if(!(DVDReadBytes(ifofile->file, data, info_length))) {
+    fprintf(stderr, "libdvdread: Unable to read PTT search table.\n");
+    free(vts_ptt_srpt);
+    free(data);
+    ifofile->vts_ptt_srpt = 0;
+    return 0;
+  }
+
+  for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+    B2N_32(data[i]);
+    /* assert(data[i] + sizeof(ptt_info_t) <= vts_ptt_srpt->last_byte + 1);
+       Magic Knight Rayearth Daybreak is mastered very strange and has 
+       Titles with 0 PTTs. They all have a data[i] offsets beyond the end of
+       of the vts_ptt_srpt structure. */
+    assert(data[i] + sizeof(ptt_info_t) <= vts_ptt_srpt->last_byte + 1 + 4);
+  }
+  
+  vts_ptt_srpt->title = malloc(vts_ptt_srpt->nr_of_srpts * sizeof(ttu_t));
+  if(!vts_ptt_srpt->title) {
+    free(vts_ptt_srpt);
+    free(data);
+    ifofile->vts_ptt_srpt = 0;
+    return 0;
+  }
+  for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+    int n;
+    if(i < vts_ptt_srpt->nr_of_srpts - 1)
+      n = (data[i+1] - data[i]);
+    else
+      n = (vts_ptt_srpt->last_byte + 1 - data[i]);
+    /* assert(n > 0 && (n % 4) == 0);
+       Magic Knight Rayearth Daybreak is mastered very strange and has 
+       Titles with 0 PTTs. */
+    if(n < 0) n = 0;
+    assert(n % 4 == 0);
+    
+    vts_ptt_srpt->title[i].nr_of_ptts = n / 4;
+    vts_ptt_srpt->title[i].ptt = malloc(n * sizeof(ptt_info_t));
+    if(!vts_ptt_srpt->title[i].ptt) {
+      for(n = 0; n < i; n++)
+        free(vts_ptt_srpt->title[n].ptt);
+      free(vts_ptt_srpt);
+      free(data);
+      ifofile->vts_ptt_srpt = 0;
+      return 0;
+    }
+    for(j = 0; j < vts_ptt_srpt->title[i].nr_of_ptts; j++) {
+      /* The assert placed here because of Magic Knight Rayearth Daybreak */
+      assert(data[i] + sizeof(ptt_info_t) <= vts_ptt_srpt->last_byte + 1);
+      vts_ptt_srpt->title[i].ptt[j].pgcn 
+        = *(uint16_t*)(((char *)data) + data[i] + 4*j - VTS_PTT_SRPT_SIZE);
+      vts_ptt_srpt->title[i].ptt[j].pgn 
+        = *(uint16_t*)(((char *)data) + data[i] + 4*j + 2 - VTS_PTT_SRPT_SIZE);
+    }
+  }
+  free(data);
+  
+  for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+    for(j = 0; j < vts_ptt_srpt->title[i].nr_of_ptts; j++) {
+      B2N_16(vts_ptt_srpt->title[i].ptt[j].pgcn);
+      B2N_16(vts_ptt_srpt->title[i].ptt[j].pgn);
+    }
+  }
+  
+  for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+    assert(vts_ptt_srpt->title[i].nr_of_ptts < 1000); // ??
+    for(j = 0; j < vts_ptt_srpt->title[i].nr_of_ptts; j++) {
+      assert(vts_ptt_srpt->title[i].ptt[j].pgcn != 0 );
+      assert(vts_ptt_srpt->title[i].ptt[j].pgcn < 1000); // ??
+      assert(vts_ptt_srpt->title[i].ptt[j].pgn != 0);
+      assert(vts_ptt_srpt->title[i].ptt[j].pgn < 100); // ??
+    }
+  }
+
+  return 1;
+}
+
+
+void ifoFree_VTS_PTT_SRPT(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  if(ifofile->vts_ptt_srpt) {
+    int i;
+    for(i = 0; i < ifofile->vts_ptt_srpt->nr_of_srpts; i++)
+      free(ifofile->vts_ptt_srpt->title[i].ptt);
+    free(ifofile->vts_ptt_srpt->title);
+    free(ifofile->vts_ptt_srpt);
+    ifofile->vts_ptt_srpt = 0;
+  }
+}
+
+
+int ifoRead_PTL_MAIT(ifo_handle_t *ifofile) {
+  ptl_mait_t *ptl_mait;
+  int info_length;
+  unsigned int i;
+
+  if(!ifofile)
+    return 0;
+  
+  if(!ifofile->vmgi_mat)
+    return 0;
+  
+  if(ifofile->vmgi_mat->ptl_mait == 0)
+    return 1;
+
+  if(!DVDFileSeek_(ifofile->file, 
+                 ifofile->vmgi_mat->ptl_mait * DVD_BLOCK_LEN))
+    return 0;
+
+  ptl_mait = (ptl_mait_t *)malloc(sizeof(ptl_mait_t));
+  if(!ptl_mait)
+    return 0;
+
+  ifofile->ptl_mait = ptl_mait;
+
+  if(!(DVDReadBytes(ifofile->file, ptl_mait, PTL_MAIT_SIZE))) {
+    free(ptl_mait);
+    ifofile->ptl_mait = 0;
+    return 0;
+  }
+
+  B2N_16(ptl_mait->nr_of_countries);
+  B2N_16(ptl_mait->nr_of_vtss);
+  B2N_32(ptl_mait->last_byte);
+  
+  info_length = ptl_mait->last_byte + 1 - PTL_MAIT_SIZE;
+  
+  assert(ptl_mait->nr_of_countries != 0);
+  assert(ptl_mait->nr_of_countries < 100); // ??
+  assert(ptl_mait->nr_of_vtss != 0);
+  assert(ptl_mait->nr_of_vtss < 100); // ??  
+  assert(ptl_mait->nr_of_countries * PTL_MAIT_COUNTRY_SIZE <= info_length);
+  
+  /* Change this to read and 'translate' the tables too. 
+     I.e don't read so much here */
+  ptl_mait->countries = (ptl_mait_country_t *)malloc(info_length);
+  if(!ptl_mait->countries) {
+    free(ptl_mait);
+    ifofile->ptl_mait = 0;
+    return 0;
+  }
+  if(!(DVDReadBytes(ifofile->file, ptl_mait->countries, info_length))) {
+    fprintf(stderr, "libdvdread: Unable to read PTL_MAIT.\n");
+    ifoFree_PTL_MAIT(ifofile);
+    return 0;
+  }
+
+  for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+    B2N_16(ptl_mait->countries[i].country_code);
+    B2N_16(ptl_mait->countries[i].pf_ptl_mai_start_byte);
+  }
+  
+  for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+    CHECK_ZERO(ptl_mait->countries[i].zero_1);
+    CHECK_ZERO(ptl_mait->countries[i].zero_2);    
+    assert(ptl_mait->countries[i].pf_ptl_mai_start_byte + 
+           8 * (ptl_mait->nr_of_vtss + 1) * 2 <= ptl_mait->last_byte + 1);
+  }
+
+  return 1;
+}
+
+
+void ifoFree_PTL_MAIT(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  if(ifofile->ptl_mait) {
+    free(ifofile->ptl_mait->countries);
+    free(ifofile->ptl_mait);
+    ifofile->ptl_mait = 0;
+  }
+}
+
+int ifoRead_TITLE_C_ADT(ifo_handle_t *ifofile) {
+
+  if(!ifofile)
+    return 0;
+
+  if(!ifofile->vtsi_mat)
+    return 0;
+
+  if(ifofile->vtsi_mat->vts_c_adt == 0) /* mandatory */
+    return 0;
+
+  ifofile->vts_c_adt = (c_adt_t *)malloc(sizeof(c_adt_t));
+  if(!ifofile->vts_c_adt)
+    return 0;
+
+  if(!ifoRead_C_ADT_internal(ifofile, ifofile->vts_c_adt, 
+                             ifofile->vtsi_mat->vts_c_adt)) {
+    free(ifofile->vts_c_adt);
+    ifofile->vts_c_adt = 0;
+    return 0;
+  }
+
+  return 1;
+}
+
+int ifoRead_C_ADT(ifo_handle_t *ifofile) {
+  unsigned int sector;
+
+  if(!ifofile)
+    return 0;
+  
+  if(ifofile->vmgi_mat) {
+    if(ifofile->vmgi_mat->vmgm_c_adt == 0)
+      return 1;
+    sector = ifofile->vmgi_mat->vmgm_c_adt;
+  } else if(ifofile->vtsi_mat) {
+    if(ifofile->vtsi_mat->vtsm_c_adt == 0)
+      return 1;
+    sector = ifofile->vtsi_mat->vtsm_c_adt;
+  } else {
+    return 0;
+  }
+  
+  ifofile->menu_c_adt = (c_adt_t *)malloc(sizeof(c_adt_t));
+  if(!ifofile->menu_c_adt)
+    return 0;
+
+  if(!ifoRead_C_ADT_internal(ifofile, ifofile->menu_c_adt, sector)) {
+    free(ifofile->menu_c_adt);
+    ifofile->menu_c_adt = 0;
+    return 0;
+  }
+
+  return 1;
+}
+
+static int ifoRead_C_ADT_internal(ifo_handle_t *ifofile, 
+                                  c_adt_t *c_adt, unsigned int sector) {
+  int i, info_length;
+
+  if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN))
+    return 0;
+
+  if(!(DVDReadBytes(ifofile->file, c_adt, C_ADT_SIZE)))
+    return 0;
+
+  B2N_16(c_adt->nr_of_vobs);
+  B2N_32(c_adt->last_byte);
+  
+  info_length = c_adt->last_byte + 1 - C_ADT_SIZE;
+  
+  CHECK_ZERO(c_adt->zero_1);
+  /* assert(c_adt->nr_of_vobs > 0);  
+     Magic Knight Rayearth Daybreak is mastered very strange and has 
+     Titles with a VOBS that has no cells. */
+  assert(info_length % sizeof(cell_adr_t) == 0);
+  assert(info_length / sizeof(cell_adr_t) >= c_adt->nr_of_vobs);
+  
+  c_adt->cell_adr_table = (cell_adr_t *)malloc(info_length); 
+  if(!c_adt->cell_adr_table)
+    return 0;
+
+  if(info_length && 
+     !(DVDReadBytes(ifofile->file, c_adt->cell_adr_table, info_length))) {
+    free(c_adt->cell_adr_table);
+    return 0;
+  }
+
+  for(i = 0; i < info_length/sizeof(cell_adr_t); i++) {
+    B2N_16(c_adt->cell_adr_table[i].vob_id);
+    B2N_32(c_adt->cell_adr_table[i].start_sector);
+    B2N_32(c_adt->cell_adr_table[i].last_sector);
+
+    CHECK_ZERO(c_adt->cell_adr_table[i].zero_1);
+    assert(c_adt->cell_adr_table[i].vob_id > 0);
+    assert(c_adt->cell_adr_table[i].vob_id <= c_adt->nr_of_vobs);
+    assert(c_adt->cell_adr_table[i].cell_id > 0);
+    assert(c_adt->cell_adr_table[i].start_sector < 
+           c_adt->cell_adr_table[i].last_sector);
+  }
+
+  return 1;
+}
+
+
+static void ifoFree_C_ADT_internal(c_adt_t *c_adt) {
+  if(c_adt) {
+    free(c_adt->cell_adr_table);
+    free(c_adt);
+  }
+}
+
+void ifoFree_C_ADT(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  ifoFree_C_ADT_internal(ifofile->menu_c_adt);
+  ifofile->menu_c_adt = 0;
+}
+
+void ifoFree_TITLE_C_ADT(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  ifoFree_C_ADT_internal(ifofile->vts_c_adt);
+  ifofile->vts_c_adt = 0;
+}
+
+int ifoRead_TITLE_VOBU_ADMAP(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return 0;
+
+  if(!ifofile->vtsi_mat)
+    return 0;
+  
+  if(ifofile->vtsi_mat->vts_vobu_admap == 0) /* mandatory */
+    return 0;
+  
+  ifofile->vts_vobu_admap = (vobu_admap_t *)malloc(sizeof(vobu_admap_t));
+  if(!ifofile->vts_vobu_admap)
+    return 0;
+
+  if(!ifoRead_VOBU_ADMAP_internal(ifofile, ifofile->vts_vobu_admap,
+                                  ifofile->vtsi_mat->vts_vobu_admap)) {
+    free(ifofile->vts_vobu_admap);
+    ifofile->vts_vobu_admap = 0;
+    return 0;
+  }
+
+  return 1;
+}
+
+int ifoRead_VOBU_ADMAP(ifo_handle_t *ifofile) {
+  unsigned int sector;
+
+  if(!ifofile)
+    return 0;
+     
+  if(ifofile->vmgi_mat) {
+    if(ifofile->vmgi_mat->vmgm_vobu_admap == 0)
+      return 1;
+    sector = ifofile->vmgi_mat->vmgm_vobu_admap;
+  } else if(ifofile->vtsi_mat) {
+    if(ifofile->vtsi_mat->vtsm_vobu_admap == 0)
+      return 1;
+    sector = ifofile->vtsi_mat->vtsm_vobu_admap;
+  } else {
+    return 0;
+  }
+  
+  ifofile->menu_vobu_admap = (vobu_admap_t *)malloc(sizeof(vobu_admap_t));
+  if(!ifofile->menu_vobu_admap)
+    return 0;
+  
+  if(!ifoRead_VOBU_ADMAP_internal(ifofile, ifofile->menu_vobu_admap, sector)) {
+    free(ifofile->menu_vobu_admap);
+    ifofile->menu_vobu_admap = 0;
+    return 0;
+  }
+
+  return 1;
+}
+
+static int ifoRead_VOBU_ADMAP_internal(ifo_handle_t *ifofile, 
+                                       vobu_admap_t *vobu_admap, 
+                                      unsigned int sector) {
+  unsigned int i;
+  int info_length;
+
+  if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN))
+    return 0;
+
+  if(!(DVDReadBytes(ifofile->file, vobu_admap, VOBU_ADMAP_SIZE)))
+    return 0;
+
+  B2N_32(vobu_admap->last_byte);
+  
+  info_length = vobu_admap->last_byte + 1 - VOBU_ADMAP_SIZE;
+  /* assert(info_length > 0);
+     Magic Knight Rayearth Daybreak is mastered very strange and has 
+     Titles with a VOBS that has no VOBUs. */
+  assert(info_length % sizeof(uint32_t) == 0);
+  
+  vobu_admap->vobu_start_sectors = (uint32_t *)malloc(info_length); 
+  if(!vobu_admap->vobu_start_sectors) {
+    return 0;
+  }
+  if(info_length && 
+     !(DVDReadBytes(ifofile->file, 
+                   vobu_admap->vobu_start_sectors, info_length))) {
+    free(vobu_admap->vobu_start_sectors);
+    return 0;
+  }
+
+  for(i = 0; i < info_length/sizeof(uint32_t); i++)
+    B2N_32(vobu_admap->vobu_start_sectors[i]);
+
+  return 1;
+}
+
+
+static void ifoFree_VOBU_ADMAP_internal(vobu_admap_t *vobu_admap) {
+  if(vobu_admap) {
+    free(vobu_admap->vobu_start_sectors);
+    free(vobu_admap);
+  }
+}
+
+void ifoFree_VOBU_ADMAP(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  ifoFree_VOBU_ADMAP_internal(ifofile->menu_vobu_admap);
+  ifofile->menu_vobu_admap = 0;
+}
+
+void ifoFree_TITLE_VOBU_ADMAP(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  ifoFree_VOBU_ADMAP_internal(ifofile->vts_vobu_admap);
+  ifofile->vts_vobu_admap = 0;
+}
+
+int ifoRead_PGCIT(ifo_handle_t *ifofile) {
+
+  if(!ifofile)
+    return 0;
+  
+  if(!ifofile->vtsi_mat)
+    return 0;
+  
+  if(ifofile->vtsi_mat->vts_pgcit == 0) /* mandatory */
+    return 0;
+  
+  ifofile->vts_pgcit = (pgcit_t *)malloc(sizeof(pgcit_t));
+  if(!ifofile->vts_pgcit)
+    return 0;
+
+  if(!ifoRead_PGCIT_internal(ifofile, ifofile->vts_pgcit, 
+                             ifofile->vtsi_mat->vts_pgcit * DVD_BLOCK_LEN)) {
+    free(ifofile->vts_pgcit);
+    ifofile->vts_pgcit = 0;
+    return 0;
+  }
+
+  return 1;
+}
+
+static int ifoRead_PGCIT_internal(ifo_handle_t *ifofile, pgcit_t *pgcit, 
+                                  unsigned int offset) {
+  int i, info_length;
+  uint8_t *data, *ptr;
+  
+  if(!DVDFileSeek_(ifofile->file, offset))
+    return 0;
+
+  if(!(DVDReadBytes(ifofile->file, pgcit, PGCIT_SIZE)))
+    return 0;
+
+  B2N_16(pgcit->nr_of_pgci_srp);
+  B2N_32(pgcit->last_byte);
+  
+  CHECK_ZERO(pgcit->zero_1);
+  /* assert(pgcit->nr_of_pgci_srp != 0);
+     Magic Knight Rayearth Daybreak is mastered very strange and has 
+     Titles with 0 PTTs. */
+  assert(pgcit->nr_of_pgci_srp < 10000); // ?? seen max of 1338
+  
+  info_length = pgcit->nr_of_pgci_srp * PGCI_SRP_SIZE;
+  data = malloc(info_length);
+  if(!data)
+    return 0;
+
+  if(info_length && !(DVDReadBytes(ifofile->file, data, info_length))) {
+    free(data);
+    return 0;
+  }
+
+  pgcit->pgci_srp = malloc(pgcit->nr_of_pgci_srp * sizeof(pgci_srp_t));
+  if(!pgcit->pgci_srp) {
+    free(data);
+    return 0;
+  }
+  ptr = data;
+  for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+    memcpy(&pgcit->pgci_srp[i], ptr, PGCI_LU_SIZE);
+    ptr += PGCI_LU_SIZE;
+    B2N_16(pgcit->pgci_srp[i].ptl_id_mask);
+    B2N_32(pgcit->pgci_srp[i].pgc_start_byte);
+    assert(pgcit->pgci_srp[i].unknown1 == 0);
+  }
+  free(data);
+  
+  for(i = 0; i < pgcit->nr_of_pgci_srp; i++)
+    assert(pgcit->pgci_srp[i].pgc_start_byte + PGC_SIZE <= pgcit->last_byte+1);
+  
+  for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+    pgcit->pgci_srp[i].pgc = malloc(sizeof(pgc_t));
+    if(!pgcit->pgci_srp[i].pgc) {
+      int j;
+      for(j = 0; j < i; j++) {
+        ifoFree_PGC(pgcit->pgci_srp[j].pgc);
+        free(pgcit->pgci_srp[j].pgc);
+      }
+      return 0;
+    }
+    if(!ifoRead_PGC(ifofile, pgcit->pgci_srp[i].pgc, 
+                    offset + pgcit->pgci_srp[i].pgc_start_byte)) {
+      int j;
+      for(j = 0; j < i; j++) {
+        ifoFree_PGC(pgcit->pgci_srp[j].pgc);
+        free(pgcit->pgci_srp[j].pgc);
+      }
+      free(pgcit->pgci_srp);
+      return 0;
+    }
+  }
+
+  return 1;
+}
+
+static void ifoFree_PGCIT_internal(pgcit_t *pgcit) {
+  if(pgcit) {
+    int i;
+    for(i = 0; i < pgcit->nr_of_pgci_srp; i++)
+      ifoFree_PGC(pgcit->pgci_srp[i].pgc);
+    free(pgcit->pgci_srp);
+  }
+}
+
+void ifoFree_PGCIT(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  if(ifofile->vts_pgcit) {
+    ifoFree_PGCIT_internal(ifofile->vts_pgcit);
+    free(ifofile->vts_pgcit);
+    ifofile->vts_pgcit = 0;
+  }
+}
+
+
+int ifoRead_PGCI_UT(ifo_handle_t *ifofile) {
+  pgci_ut_t *pgci_ut;
+  unsigned int sector;
+  unsigned int i;  
+  int info_length;
+  uint8_t *data, *ptr;
+
+  if(!ifofile)
+    return 0;
+  
+  if(ifofile->vmgi_mat) {
+    if(ifofile->vmgi_mat->vmgm_pgci_ut == 0)
+      return 1;
+    sector = ifofile->vmgi_mat->vmgm_pgci_ut;
+  } else if(ifofile->vtsi_mat) {
+    if(ifofile->vtsi_mat->vtsm_pgci_ut == 0)
+      return 1;
+    sector = ifofile->vtsi_mat->vtsm_pgci_ut;
+  } else {
+    return 0;
+  }
+  
+  ifofile->pgci_ut = (pgci_ut_t *)malloc(sizeof(pgci_ut_t));
+  if(!ifofile->pgci_ut)
+    return 0;
+  
+  if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN)) {
+    free(ifofile->pgci_ut);
+    ifofile->pgci_ut = 0;
+    return 0;
+  }
+  
+  if(!(DVDReadBytes(ifofile->file, ifofile->pgci_ut, PGCI_UT_SIZE))) {
+    free(ifofile->pgci_ut);
+    ifofile->pgci_ut = 0;
+    return 0;
+  }
+  
+  pgci_ut = ifofile->pgci_ut;
+  
+  B2N_16(pgci_ut->nr_of_lus);
+  B2N_32(pgci_ut->last_byte);
+  
+  CHECK_ZERO(pgci_ut->zero_1);
+  assert(pgci_ut->nr_of_lus != 0);
+  assert(pgci_ut->nr_of_lus < 100); // ?? 3-4 ?
+  assert((uint32_t)pgci_ut->nr_of_lus * PGCI_LU_SIZE < pgci_ut->last_byte);
+
+  info_length = pgci_ut->nr_of_lus * PGCI_LU_SIZE;
+  data = malloc(info_length);
+  if(!data) {
+    free(pgci_ut);
+    ifofile->pgci_ut = 0;
+    return 0;
+  }
+  if(!(DVDReadBytes(ifofile->file, data, info_length))) {
+    free(data);
+    free(pgci_ut);
+    ifofile->pgci_ut = 0;
+    return 0;
+  }
+
+  pgci_ut->lu = malloc(pgci_ut->nr_of_lus * sizeof(pgci_lu_t));
+  if(!pgci_ut->lu) {
+    free(data);
+    free(pgci_ut);
+    ifofile->pgci_ut = 0;
+   return 0;
+  }
+  ptr = data;
+  for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+    memcpy(&pgci_ut->lu[i], ptr, PGCI_LU_SIZE);
+    ptr += PGCI_LU_SIZE;
+    B2N_16(pgci_ut->lu[i].lang_code); 
+    B2N_32(pgci_ut->lu[i].lang_start_byte); 
+  }
+  free(data);
+  
+  for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+    CHECK_ZERO(pgci_ut->lu[i].zero_1);
+    // Maybe this is only defined for v1.1 and later titles?
+    /* If the bits in 'lu[i].exists' are enumerated abcd efgh then:
+            VTS_x_yy.IFO        VIDEO_TS.IFO
+       a == 0x83 "Root"         0x82 "Title"
+       b == 0x84 "Subpicture"
+       c == 0x85 "Audio"
+       d == 0x86 "Angle"
+       e == 0x87 "PTT"
+    */
+    assert((pgci_ut->lu[i].exists & 0x07) == 0);
+  }
+
+  for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+    pgci_ut->lu[i].pgcit = malloc(sizeof(pgcit_t));
+    if(!pgci_ut->lu[i].pgcit) {
+      unsigned int j;
+      for(j = 0; j < i; j++) {
+        ifoFree_PGCIT_internal(pgci_ut->lu[j].pgcit);
+        free(pgci_ut->lu[j].pgcit);
+      }
+      free(pgci_ut->lu);
+      free(pgci_ut);
+      ifofile->pgci_ut = 0;
+      return 0;
+    }
+    if(!ifoRead_PGCIT_internal(ifofile, pgci_ut->lu[i].pgcit, 
+                               sector * DVD_BLOCK_LEN 
+                               + pgci_ut->lu[i].lang_start_byte)) {
+      unsigned int j;
+      for(j = 0; j < i; j++) {
+        ifoFree_PGCIT_internal(pgci_ut->lu[j].pgcit);
+        free(pgci_ut->lu[j].pgcit);
+      }
+      free(pgci_ut->lu[i].pgcit);
+      free(pgci_ut->lu);
+      free(pgci_ut);
+      ifofile->pgci_ut = 0;
+      return 0;
+    }
+    // FIXME: Iterate and verify that all menus that should exists accordingly
+    //        to pgci_ut->lu[i].exists really do?
+  }
+
+  return 1;
+}
+
+
+void ifoFree_PGCI_UT(ifo_handle_t *ifofile) {
+  unsigned int i;
+
+  if(!ifofile)
+    return;
+  
+  if(ifofile->pgci_ut) {
+    for(i = 0; i < ifofile->pgci_ut->nr_of_lus; i++) {
+      ifoFree_PGCIT_internal(ifofile->pgci_ut->lu[i].pgcit);
+      free(ifofile->pgci_ut->lu[i].pgcit);
+    }
+    free(ifofile->pgci_ut->lu);
+    free(ifofile->pgci_ut);
+    ifofile->pgci_ut = 0;
+  }
+}
+
+static int ifoRead_VTS_ATTRIBUTES(ifo_handle_t *ifofile, 
+                                  vts_attributes_t *vts_attributes, 
+                                  unsigned int offset) {
+  unsigned int i;
+
+  if(!DVDFileSeek_(ifofile->file, offset))
+    return 0;
+
+  if(!(DVDReadBytes(ifofile->file, vts_attributes, sizeof(vts_attributes_t))))
+    return 0;
+
+  B2N_32(vts_attributes->last_byte);
+  B2N_32(vts_attributes->vts_cat);
+  B2N_16(vts_attributes->vtsm_audio_attr.lang_code);
+  B2N_16(vts_attributes->vtsm_subp_attr.lang_code);
+  for(i = 0; i < 8; i++)
+    B2N_16(vts_attributes->vtstt_audio_attr[i].lang_code);
+  for(i = 0; i < 32; i++)
+    B2N_16(vts_attributes->vtstt_subp_attr[i].lang_code);
+  
+  CHECK_ZERO(vts_attributes->zero_1);
+  CHECK_ZERO(vts_attributes->zero_2);
+  CHECK_ZERO(vts_attributes->zero_3);
+  CHECK_ZERO(vts_attributes->zero_4);
+  CHECK_ZERO(vts_attributes->zero_5);
+  CHECK_ZERO(vts_attributes->zero_6);
+  CHECK_ZERO(vts_attributes->zero_7);
+  assert(vts_attributes->nr_of_vtsm_audio_streams <= 1);
+  assert(vts_attributes->nr_of_vtsm_subp_streams <= 1);
+  assert(vts_attributes->nr_of_vtstt_audio_streams <= 8);
+  for(i = vts_attributes->nr_of_vtstt_audio_streams; i < 8; i++)
+    CHECK_ZERO(vts_attributes->vtstt_audio_attr[i]);
+  assert(vts_attributes->nr_of_vtstt_subp_streams <= 32);
+  {
+    int nr_coded;
+    assert(vts_attributes->last_byte + 1 - VTS_ATTRIBUTES_MIN_SIZE >= 0);  
+    nr_coded = (vts_attributes->last_byte + 1 - VTS_ATTRIBUTES_MIN_SIZE)/6;
+    // This is often nr_coded = 70, how do you know how many there really are?
+    if(nr_coded > 32) { // We haven't read more from disk/file anyway
+      nr_coded = 32;
+    }
+    assert(vts_attributes->nr_of_vtstt_subp_streams <= nr_coded);
+    for(i = vts_attributes->nr_of_vtstt_subp_streams; i < nr_coded; i++)
+      CHECK_ZERO(vts_attributes->vtstt_subp_attr[i]);
+  }
+
+  return 1;
+}
+
+
+
+int ifoRead_VTS_ATRT(ifo_handle_t *ifofile) {
+  vts_atrt_t *vts_atrt;
+  unsigned int i, info_length, sector;
+  uint32_t *data;
+
+  if(!ifofile)
+    return 0;
+  
+  if(!ifofile->vmgi_mat)
+    return 0;
+  
+  if(ifofile->vmgi_mat->vts_atrt == 0) /* mandatory */
+    return 0;
+  
+  sector = ifofile->vmgi_mat->vts_atrt;
+  if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN))
+    return 0;
+
+  vts_atrt = (vts_atrt_t *)malloc(sizeof(vts_atrt_t));
+  if(!vts_atrt)
+    return 0;
+
+  ifofile->vts_atrt = vts_atrt;
+  
+  if(!(DVDReadBytes(ifofile->file, vts_atrt, VTS_ATRT_SIZE))) {
+    free(vts_atrt);
+    ifofile->vts_atrt = 0;
+    return 0;
+  }
+
+  B2N_16(vts_atrt->nr_of_vtss);
+  B2N_32(vts_atrt->last_byte);
+
+  CHECK_ZERO(vts_atrt->zero_1);
+  assert(vts_atrt->nr_of_vtss != 0);
+  assert(vts_atrt->nr_of_vtss < 100); //??
+  assert((uint32_t)vts_atrt->nr_of_vtss * (4 + VTS_ATTRIBUTES_MIN_SIZE) + 
+         VTS_ATRT_SIZE < vts_atrt->last_byte + 1);
+
+  info_length = vts_atrt->nr_of_vtss * sizeof(uint32_t);
+  data = (uint32_t *)malloc(info_length);
+  if(!data) {
+    free(vts_atrt);
+    ifofile->vts_atrt = 0;
+    return 0;
+  }
+  if(!(DVDReadBytes(ifofile->file, data, info_length))) {
+    free(data);
+    free(vts_atrt);
+    ifofile->vts_atrt = 0;
+    return 0;
+  }
+  
+  for(i = 0; i < vts_atrt->nr_of_vtss; i++) {
+    B2N_32(data[i]);
+    assert(data[i] + VTS_ATTRIBUTES_MIN_SIZE < vts_atrt->last_byte + 1);
+  }
+  
+  info_length = vts_atrt->nr_of_vtss * sizeof(vts_attributes_t);
+  vts_atrt->vts = (vts_attributes_t *)malloc(info_length);
+  if(!vts_atrt->vts) {
+    free(data);
+    free(vts_atrt);
+    ifofile->vts_atrt = 0;
+    return 0;
+  }
+  for(i = 0; i < vts_atrt->nr_of_vtss; i++) {
+    unsigned int offset = data[i];
+    if(!ifoRead_VTS_ATTRIBUTES(ifofile, &(vts_atrt->vts[i]),
+                               (sector * DVD_BLOCK_LEN) + offset)) {
+      free(data);
+      free(vts_atrt);
+      ifofile->vts_atrt = 0;
+      return 0;
+    }
+
+    // This assert cant be in ifoRead_VTS_ATTRIBUTES
+    assert(offset + vts_atrt->vts[i].last_byte <= vts_atrt->last_byte + 1);
+    // Is this check correct?
+  }
+  free(data);
+
+  return 1;
+}
+
+
+void ifoFree_VTS_ATRT(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  if(ifofile->vts_atrt) {
+    free(ifofile->vts_atrt->vts);
+    free(ifofile->vts_atrt);
+    ifofile->vts_atrt = 0;
+  }
+}
+
+
+int ifoRead_TXTDT_MGI(ifo_handle_t *ifofile) {
+  txtdt_mgi_t *txtdt_mgi;
+
+  if(!ifofile)
+    return 0;
+  
+  if(!ifofile->vmgi_mat)
+    return 0;
+  /* Return successfully if there is nothing to read. */ 
+  if(ifofile->vmgi_mat->txtdt_mgi == 0)
+    return 1;
+
+  if(!DVDFileSeek_(ifofile->file, 
+                  ifofile->vmgi_mat->txtdt_mgi * DVD_BLOCK_LEN))
+    return 0;
+  
+  txtdt_mgi = (txtdt_mgi_t *)malloc(sizeof(txtdt_mgi_t));
+  if(!txtdt_mgi) {
+    return 0;
+  }
+  ifofile->txtdt_mgi = txtdt_mgi;
+
+  if(!(DVDReadBytes(ifofile->file, txtdt_mgi, TXTDT_MGI_SIZE))) {
+    fprintf(stderr, "libdvdread: Unable to read TXTDT_MGI.\n");
+    free(txtdt_mgi);
+    ifofile->txtdt_mgi = 0;
+    return 0;
+  }
+
+  // fprintf(stderr, "-- Not done yet --\n");
+  return 1;
+}
+
+void ifoFree_TXTDT_MGI(ifo_handle_t *ifofile) {
+  if(!ifofile)
+    return;
+  
+  if(ifofile->txtdt_mgi) {
+    free(ifofile->txtdt_mgi);
+    ifofile->txtdt_mgi = 0;
+  }
+}
+
diff --git a/extras/libdvdread/ifo_read.h b/extras/libdvdread/ifo_read.h
new file mode 100644 (file)
index 0000000..b1c20e9
--- /dev/null
@@ -0,0 +1,186 @@
+/**
+ * Copyright (C) 2000 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
+ */
+
+#ifndef IFO_READ_H_INCLUDED
+#define IFO_READ_H_INCLUDED
+
+#include <dvdread/ifo_types.h>
+#include <dvdread/dvd_reader.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * 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 *dvd, int title);
+
+/**
+ * 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 *dvd);
+
+/**
+ * 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 *dvd, int title);
+
+/**
+ * Cleans up the IFO information.  This will free all data allocated for the
+ * substructures.
+ */
+void ifoClose(ifo_handle_t *ifofile);
+
+/**
+ * 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.
+ */
+
+/**
+ * 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 *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 *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 *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 *ifofile);
+
+/**
+ * Reads in the first play program chain data, filling the
+ * ifofile->first_play_pgc structure.  This data is only located in the video
+ * manager information file.  This structure is mandatory, and must be included
+ * in the VMGI file.
+ */
+int ifoRead_FP_PGC(ifo_handle_t *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 *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 *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 *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 *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 *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 *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 *ifofile);
+
+/**
+ * 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 *ifofile);
+void ifoFree_VTS_ATRT(ifo_handle_t *ifofile);
+void ifoFree_TT_SRPT(ifo_handle_t *ifofile);
+void ifoFree_VTS_PTT_SRPT(ifo_handle_t *ifofile);
+void ifoFree_FP_PGC(ifo_handle_t *ifofile);
+void ifoFree_PGCIT(ifo_handle_t *ifofile);
+void ifoFree_PGCI_UT(ifo_handle_t *ifofile);
+void ifoFree_C_ADT(ifo_handle_t *ifofile);
+void ifoFree_TITLE_C_ADT(ifo_handle_t *ifofile);
+void ifoFree_VOBU_ADMAP(ifo_handle_t *ifofile);
+void ifoFree_TITLE_VOBU_ADMAP(ifo_handle_t *ifofile);
+void ifoFree_TXTDT_MGI(ifo_handle_t *ifofile);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* IFO_READ_H_INCLUDED */
diff --git a/extras/libdvdread/ifo_types.h b/extras/libdvdread/ifo_types.h
new file mode 100644 (file)
index 0000000..fe87fbb
--- /dev/null
@@ -0,0 +1,747 @@
+/**
+ * Copyright (C) 2000 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
+ */
+
+#ifndef IFO_TYPES_H_INCLUDED
+#define IFO_TYPES_H_INCLUDED
+
+#include <inttypes.h>
+#include <dvdread/dvd_reader.h>
+
+
+#undef ATTRIBUTE_PACKED
+#undef PRAGMA_PACK_BEGIN 
+#undef PRAGMA_PACK_END
+
+#if defined(__GNUC__)
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
+#define ATTRIBUTE_PACKED __attribute__ ((packed))
+#define PRAGMA_PACK 0
+#endif
+#endif
+
+#if !defined(ATTRIBUTE_PACKED)
+#define ATTRIBUTE_PACKED
+#define PRAGMA_PACK 1
+#endif
+
+#if PRAGMA_PACK
+#pragma pack(1)
+#endif
+
+
+/**
+ * Common
+ *
+ * The following structures are used in both the VMGI and VTSI.
+ */
+
+
+/**
+ * DVD Time Information.
+ */
+typedef struct {
+  uint8_t hour;
+  uint8_t minute;
+  uint8_t second;
+  uint8_t frame_u; // The two high bits are the frame rate.
+} ATTRIBUTE_PACKED dvd_time_t;
+
+/**
+ * Type to store per-command data.
+ */
+typedef struct {
+  uint8_t bytes[8];
+} ATTRIBUTE_PACKED vm_cmd_t;
+#define COMMAND_DATA_SIZE 8
+
+
+/**
+ * Video Attributes.
+ */
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+  unsigned int mpeg_version         : 2;
+  unsigned int video_format         : 2;
+  unsigned int display_aspect_ratio : 2;
+  unsigned int permitted_df         : 2;
+  
+  unsigned int line21_cc_1          : 1;
+  unsigned int line21_cc_2          : 1;
+  unsigned int unknown1             : 2;
+  
+  unsigned int picture_size         : 2;
+  unsigned int letterboxed          : 1;
+  unsigned int film_mode            : 1;
+#else
+  unsigned int permitted_df         : 2;
+  unsigned int display_aspect_ratio : 2;
+  unsigned int video_format         : 2;
+  unsigned int mpeg_version         : 2;
+  
+  unsigned int film_mode            : 1;
+  unsigned int letterboxed          : 1;
+  unsigned int picture_size         : 2;
+  
+  unsigned int unknown1             : 2;
+  unsigned int line21_cc_2          : 1;
+  unsigned int line21_cc_1          : 1;
+#endif
+} ATTRIBUTE_PACKED video_attr_t;
+
+/**
+ * Audio Attributes. (Incomplete/Wrong?)
+ */
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+  unsigned int audio_format           : 3;
+  unsigned int multichannel_extension : 1;
+  unsigned int lang_type              : 2;
+  unsigned int application_mode       : 2;
+  
+  unsigned int quantization           : 2;
+  unsigned int sample_frequency       : 2;
+  unsigned int unknown1               : 1;
+  unsigned int channels               : 3;
+#else
+  unsigned int application_mode       : 2;
+  unsigned int lang_type              : 2;
+  unsigned int multichannel_extension : 1;
+  unsigned int audio_format           : 3;
+  
+  unsigned int channels               : 3;
+  unsigned int unknown1               : 1;
+  unsigned int sample_frequency       : 2;
+  unsigned int quantization           : 2;
+#endif
+  uint16_t lang_code;
+  uint8_t  lang_code2; // ??
+  uint8_t  lang_extension;
+  uint16_t unknown2;
+} ATTRIBUTE_PACKED audio_attr_t;
+
+/**
+ * Subpicture Attributes.(Incomplete/Wrong)
+ */
+typedef struct {
+  /*
+   * type: 0 not specified
+   *       1 language
+   *       2 other
+   * coding mode: 0 run length
+   *              1 extended
+   *              2 other
+   * language: indicates language if type == 1
+   * lang extension: if type == 1 contains the lang extension
+   */
+  uint8_t type;
+  uint8_t zero1;
+  uint16_t lang_code;
+  uint8_t lang_extension;
+  uint8_t zero2;
+} ATTRIBUTE_PACKED subp_attr_t;
+
+
+
+/**
+ * PGC Command Table.
+ */ 
+typedef struct {
+  uint16_t nr_of_pre;
+  uint16_t nr_of_post;
+  uint16_t nr_of_cell;
+  uint16_t zero_1;
+  vm_cmd_t *pre_cmds;
+  vm_cmd_t *post_cmds;
+  vm_cmd_t *cell_cmds;
+} ATTRIBUTE_PACKED pgc_command_tbl_t;
+#define PGC_COMMAND_TBL_SIZE 8
+
+/**
+ * PGC Program Map
+ */
+typedef uint8_t pgc_program_map_t; 
+
+/**
+ * Cell Playback Information.
+ */
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+  unsigned int block_mode       : 2;
+  unsigned int block_type       : 2;
+  unsigned int seamless_play    : 1;
+  unsigned int interleaved      : 1;
+  unsigned int stc_discontinuity: 1;
+  unsigned int seamless_angle   : 1;
+  
+  unsigned int unknown1         : 1;
+  unsigned int restricted       : 1;
+  unsigned int unknown2         : 6;
+#else
+  unsigned int seamless_angle   : 1;
+  unsigned int stc_discontinuity: 1;
+  unsigned int interleaved      : 1;
+  unsigned int seamless_play    : 1;
+  unsigned int block_type       : 2;
+  unsigned int block_mode       : 2;
+  
+  unsigned int unknown2         : 6;
+  unsigned int restricted       : 1;
+  unsigned int unknown1         : 1;
+#endif
+  uint8_t still_time;
+  uint8_t cell_cmd_nr;
+  dvd_time_t playback_time;
+  uint32_t first_sector;
+  uint32_t first_ilvu_end_sector;
+  uint32_t last_vobu_start_sector;
+  uint32_t last_sector;
+} ATTRIBUTE_PACKED cell_playback_t;
+
+#define BLOCK_TYPE_NONE         0x0
+#define BLOCK_TYPE_ANGLE_BLOCK  0x1
+
+#define BLOCK_MODE_NOT_IN_BLOCK 0x0
+#define BLOCK_MODE_FIRST_CELL   0x1
+#define BLOCK_MODE_IN_BLOCK     0x2
+#define BLOCK_MODE_LAST_CELL    0x3
+
+/**
+ * Cell Position Information.
+ */
+typedef struct {
+  uint16_t vob_id_nr;
+  uint8_t  zero_1;
+  uint8_t  cell_nr;
+} ATTRIBUTE_PACKED cell_position_t;
+
+/**
+ * User Operations.
+ */
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+  unsigned int zero                           : 7; // 25-31
+  unsigned int video_pres_mode_change         : 1; // 24
+  
+  unsigned int karaoke_audio_pres_mode_change : 1; // 23
+  unsigned int angle_change                   : 1; // 22
+  unsigned int subpic_stream_change           : 1; // 21
+  unsigned int audio_stream_change            : 1; // 20
+  unsigned int pause_on                       : 1; // 19
+  unsigned int still_off                      : 1; // 18
+  unsigned int button_select_or_activate      : 1; // 17
+  unsigned int resume                         : 1; // 16
+  
+  unsigned int chapter_menu_call              : 1; // 15
+  unsigned int angle_menu_call                : 1; // 14
+  unsigned int audio_menu_call                : 1; // 13
+  unsigned int subpic_menu_call               : 1; // 12
+  unsigned int root_menu_call                 : 1; // 11
+  unsigned int title_menu_call                : 1; // 10
+  unsigned int backward_scan                  : 1; // 9
+  unsigned int forward_scan                   : 1; // 8
+  
+  unsigned int next_pg_search                 : 1; // 7
+  unsigned int prev_or_top_pg_search          : 1; // 6
+  unsigned int time_or_chapter_search         : 1; // 5
+  unsigned int go_up                          : 1; // 4
+  unsigned int stop                           : 1; // 3
+  unsigned int title_play                     : 1; // 2
+  unsigned int chapter_search_or_play         : 1; // 1
+  unsigned int title_or_time_play             : 1; // 0
+#else
+  unsigned int video_pres_mode_change         : 1; // 24
+  unsigned int zero                           : 7; // 25-31
+  
+  unsigned int resume                         : 1; // 16
+  unsigned int button_select_or_activate      : 1; // 17
+  unsigned int still_off                      : 1; // 18
+  unsigned int pause_on                       : 1; // 19
+  unsigned int audio_stream_change            : 1; // 20
+  unsigned int subpic_stream_change           : 1; // 21
+  unsigned int angle_change                   : 1; // 22
+  unsigned int karaoke_audio_pres_mode_change : 1; // 23
+  
+  unsigned int forward_scan                   : 1; // 8
+  unsigned int backward_scan                  : 1; // 9
+  unsigned int title_menu_call                : 1; // 10
+  unsigned int root_menu_call                 : 1; // 11
+  unsigned int subpic_menu_call               : 1; // 12
+  unsigned int audio_menu_call                : 1; // 13
+  unsigned int angle_menu_call                : 1; // 14
+  unsigned int chapter_menu_call              : 1; // 15
+  
+  unsigned int title_or_time_play             : 1; // 0
+  unsigned int chapter_search_or_play         : 1; // 1
+  unsigned int title_play                     : 1; // 2
+  unsigned int stop                           : 1; // 3
+  unsigned int go_up                          : 1; // 4
+  unsigned int time_or_chapter_search         : 1; // 5
+  unsigned int prev_or_top_pg_search          : 1; // 6
+  unsigned int next_pg_search                 : 1; // 7
+#endif
+} ATTRIBUTE_PACKED user_ops_t;
+
+/**
+ * Program Chain Information.
+ */
+typedef struct {
+  uint16_t zero_1;
+  uint8_t  nr_of_programs;
+  uint8_t  nr_of_cells;
+  dvd_time_t playback_time;
+  user_ops_t prohibited_ops;
+  uint16_t audio_control[8]; /* New type? */
+  uint32_t subp_control[32]; /* New type? */
+  uint16_t next_pgc_nr;
+  uint16_t prev_pgc_nr;
+  uint16_t goup_pgc_nr;
+  uint8_t  still_time;
+  uint8_t  pg_playback_mode;
+  uint32_t palette[16]; /* New type struct {zero_1, Y, Cr, Cb} ? */
+  uint16_t command_tbl_offset;
+  uint16_t program_map_offset;
+  uint16_t cell_playback_offset;
+  uint16_t cell_position_offset;
+  pgc_command_tbl_t *command_tbl;
+  pgc_program_map_t  *program_map;
+  cell_playback_t *cell_playback;
+  cell_position_t *cell_position;
+} ATTRIBUTE_PACKED pgc_t;
+#define PGC_SIZE 236
+
+/**
+ * Program Chain Information Search Pointer.
+ */
+typedef struct {
+  uint8_t  entry_id;
+#ifdef WORDS_BIGENDIAN
+  unsigned int block_mode : 2;
+  unsigned int block_type : 2;
+  unsigned int unknown1   : 4;
+#else
+  unsigned int unknown1   : 4;
+  unsigned int block_type : 2;
+  unsigned int block_mode : 2;
+#endif  
+  uint16_t ptl_id_mask;
+  uint32_t pgc_start_byte;
+  pgc_t *pgc;
+} ATTRIBUTE_PACKED pgci_srp_t;
+#define PGCI_SRP_SIZE 8
+
+/**
+ * Program Chain Information Table.
+ */
+typedef struct {
+  uint16_t nr_of_pgci_srp;
+  uint16_t zero_1;
+  uint32_t last_byte;
+  pgci_srp_t *pgci_srp;
+} ATTRIBUTE_PACKED pgcit_t;
+#define PGCIT_SIZE 8
+
+/**
+ * Menu PGCI Language Unit.
+ */
+typedef struct {
+  uint16_t lang_code;
+  uint8_t  zero_1;
+  uint8_t  exists;
+  uint32_t lang_start_byte;
+  pgcit_t *pgcit;
+} ATTRIBUTE_PACKED pgci_lu_t;
+#define PGCI_LU_SIZE 8
+
+/**
+ * Menu PGCI Unit Table.
+ */
+typedef struct {
+  uint16_t nr_of_lus;
+  uint16_t zero_1;
+  uint32_t last_byte;
+  pgci_lu_t *lu;
+} ATTRIBUTE_PACKED pgci_ut_t;
+#define PGCI_UT_SIZE 8
+
+/**
+ * Cell Address Information.
+ */
+typedef struct {
+  uint16_t vob_id;
+  uint8_t  cell_id;
+  uint8_t  zero_1;
+  uint32_t start_sector;
+  uint32_t last_sector;
+} ATTRIBUTE_PACKED cell_adr_t;
+
+/**
+ * Cell Address Table.
+ */
+typedef struct {
+  uint16_t nr_of_vobs; /* VOBs */
+  uint16_t zero_1;
+  uint32_t last_byte;
+  cell_adr_t *cell_adr_table;
+} ATTRIBUTE_PACKED c_adt_t;
+#define C_ADT_SIZE 8
+
+/**
+ * VOBU Address Map.
+ */
+typedef struct {
+  uint32_t last_byte;
+  uint32_t *vobu_start_sectors;
+} ATTRIBUTE_PACKED vobu_admap_t;
+#define VOBU_ADMAP_SIZE 4
+
+
+
+
+/**
+ * VMGI
+ *
+ * The following structures relate to the Video Manager.
+ */
+
+/**
+ * Video Manager Information Management Table.
+ */
+typedef struct {
+  char     vmg_identifier[12];
+  uint32_t vmg_last_sector;
+  uint8_t  zero_1[12];
+  uint32_t vmgi_last_sector;
+  uint8_t  zero_2;
+  uint8_t  specification_version;
+  uint32_t vmg_category;
+  uint16_t vmg_nr_of_volumes;
+  uint16_t vmg_this_volume_nr;
+  uint8_t  disc_side;
+  uint8_t  zero_3[19];
+  uint16_t vmg_nr_of_title_sets;  /* Number of VTSs. */
+  char     provider_identifier[32];
+  uint64_t vmg_pos_code;
+  uint8_t  zero_4[24];
+  uint32_t vmgi_last_byte;
+  uint32_t first_play_pgc;
+  uint8_t  zero_5[56];
+  uint32_t vmgm_vobs;             /* sector */
+  uint32_t tt_srpt;               /* sector */
+  uint32_t vmgm_pgci_ut;          /* sector */
+  uint32_t ptl_mait;              /* sector */
+  uint32_t vts_atrt;              /* sector */
+  uint32_t txtdt_mgi;             /* sector */
+  uint32_t vmgm_c_adt;            /* sector */
+  uint32_t vmgm_vobu_admap;       /* sector */
+  uint8_t  zero_6[32];
+  
+  video_attr_t vmgm_video_attr;
+  uint8_t  zero_7;
+  uint8_t  nr_of_vmgm_audio_streams; // should be 0 or 1
+  audio_attr_t vmgm_audio_attr;
+  audio_attr_t zero_8[7];
+  uint8_t  zero_9[17];
+  uint8_t  nr_of_vmgm_subp_streams; // should be 0 or 1
+  subp_attr_t  vmgm_subp_attr;
+  subp_attr_t  zero_10[27];  /* XXX: how much 'padding' here? */
+} ATTRIBUTE_PACKED vmgi_mat_t;
+
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+  unsigned int zero_1                    : 1;
+  unsigned int multi_or_random_pgc_title : 1; // 0 == one sequential pgc title
+  unsigned int jlc_exists_in_cell_cmd    : 1;
+  unsigned int jlc_exists_in_prepost_cmd : 1;
+  unsigned int jlc_exists_in_button_cmd  : 1;
+  unsigned int jlc_exists_in_tt_dom      : 1;
+  unsigned int chapter_search_or_play    : 1; // UOP 1
+  unsigned int title_or_time_play        : 1; // UOP 0
+#else
+  unsigned int title_or_time_play        : 1; // UOP 0
+  unsigned int chapter_search_or_play    : 1; // UOP 1
+  unsigned int jlc_exists_in_tt_dom      : 1;
+  unsigned int jlc_exists_in_button_cmd  : 1;
+  unsigned int jlc_exists_in_prepost_cmd : 1;
+  unsigned int jlc_exists_in_cell_cmd    : 1;
+  unsigned int multi_or_random_pgc_title : 1; // 0 == one sequential pgc title
+  unsigned int zero_1                    : 1;
+#endif
+} ATTRIBUTE_PACKED playback_type_t;
+
+/**
+ * Title Information.
+ */
+typedef struct {
+  playback_type_t pb_ty;
+  uint8_t  nr_of_angles;
+  uint16_t nr_of_ptts;
+  uint16_t parental_id;
+  uint8_t  title_set_nr;
+  uint8_t  vts_ttn;
+  uint32_t title_set_sector;
+} ATTRIBUTE_PACKED title_info_t;
+
+/**
+ * PartOfTitle Search Pointer Table.
+ */
+typedef struct {
+  uint16_t nr_of_srpts;
+  uint16_t zero_1;
+  uint32_t last_byte;
+  title_info_t *title;
+} ATTRIBUTE_PACKED tt_srpt_t;
+#define TT_SRPT_SIZE 8
+
+/**
+ * Parental Management Information Unit Table.
+ */
+typedef struct {
+  uint16_t country_code;
+  uint16_t zero_1;
+  uint16_t pf_ptl_mai_start_byte;
+  uint16_t zero_2;
+  /* uint16_t *pf_ptl_mai // table of nr_of_vtss+1 x 8 */
+} ATTRIBUTE_PACKED ptl_mait_country_t;
+#define PTL_MAIT_COUNTRY_SIZE 8
+
+/**
+ * Parental Management Information Table.
+ */
+typedef struct {
+  uint16_t nr_of_countries;
+  uint16_t nr_of_vtss;
+  uint32_t last_byte;
+  ptl_mait_country_t *countries;
+} ATTRIBUTE_PACKED ptl_mait_t;
+#define PTL_MAIT_SIZE 8
+
+/**
+ * Video Title Set Attributes.
+ */
+typedef struct {
+  uint32_t last_byte;
+  uint32_t vts_cat;
+  
+  video_attr_t vtsm_vobs_attr;
+  uint8_t  zero_1;
+  uint8_t  nr_of_vtsm_audio_streams; // should be 0 or 1
+  audio_attr_t vtsm_audio_attr;
+  audio_attr_t zero_2[7];  
+  uint8_t  zero_3[16];
+  uint8_t  zero_4;
+  uint8_t  nr_of_vtsm_subp_streams; // should be 0 or 1
+  subp_attr_t vtsm_subp_attr;
+  subp_attr_t zero_5[27];
+  
+  uint8_t  zero_6[2];
+  
+  video_attr_t vtstt_vobs_video_attr;
+  uint8_t  zero_7;
+  uint8_t  nr_of_vtstt_audio_streams;
+  audio_attr_t vtstt_audio_attr[8];
+  uint8_t  zero_8[16];
+  uint8_t  zero_9;
+  uint8_t  nr_of_vtstt_subp_streams;
+  subp_attr_t vtstt_subp_attr[32];
+} ATTRIBUTE_PACKED vts_attributes_t;
+#define VTS_ATTRIBUTES_SIZE 542
+#define VTS_ATTRIBUTES_MIN_SIZE 356
+
+/**
+ * Video Title Set Attribute Table.
+ */
+typedef struct {
+  uint16_t nr_of_vtss;
+  uint16_t zero_1;
+  uint32_t last_byte;
+  vts_attributes_t *vts;
+} ATTRIBUTE_PACKED vts_atrt_t;
+#define VTS_ATRT_SIZE 8
+
+/**
+ * Text Data. (Incomplete)
+ */
+typedef struct {
+  uint32_t last_byte;    /* offsets are relative here */
+  uint16_t offsets[100]; /* == nr_of_srpts + 1 (first is disc title) */
+#if 0  
+  uint16_t unknown; // 0x48 ?? 0x48 words (16bit) info following
+  uint16_t zero_1;
+  
+  uint8_t type_of_info;//?? 01 == disc, 02 == Title, 04 == Title part 
+  uint8_t unknown1;
+  uint8_t unknown2;
+  uint8_t unknown3;
+  uint8_t unknown4;//?? allways 0x30 language?, text format?
+  uint8_t unknown5;
+  uint16_t offset; // from first 
+  
+  char text[12]; // ended by 0x09
+#endif
+} ATTRIBUTE_PACKED txtdt_t;
+
+/**
+ * Text Data Language Unit. (Incomplete)
+ */ 
+typedef struct {
+  uint16_t lang_code;
+  uint16_t unknown;      /* 0x0001, title 1? disc 1? side 1? */
+  uint32_t txtdt_start_byte;  /* prt, rel start of vmg_txtdt_mgi  */
+  txtdt_t  *txtdt;
+} ATTRIBUTE_PACKED txtdt_lu_t;
+#define TXTDT_LU_SIZE 8
+
+/**
+ * Text Data Manager Information. (Incomplete)
+ */
+typedef struct {
+  char disc_name[14];            /* how many bytes?? */
+  uint16_t nr_of_language_units; /* 32bit??          */
+  uint32_t last_byte;
+  txtdt_lu_t *lu;
+} ATTRIBUTE_PACKED txtdt_mgi_t;
+#define TXTDT_MGI_SIZE 20
+
+
+/**
+ * VTS
+ *
+ * Structures relating to the Video Title Set (VTS).
+ */
+
+/**
+ * Video Title Set Information Management Table.
+ */
+typedef struct {
+  char vts_identifier[12];
+  uint32_t vts_last_sector;
+  uint8_t  zero_1[12];
+  uint32_t vtsi_last_sector;
+  uint8_t  zero_2;
+  uint8_t  specification_version;
+  uint32_t vts_category;
+  uint16_t zero_3;
+  uint16_t zero_4;
+  uint8_t  zero_5;
+  uint8_t  zero_6[19];
+  uint16_t zero_7;
+  uint8_t  zero_8[32];
+  uint64_t zero_9;
+  uint8_t  zero_10[24];
+  uint32_t vtsi_last_byte;
+  uint32_t zero_11;
+  uint8_t  zero_12[56];
+  uint32_t vtsm_vobs;       /* sector */
+  uint32_t vtstt_vobs;      /* sector */
+  uint32_t vts_ptt_srpt;    /* sector */
+  uint32_t vts_pgcit;       /* sector */
+  uint32_t vtsm_pgci_ut;    /* sector */
+  uint32_t vts_tmapt;       /* sector */  // XXX: FIXME TODO Implement
+  uint32_t vtsm_c_adt;      /* sector */
+  uint32_t vtsm_vobu_admap; /* sector */
+  uint32_t vts_c_adt;       /* sector */
+  uint32_t vts_vobu_admap;  /* sector */
+  uint8_t  zero_13[24];
+  
+  video_attr_t vtsm_video_attr;
+  uint8_t  zero_14;
+  uint8_t  nr_of_vtsm_audio_streams; // should be 0 or 1
+  audio_attr_t vtsm_audio_attr;
+  audio_attr_t zero_15[7];
+  uint8_t  zero_16[17];
+  uint8_t  nr_of_vtsm_subp_streams; // should be 0 or 1
+  subp_attr_t vtsm_subp_attr;
+  subp_attr_t zero_17[27];
+  uint8_t  zero_18[2];
+  
+  video_attr_t vts_video_attr;
+  uint8_t  zero_19;
+  uint8_t  nr_of_vts_audio_streams;
+  audio_attr_t vts_audio_attr[8];
+  uint8_t  zero_20[17];
+  uint8_t  nr_of_vts_subp_streams;
+  subp_attr_t vts_subp_attr[32];
+  /* XXX: how much 'padding' here, if any? */
+} ATTRIBUTE_PACKED vtsi_mat_t;
+
+/**
+ * PartOfTitle Unit Information.
+ */
+typedef struct {
+  uint16_t pgcn;
+  uint16_t pgn;
+} ATTRIBUTE_PACKED ptt_info_t;
+
+/**
+ * PartOfTitle Information.
+ */
+typedef struct {
+  uint16_t nr_of_ptts;
+  ptt_info_t *ptt;
+} ATTRIBUTE_PACKED ttu_t;
+
+/**
+ * PartOfTitle Search Pointer Table.
+ */
+typedef struct {
+  uint16_t nr_of_srpts;
+  uint16_t zero_1;
+  uint32_t last_byte;
+  ttu_t  *title;
+} ATTRIBUTE_PACKED vts_ptt_srpt_t;
+#define VTS_PTT_SRPT_SIZE 8
+
+
+#if PRAGMA_PACK
+#pragma pack()
+#endif
+
+
+/**
+ * The following structure defines an IFO file.  The structure is divided into
+ * two parts, the VMGI, or Video Manager Information, which is read from the
+ * VIDEO_TS.[IFO,BUP] file, and the VTSI, or Video Title Set Information, which
+ * is read in from the VTS_XX_0.[IFO,BUP] files.
+ */
+typedef struct {
+  dvd_file_t *file;
+  
+  /* VMGI */
+  vmgi_mat_t     *vmgi_mat;
+  tt_srpt_t      *tt_srpt;
+  pgc_t          *first_play_pgc;    
+  ptl_mait_t     *ptl_mait;
+  vts_atrt_t     *vts_atrt;
+  txtdt_mgi_t    *txtdt_mgi;
+  
+  /* Common */
+  pgci_ut_t      *pgci_ut;
+  c_adt_t        *menu_c_adt;
+  vobu_admap_t   *menu_vobu_admap;
+  
+  /* VTSI */
+  vtsi_mat_t     *vtsi_mat;
+  vts_ptt_srpt_t *vts_ptt_srpt;
+  pgcit_t        *vts_pgcit;
+  int            *vts_tmapt; // FIXME add/correct the type
+  c_adt_t        *vts_c_adt;
+  vobu_admap_t   *vts_vobu_admap;
+} ifo_handle_t;
+
+#endif /* IFO_TYPES_H_INCLUDED */
diff --git a/extras/libdvdread/nav_print.c b/extras/libdvdread/nav_print.c
new file mode 100644 (file)
index 0000000..f40d42c
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2000 Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * Much of the contents in this file is based on VOBDUMP.
+ *
+ * VOBDUMP: a program for examining DVD .VOB filse
+ *
+ * Copyright 1998, 1999 Eric Smith <eric@brouhaha.com>
+ *
+ * VOBDUMP is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.  Note that I am not
+ * granting permission to redistribute or modify VOBDUMP under the
+ * terms of any later version of the General Public License.
+ *
+ * This program is distributed in the hope that it will be useful (or
+ * at least amusing), but WITHOUT ANY WARRANTY; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "config.h" // Needed for WORDS_BIGENDIAN
+#include "nav_types.h"
+#include "nav_print.h"
+
+
+static void print_time(dvd_time_t *dtime) {
+  const char *rate;
+  assert((dtime->hour>>4) < 0xa && (dtime->hour&0xf) < 0xa);
+  assert((dtime->minute>>4) < 0x7 && (dtime->minute&0xf) < 0xa);
+  assert((dtime->second>>4) < 0x7 && (dtime->second&0xf) < 0xa);
+  assert((dtime->frame_u&0xf) < 0xa);
+  
+  printf("%02x:%02x:%02x.%02x", 
+        dtime->hour,
+        dtime->minute,
+        dtime->second,
+        dtime->frame_u & 0x3f);
+  switch((dtime->frame_u & 0xc0) >> 6) {
+  case 1:
+    rate = "25.00";
+    break;
+  case 3:
+    rate = "29.97";
+    break;
+  default:
+    rate = "(please send a bug report)";
+    break;
+  } 
+  printf(" @ %s fps", rate);
+}
+
+
+static void navPrint_PCI_GI(pci_gi_t *pci_gi) {
+  int i;
+
+  printf("pci_gi:\n");
+  printf("nv_pck_lbn    0x%08x\n", pci_gi->nv_pck_lbn);
+  printf("vobu_cat      0x%04x\n", pci_gi->vobu_cat);
+  printf("vobu_uop_ctl  0x%08x\n", *(uint32_t*)&pci_gi->vobu_uop_ctl);
+  printf("vobu_s_ptm    0x%08x\n", pci_gi->vobu_s_ptm);
+  printf("vobu_e_ptm    0x%08x\n", pci_gi->vobu_e_ptm);
+  printf("vobu_se_e_ptm 0x%08x\n", pci_gi->vobu_se_e_ptm);
+  printf("e_eltm        ");
+  print_time(&pci_gi->e_eltm);
+  printf("\n");
+  
+  printf("vobu_isrc     \"");
+  for(i = 0; i < 32; i++) {
+    char c = pci_gi->vobu_isrc[i];
+    if((c >= ' ') && (c <= '~'))
+      printf("%c", c);
+    else
+      printf(".");
+  }
+  printf("\"\n");
+}
+
+static void navPrint_NSML_AGLI(nsml_agli_t *nsml_agli) {
+  int i, j = 0;
+  
+  for(i = 0; i < 9; i++)
+    j |= nsml_agli->nsml_agl_dsta[i];
+  if(j == 0)
+    return;
+  
+  printf("nsml_agli:\n");
+  for(i = 0; i < 9; i++)
+    if(nsml_agli->nsml_agl_dsta[i])
+      printf("nsml_agl_c%d_dsta  0x%08x\n", i + 1, 
+            nsml_agli->nsml_agl_dsta[i]);
+}
+
+static void navPrint_HL_GI(hl_gi_t *hl_gi, int *btngr_ns, int *btn_ns) {
+  
+  if((hl_gi->hli_ss & 0x03) == 0)
+    return;
+  
+  printf("hl_gi:\n");
+  printf("hli_ss        0x%01x\n", hl_gi->hli_ss & 0x03);
+  printf("hli_s_ptm     0x%08x\n", hl_gi->hli_s_ptm);
+  printf("hli_e_ptm     0x%08x\n", hl_gi->hli_e_ptm);
+  printf("btn_se_e_ptm  0x%08x\n", hl_gi->btn_se_e_ptm);
+
+  *btngr_ns = hl_gi->btngr_ns;
+  printf("btngr_ns      %d\n",  hl_gi->btngr_ns);
+  printf("btngr%d_dsp_ty    0x%02x\n", 1, hl_gi->btngr1_dsp_ty);
+  printf("btngr%d_dsp_ty    0x%02x\n", 2, hl_gi->btngr2_dsp_ty);
+  printf("btngr%d_dsp_ty    0x%02x\n", 3, hl_gi->btngr3_dsp_ty);
+  
+  printf("btn_ofn       %d\n", hl_gi->btn_ofn);
+  *btn_ns = hl_gi->btn_ns;
+  printf("btn_ns        %d\n", hl_gi->btn_ns);
+  printf("nsl_btn_ns    %d\n", hl_gi->nsl_btn_ns);
+  printf("fosl_btnn     %d\n", hl_gi->fosl_btnn);
+  printf("foac_btnn     %d\n", hl_gi->foac_btnn);
+}
+
+static void navPrint_BTN_COLIT(btn_colit_t *btn_colit) {
+  int i, j;
+  
+  j = 0;
+  for(i = 0; i < 6; i++)
+    j |= btn_colit->btn_coli[i/2][i&1];
+  if(j == 0)
+    return;
+  
+  printf("btn_colit:\n");
+  for(i = 0; i < 3; i++)
+    for(j = 0; j < 2; j++)
+      printf("btn_cqoli %d  %s_coli:  %08x\n",
+            i, (j == 0) ? "sl" : "ac",
+            btn_colit->btn_coli[i][j]);
+}
+
+static void navPrint_BTNIT(btni_t *btni_table, int btngr_ns, int btn_ns) {
+  int i, j;
+  
+  printf("btnit:\n");
+  printf("btngr_ns: %i\n", btngr_ns);
+  printf("btn_ns: %i\n", btn_ns);
+  
+  if(btngr_ns == 0)
+    return;
+  
+  for(i = 0; i < btngr_ns; i++) {
+    for(j = 0; j < (36 / btngr_ns); j++) {
+      if(j < btn_ns) {
+       btni_t *btni = &btni_table[(36 / btngr_ns) * i + j];
+       
+       printf("group %d btni %d:  ", i+1, j+1);
+       printf("btn_coln %d, auto_action_mode %d\n",
+              btni->btn_coln, btni->auto_action_mode);
+       printf("coords   (%d, %d) .. (%d, %d)\n",
+              btni->x_start, btni->y_start, btni->x_end, btni->y_end);
+       
+       printf("up %d, ", btni->up);
+       printf("down %d, ", btni->down);
+       printf("left %d, ", btni->left);
+       printf("right %d\n", btni->right);
+       
+       // ifoPrint_COMMAND(&btni->cmd);
+       printf("\n");
+      }
+    }
+  }
+}
+
+static void navPrint_HLI(hli_t *hli) {
+  int btngr_ns = 0, btn_ns = 0;
+  
+  printf("hli:\n");
+  navPrint_HL_GI(&hli->hl_gi, & btngr_ns, & btn_ns);
+  navPrint_BTN_COLIT(&hli->btn_colit);
+  navPrint_BTNIT(hli->btnit, btngr_ns, btn_ns);
+}
+
+void navPrint_PCI(pci_t *pci) {
+  printf("pci packet:\n");
+  navPrint_PCI_GI(&pci->pci_gi);
+  navPrint_NSML_AGLI(&pci->nsml_agli);
+  navPrint_HLI(&pci->hli);
+}
+
+static void navPrint_DSI_GI(dsi_gi_t *dsi_gi) {
+  printf("dsi_gi:\n");
+  printf("nv_pck_scr     0x%08x\n", dsi_gi->nv_pck_scr);
+  printf("nv_pck_lbn     0x%08x\n", dsi_gi->nv_pck_lbn );
+  printf("vobu_ea        0x%08x\n", dsi_gi->vobu_ea);
+  printf("vobu_1stref_ea 0x%08x\n", dsi_gi->vobu_1stref_ea);
+  printf("vobu_2ndref_ea 0x%08x\n", dsi_gi->vobu_2ndref_ea);
+  printf("vobu_3rdref_ea 0x%08x\n", dsi_gi->vobu_3rdref_ea);
+  printf("vobu_vob_idn   0x%04x\n", dsi_gi->vobu_vob_idn);
+  printf("vobu_c_idn     0x%02x\n", dsi_gi->vobu_c_idn);
+  printf("c_eltm         ");
+  print_time(&dsi_gi->c_eltm);
+  printf("\n");
+}
+
+static void navPrint_SML_PBI(sml_pbi_t *sml_pbi) {
+  printf("sml_pbi:\n");
+  printf("category 0x%04x\n", sml_pbi->category);
+  if(sml_pbi->category & 0x8000)
+    printf("VOBU is in preunit\n");
+  if(sml_pbi->category & 0x4000)
+    printf("VOBU is in ILVU\n");
+  if(sml_pbi->category & 0x2000)
+    printf("VOBU at the beginning of ILVU\n");
+  if(sml_pbi->category & 0x1000)
+    printf("VOBU at end of PREU of ILVU\n");
+  
+  printf("ilvu_ea       0x%08x\n", sml_pbi->ilvu_ea);
+  printf("nxt_ilvu_sa   0x%08x\n", sml_pbi->ilvu_sa);
+  printf("nxt_ilvu_size 0x%04x\n", sml_pbi->size);
+  
+  printf("vob_v_s_s_ptm 0x%08x\n", sml_pbi->vob_v_s_s_ptm);
+  printf("vob_v_e_e_ptm 0x%08x\n", sml_pbi->vob_v_e_e_ptm);
+  
+  /* $$$ more code needed here */
+}
+
+static void navPrint_SML_AGLI(sml_agli_t *sml_agli) {
+  int i;
+  printf("sml_agli:\n");
+  for(i = 0; i < 9; i++) {
+    printf("agl_c%d address: 0x%08x size 0x%04x\n", i,
+          sml_agli->data[i].address, sml_agli->data[i].size);
+  }
+}
+
+static void navPrint_VOBU_SRI(vobu_sri_t *vobu_sri) {
+  int i;
+  int stime[19] = { 240, 120, 60, 20, 15, 14, 13, 12, 11, 
+                    10,   9,  8,  7,  6,  5,  4,  3,  2, 1};
+  printf("vobu_sri:\n");
+  printf("Next VOBU with Video %08x\n", vobu_sri->next_video);
+  for(i = 0; i < 19; i++) {
+    printf("%3.1f %08x ", stime[i]/2.0, vobu_sri->fwda[i]);
+  }
+  printf("\n");
+  printf("Next VOBU %08x\n", vobu_sri->next_vobu);
+  printf("--\n");
+  printf("Prev VOBU %08x\n", vobu_sri->prev_vobu);
+  for(i = 0; i < 19; i++) {
+    printf("%3.1f %08x ", stime[18 - i]/2.0, vobu_sri->bwda[i]);
+  }
+  printf("\n");
+  printf("Prev VOBU with Video %08x\n", vobu_sri->prev_video);
+}
+
+static void navPrint_SYNCI(synci_t *synci) {
+  int i;
+  
+  printf("synci:\n");
+  /* $$$ more code needed here */
+  for(i = 0; i < 8; i++)
+    printf("%04x ", synci->a_synca[i]);
+  for(i = 0; i < 32; i++)
+    printf("%08x ", synci->sp_synca[i]);
+}
+
+void navPrint_DSI(dsi_t *dsi) {
+  printf("dsi packet:\n");
+  navPrint_DSI_GI(&dsi->dsi_gi);
+  navPrint_SML_PBI(&dsi->sml_pbi);
+  navPrint_SML_AGLI(&dsi->sml_agli);
+  navPrint_VOBU_SRI(&dsi->vobu_sri);
+  navPrint_SYNCI(&dsi->synci);
+}
+
+
diff --git a/extras/libdvdread/nav_print.h b/extras/libdvdread/nav_print.h
new file mode 100644 (file)
index 0000000..53cc514
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * Copyright (C) 2001 Billy Biggs <vektor@dumbterm.net>,
+ *                    Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef NAV_PRINT_H_INCLUDED
+#define NAV_PRINT_H_INCLUDED
+
+#include <stdio.h>
+#include <dvdread/nav_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * This file provides example functions for printing information about the NAV
+ * packet to stdout.
+ */
+
+void navPrint_PCI(pci_t *pci);
+void navPrint_DSI(dsi_t *dsi);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* NAV_PRINT_H_INCLUDED */
diff --git a/extras/libdvdread/nav_read.c b/extras/libdvdread/nav_read.c
new file mode 100644 (file)
index 0000000..293dafb
--- /dev/null
@@ -0,0 +1,207 @@
+/**
+ * Copyright (C) 2000 Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "config.h" // Needed for WORDS_BIGENDIAN
+#include "bswap.h"
+#include "nav_types.h"
+#include "nav_read.h"
+
+void navRead_PCI(pci_t *pci, unsigned char *buffer) {
+  int i, j;
+
+  assert(sizeof(pci_t) == PCI_BYTES - 1); // -1 for substream id
+  
+  memcpy(pci, buffer, sizeof(pci_t));
+
+  /* Endian conversions  */
+
+  /* pci pci_gi */
+  B2N_32(pci->pci_gi.nv_pck_lbn);
+  B2N_16(pci->pci_gi.vobu_cat);
+  B2N_32(pci->pci_gi.vobu_s_ptm);
+  B2N_32(pci->pci_gi.vobu_e_ptm);
+  B2N_32(pci->pci_gi.vobu_se_e_ptm);
+
+  /* pci nsml_agli */
+  for(i = 0; i < 9; i++)
+    B2N_32(pci->nsml_agli.nsml_agl_dsta[i]);
+
+  /* pci hli hli_gi */
+  B2N_16(pci->hli.hl_gi.hli_ss);
+  B2N_32(pci->hli.hl_gi.hli_s_ptm);
+  B2N_32(pci->hli.hl_gi.hli_e_ptm);
+  B2N_32(pci->hli.hl_gi.btn_se_e_ptm);
+
+  /* pci hli btn_colit */
+  for(i = 0; i < 3; i++)
+    for(j = 0; j < 2; j++)
+      B2N_32(pci->hli.btn_colit.btn_coli[i][j]);
+
+
+  /* pci hli btni */
+  /* There are some issues with this bitfiled with some compilers 
+     because they stradle word boundaries. */
+  
+#if !defined(WORDS_BIGENDIAN)
+  for(i = 0; i < 36; i++) {
+#if 0 /* Wierd Sun CC code that does not work */
+    unsigned char m[6];
+    memcpy(m, &pci->hli.btnit[i], 6);
+    pci->hli.btnit[i].zero1   = (m[1] >> 2);
+    pci->hli.btnit[i].x_start = (m[0] << 4) | (m[1] >> 4);
+    pci->hli.btnit[i].x_end   = (m[1] << 8) | m[2];
+    pci->hli.btnit[i].y_start = (m[3] << 4) | (m[4] >> 4);
+    pci->hli.btnit[i].y_end   = (m[4] << 8) | m[5];
+    pci->hli.btnit[i].zero2   = (m[4] >> 2);
+    pci->hli.btnit[i].btn_coln = (m[0] >> 6);
+    pci->hli.btnit[i].auto_action_mode = (m[3] >> 6);
+#else
+    char tmp[6], swap;
+    memcpy(tmp, &(pci->hli.btnit[i]), 6);
+    /* This is a B2N_24() */
+    swap = tmp[0]; tmp[0] = tmp[2]; tmp[2] = swap;
+    /* This is a B2N_24() */
+    swap = tmp[3]; tmp[3] = tmp[5]; tmp[5] = swap;
+    memcpy(&(pci->hli.btnit[i]), tmp, 6);
+#endif
+  }
+#endif
+
+
+  /* Asserts */
+
+  /* pci pci gi */ 
+  assert(pci->pci_gi.zero1 == 0);
+
+  /* pci hli hli_gi */
+  assert(pci->hli.hl_gi.zero1 == 0);
+  assert(pci->hli.hl_gi.zero2 == 0);
+  assert(pci->hli.hl_gi.zero3 == 0);
+  assert(pci->hli.hl_gi.zero4 == 0);
+  assert(pci->hli.hl_gi.zero5 == 0);
+
+  /* Are there buttons defined here? */
+  if((pci->hli.hl_gi.hli_ss & 0x03) != 0) {
+    assert(pci->hli.hl_gi.btn_ns != 0); 
+    assert(pci->hli.hl_gi.btngr_ns != 0); 
+  } else {
+    assert((pci->hli.hl_gi.btn_ns != 0 && pci->hli.hl_gi.btngr_ns != 0) 
+          || (pci->hli.hl_gi.btn_ns == 0 && pci->hli.hl_gi.btngr_ns == 0));
+  }
+
+  /* pci hli btnit */
+  
+#if NDEBUG
+  for(i = 0; i < pci->hli.hl_gi.btngr_ns; i++) {
+    for(j = 0; j < (36 / pci->hli.hl_gi.btngr_ns); j++) {
+      int n = (36 / pci->hli.hl_gi.btngr_ns) * i + j;
+      assert(pci->hli.btnit[n].zero1 == 0);
+      assert(pci->hli.btnit[n].zero2 == 0);
+      assert(pci->hli.btnit[n].zero3 == 0);
+      assert(pci->hli.btnit[n].zero4 == 0);
+      assert(pci->hli.btnit[n].zero5 == 0);
+      assert(pci->hli.btnit[n].zero6 == 0);
+      
+      if (j < pci->hli.hl_gi.btn_ns) { 
+       assert(pci->hli.btnit[n].x_start <= pci->hli.btnit[n].x_end);
+       assert(pci->hli.btnit[n].y_start <= pci->hli.btnit[n].y_end);
+       assert(pci->hli.btnit[n].up <= pci->hli.hl_gi.btn_ns);
+       assert(pci->hli.btnit[n].down <= pci->hli.hl_gi.btn_ns);
+       assert(pci->hli.btnit[n].left <= pci->hli.hl_gi.btn_ns);
+       assert(pci->hli.btnit[n].right <= pci->hli.hl_gi.btn_ns);
+       //vmcmd_verify(pci->hli.btnit[n].cmd);
+      } else {
+       int k;
+       assert(pci->hli.btnit[n].btn_coln == 0);
+       assert(pci->hli.btnit[n].auto_action_mode == 0);
+       assert(pci->hli.btnit[n].x_start == 0);
+       assert(pci->hli.btnit[n].y_start == 0);
+       assert(pci->hli.btnit[n].x_end == 0);
+       assert(pci->hli.btnit[n].y_end == 0);
+       assert(pci->hli.btnit[n].up == 0);
+       assert(pci->hli.btnit[n].down == 0);
+       assert(pci->hli.btnit[n].left == 0);
+       assert(pci->hli.btnit[n].right == 0);
+       for (k = 0; k < 8; k++)
+         assert(pci->hli.btnit[n].cmd.bytes[k] == 0); //CHECK_ZERO?
+      }
+    }
+  }
+#endif
+}
+
+void navRead_DSI(dsi_t *dsi, unsigned char *buffer) {
+  int i;
+
+  assert(sizeof(dsi_t) == DSI_BYTES - 1); // -1 for substream id
+  
+  memcpy(dsi, buffer, sizeof(dsi_t));
+
+  /* Endian conversions */
+
+  /* dsi dsi gi */
+  B2N_32(dsi->dsi_gi.nv_pck_scr);
+  B2N_32(dsi->dsi_gi.nv_pck_lbn);
+  B2N_32(dsi->dsi_gi.vobu_ea);
+  B2N_32(dsi->dsi_gi.vobu_1stref_ea);
+  B2N_32(dsi->dsi_gi.vobu_2ndref_ea);
+  B2N_32(dsi->dsi_gi.vobu_3rdref_ea);
+  B2N_16(dsi->dsi_gi.vobu_vob_idn);
+
+  /* dsi sml pbi */
+  B2N_16(dsi->sml_pbi.category);
+  B2N_32(dsi->sml_pbi.ilvu_ea);
+  B2N_32(dsi->sml_pbi.ilvu_sa);
+  B2N_16(dsi->sml_pbi.size);
+  B2N_32(dsi->sml_pbi.vob_v_s_s_ptm);
+  B2N_32(dsi->sml_pbi.vob_v_e_e_ptm);
+
+  /* dsi sml agli */
+  for(i = 0; i < 9; i++) {
+    B2N_32(dsi->sml_agli.data[ i ].address);
+    B2N_16(dsi->sml_agli.data[ i ].size);
+  }
+
+  /* dsi vobu sri */
+  B2N_32(dsi->vobu_sri.next_video);
+  for(i = 0; i < 19; i++)
+    B2N_32(dsi->vobu_sri.fwda[i]);
+  B2N_32(dsi->vobu_sri.next_vobu);
+  B2N_32(dsi->vobu_sri.prev_vobu);
+  for(i = 0; i < 19; i++)
+    B2N_32(dsi->vobu_sri.bwda[i]);
+  B2N_32(dsi->vobu_sri.prev_video);
+
+  /* dsi synci */
+  for(i = 0; i < 8; i++)
+    B2N_16(dsi->synci.a_synca[i]);
+  for(i = 0; i < 32; i++)
+    B2N_32(dsi->synci.sp_synca[i]);
+
+  
+  /* Asserts */
+
+  /* dsi dsi gi */
+  assert(dsi->dsi_gi.zero1 == 0);
+}
+
diff --git a/extras/libdvdread/nav_read.h b/extras/libdvdread/nav_read.h
new file mode 100644 (file)
index 0000000..3f9ca6a
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * Copyright (C) 2000, 2001 Håkan Hjort <d95hjort@dtek.chalmers.se>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef NAV_READ_H_INCLUDED
+#define NAV_READ_H_INCLUDED
+
+#include <dvdread/nav_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Reads the PCI packet which begins at buffer into pci.
+ */
+void navRead_PCI(pci_t *pci, unsigned char *buffer);
+
+/**
+ * Reads the DSI packet which begins at buffer into dsi.
+ */
+void navRead_DSI(dsi_t *dsi, unsigned char *buffer);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* NAV_READ_H_INCLUDED */
diff --git a/extras/libdvdread/nav_types.h b/extras/libdvdread/nav_types.h
new file mode 100644 (file)
index 0000000..14e7518
--- /dev/null
@@ -0,0 +1,321 @@
+/**
+ * Copyright (C) 2000 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
+ */
+
+#ifndef NAV_TYPES_H_INCLUDED
+#define NAV_TYPES_H_INCLUDED
+
+#include <inttypes.h>
+#include <dvdread/ifo_types.h> // only dvd_time_t, vm_cmd_t and user_ops_t
+
+
+#undef ATTRIBUTE_PACKED
+#undef PRAGMA_PACK_BEGIN 
+#undef PRAGMA_PACK_END
+
+#if defined(__GNUC__)
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
+#define ATTRIBUTE_PACKED __attribute__ ((packed))
+#define PRAGMA_PACK 0
+#endif
+#endif
+
+#if !defined(ATTRIBUTE_PACKED)
+#define ATTRIBUTE_PACKED
+#define PRAGMA_PACK 1
+#endif
+
+
+/* The length including the substream id byte. */
+#define PCI_BYTES 0x3d4
+#define DSI_BYTES 0x3fa
+
+#define PS2_PCI_SUBSTREAM_ID 0x00
+#define PS2_DSI_SUBSTREAM_ID 0x01
+
+/* Remove this */
+#define DSI_START_BYTE 1031
+
+
+#if PRAGMA_PACK
+#pragma pack(1)
+#endif
+
+
+/**
+ * PCI General Information 
+ */
+typedef struct {
+  uint32_t nv_pck_lbn;
+  uint16_t vobu_cat;
+  uint16_t zero1;
+  user_ops_t vobu_uop_ctl;
+  uint32_t vobu_s_ptm;
+  uint32_t vobu_e_ptm;
+  uint32_t vobu_se_e_ptm;
+  dvd_time_t e_eltm;
+  char vobu_isrc[32];
+} ATTRIBUTE_PACKED pci_gi_t;
+
+/**
+ * Non Seamless Angle Information
+ */
+typedef struct {
+  uint32_t nsml_agl_dsta[9]; 
+} ATTRIBUTE_PACKED nsml_agli_t;
+
+/** 
+ * Highlight General Information 
+ */
+typedef struct {
+  uint16_t hli_ss; // only low 2 bits
+  uint32_t hli_s_ptm;
+  uint32_t hli_e_ptm;
+  uint32_t btn_se_e_ptm;
+#ifdef WORDS_BIGENDIAN
+  unsigned int zero1 : 2;
+  unsigned int btngr_ns : 2;
+  unsigned int zero2 : 1;
+  unsigned int btngr1_dsp_ty : 3;
+  unsigned int zero3 : 1;
+  unsigned int btngr2_dsp_ty : 3;
+  unsigned int zero4 : 1;
+  unsigned int btngr3_dsp_ty : 3;
+#else
+  unsigned int btngr1_dsp_ty : 3;
+  unsigned int zero2 : 1;
+  unsigned int btngr_ns : 2;
+  unsigned int zero1 : 2;
+  unsigned int btngr3_dsp_ty : 3;
+  unsigned int zero4 : 1;
+  unsigned int btngr2_dsp_ty : 3;
+  unsigned int zero3 : 1;
+#endif
+  uint8_t btn_ofn;
+  uint8_t btn_ns;     // only low 6 bits
+  uint8_t nsl_btn_ns; // only low 6 bits
+  uint8_t zero5;
+  uint8_t fosl_btnn;  // only low 6 bits
+  uint8_t foac_btnn;  // only low 6 bits
+} ATTRIBUTE_PACKED hl_gi_t;
+
+
+/** 
+ * Button Color Information Table 
+ */
+typedef struct {
+  uint32_t btn_coli[3][2];
+} ATTRIBUTE_PACKED btn_colit_t;
+
+
+/*
+  btn_coln         11000000 00000000 00000000 00000000 00000000 00000000
+  x_start          00111111 11110000 00000000 00000000 00000000 00000000
+  zero1            00000000 00001100 00000000 00000000 00000000 00000000
+  x_end            00000000 00000011 11111111 00000000 00000000 00000000
+  auto_action_mode 00000000 00000000 00000000 11000000 00000000 00000000
+  y_start          00000000 00000000 00000000 00111111 11110000 00000000
+  zero2            00000000 00000000 00000000 00000000 00001100 00000000
+  y_end            00000000 00000000 00000000 00000000 00000011 11111111
+
+  unsigned int btn_coln         : 2;  //  0 - m[0]>>6
+  unsigned int x_start          : 10; //  2 - m[0]<<4 | m[1]>>4
+  unsigned int zero1            : 2;  // 12 - m[1]>>2
+  unsigned int x_end            : 10; // 14 - m[1]<<8 | m[2]
+  
+  unsigned int auto_action_mode : 2;  // 24 - m[3]>>6
+  unsigned int y_start          : 10; // 26 - m[3]<<4 | m[4]>>4
+  unsigned int zero2            : 2;  // 36 - m[4]>>2
+  unsigned int y_end            : 10; // 38 - m[4]<<8 | m[5]
+ */
+
+/** 
+ * Button Information
+ */
+typedef struct {
+#if 0 /* Wierd Sun CC code that does not work */
+  unsigned int zero1            : 2;
+  unsigned int x_start          : 10;
+  unsigned int x_end            : 10;
+  unsigned int y_start          : 10;
+  
+  unsigned int zero2            : 2;  
+  unsigned int btn_coln         : 2;
+  unsigned int auto_action_mode : 2;
+  unsigned int y_end            : 10;
+#endif
+#ifdef WORDS_BIGENDIAN
+  unsigned int btn_coln         : 2;
+  unsigned int x_start          : 10;
+  unsigned int zero1            : 2;
+  unsigned int x_end            : 10;
+  unsigned int auto_action_mode : 2;
+  unsigned int y_start          : 10;
+  unsigned int zero2            : 2;
+  unsigned int y_end            : 10;
+
+  unsigned int zero3            : 2;
+  unsigned int up               : 6;
+  unsigned int zero4            : 2;
+  unsigned int down             : 6;
+  unsigned int zero5            : 2;
+  unsigned int left             : 6;
+  unsigned int zero6            : 2;
+  unsigned int right            : 6;
+#else
+  unsigned int x_end            : 10;
+  unsigned int zero1            : 2;
+  unsigned int x_start          : 10;
+  unsigned int btn_coln         : 2;
+  unsigned int y_end            : 10;
+  unsigned int zero2            : 2;
+  unsigned int y_start          : 10;
+  unsigned int auto_action_mode : 2;
+
+  unsigned int up               : 6;
+  unsigned int zero3            : 2;
+  unsigned int down             : 6;
+  unsigned int zero4            : 2;
+  unsigned int left             : 6;
+  unsigned int zero5            : 2;
+  unsigned int right            : 6;
+  unsigned int zero6            : 2;
+#endif
+  vm_cmd_t cmd;
+} ATTRIBUTE_PACKED btni_t;
+
+/**
+ * Highlight Information 
+ */
+typedef struct {
+  hl_gi_t     hl_gi;
+  btn_colit_t btn_colit;
+  btni_t      btnit[36];
+} ATTRIBUTE_PACKED hli_t;
+
+/**
+ * PCI packet
+ */
+typedef struct {
+  pci_gi_t    pci_gi;
+  nsml_agli_t nsml_agli;
+  hli_t       hli;
+  uint8_t     zero1[189];
+} ATTRIBUTE_PACKED pci_t;
+
+
+
+
+/**
+ * DSI General Information 
+ */
+typedef struct {
+  uint32_t nv_pck_scr;
+  uint32_t nv_pck_lbn;
+  uint32_t vobu_ea;
+  uint32_t vobu_1stref_ea;
+  uint32_t vobu_2ndref_ea;
+  uint32_t vobu_3rdref_ea;
+  uint16_t vobu_vob_idn;
+  uint8_t  zero1;
+  uint8_t  vobu_c_idn;
+  dvd_time_t c_eltm;
+} ATTRIBUTE_PACKED dsi_gi_t;
+
+/**
+ * Seamless Playback Information
+ */
+typedef struct {
+  uint16_t category; // category of seamless VOBU
+  uint32_t ilvu_ea;  // end address of interleaved Unit (sectors)
+  uint32_t ilvu_sa;  // start address of next interleaved unit (sectors)
+  uint16_t size;     // size of next interleaved unit (sectors)
+  uint32_t vob_v_s_s_ptm; /* video start ptm in vob */
+  uint32_t vob_v_e_e_ptm; /* video end ptm in vob */
+  struct {
+    uint32_t stp_ptm1;
+    uint32_t stp_ptm2;
+    uint32_t gap_len1;
+    uint32_t gap_len2;      
+  } vob_a[8];
+} ATTRIBUTE_PACKED sml_pbi_t;
+
+/**
+ * Seamless Angle Infromation for one angle
+ */
+typedef struct {
+    uint32_t address; // Sector offset to next ILVU, high bit is before/after
+    uint16_t size;    // Byte size of the ILVU poited to by address.
+} ATTRIBUTE_PACKED sml_agl_data_t;
+
+/**
+ * Seamless Angle Infromation
+ */
+typedef struct {
+  sml_agl_data_t data[9];
+} ATTRIBUTE_PACKED sml_agli_t;
+
+/**
+ * VOBU Search Information 
+ */
+typedef struct {
+  uint32_t next_video; // Next vobu that contains video
+  uint32_t fwda[19];   // Forwards, time
+  uint32_t next_vobu;
+  uint32_t prev_vobu;
+  uint32_t bwda[19];   // Backwards, time
+  uint32_t prev_video;
+} ATTRIBUTE_PACKED vobu_sri_t;
+
+#define SRI_END_OF_CELL 0x3fffffff
+
+/**
+ * Synchronous Information
+ */ 
+typedef struct {
+  uint16_t a_synca[8];   // Sector offset to first audio packet for this VOBU
+  uint32_t sp_synca[32]; // Sector offset to first subpicture packet
+} ATTRIBUTE_PACKED synci_t;
+
+/**
+ * DSI packet
+ */
+typedef struct {
+  dsi_gi_t   dsi_gi;
+  sml_pbi_t  sml_pbi;
+  sml_agli_t sml_agli;
+  vobu_sri_t vobu_sri;
+  synci_t    synci;
+  uint8_t    zero1[471];
+} ATTRIBUTE_PACKED dsi_t;
+
+
+#if PRAGMA_PACK
+#pragma pack()
+#endif
+
+#endif /* NAV_TYPES_H_INCLUDED */
diff --git a/extras/libdvdread/videolan/.cvsignore b/extras/libdvdread/videolan/.cvsignore
new file mode 100644 (file)
index 0000000..63e7180
--- /dev/null
@@ -0,0 +1 @@
+.dep
diff --git a/extras/libdvdread/videolan/dvdread.c b/extras/libdvdread/videolan/dvdread.c
new file mode 100644 (file)
index 0000000..8c7ccce
--- /dev/null
@@ -0,0 +1,867 @@
+/*****************************************************************************
+ * dvdread.c: replacement for dvd_reader.c that always takes dvdcss functions
+ * (hard-linked) and adds a readv call function to tha API.
+ *****************************************************************************
+ * Copyright (C) 2001 Billy Biggs <vektor@dumbterm.net>.
+ * $Id: dvdread.c,v 1.1 2001/11/25 05:04:38 stef Exp $
+ *
+ * Author: Billy Biggs <vektor@dumbterm.net>
+ *         Stéphane Borel <stef@via.ecp.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ *****************************************************************************/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/uio.h> /* readv() */
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <dlfcn.h>
+#include <dirent.h>
+
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__)
+#define SYS_BSD 1
+#endif
+
+#if defined(__sun)
+#include <sys/mnttab.h>
+#elif defined(SYS_BSD)
+#include <fstab.h>
+#elif defined(__linux__)
+#include <mntent.h>
+#endif
+
+#if defined(SYS_BSD)
+typedef off_t off64_t;
+#define lseek64 lseek
+#define stat64 stat
+#endif
+
+#include "dvd_udf.h"
+#include "dvd_reader.h"
+
+#include "../libdvdcss/videolan/dvdcss.h"
+
+struct dvd_reader_s {
+    /* Basic information. */
+    int isImageFile;
+
+    /* Information required for an image file. */
+    dvdcss_handle dev;
+    int init_keys;
+    int fd;
+
+    /* Information required for a directory path drive. */
+    char *path_root;
+};
+
+struct dvd_file_s {
+    /* Basic information. */
+    dvd_reader_t *dvd;
+
+    /* 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 ];
+    int title_fds[ 9 ];
+
+    /* Calculated at open-time, size in blocks. */
+    ssize_t filesize;
+};
+
+/**
+ * Open a DVD image or block device file.
+ */
+static dvd_reader_t *DVDOpenImageFile( const char *location )
+{
+    dvd_reader_t *dvd;
+    dvdcss_handle dev = 0;
+    int fd = -1;
+
+    dev = dvdcss_open( (char *) location );
+    if( !dev ) {
+        fprintf( stderr, "libdvdread: Can't open %s for reading.\n",
+            location );
+        return 0;
+    }
+
+    dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
+    if( !dvd ) return 0;
+    dvd->isImageFile = 1;
+    dvd->dev = dev;
+    dvd->init_keys = 0;
+    dvd->fd = fd;
+    dvd->path_root = 0;
+    
+    return dvd;
+}
+
+static dvd_reader_t *DVDOpenPath( const char *path_root )
+{
+    dvd_reader_t *dvd;
+
+    dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
+    if( !dvd ) return 0;
+    dvd->isImageFile = 0;
+    dvd->dev = 0;
+    dvd->init_keys = 0;
+    dvd->fd = -1;
+    dvd->path_root = strdup( path_root );
+
+    return dvd;
+}
+
+#if defined(__sun)
+/* /dev/rdsk/c0t6d0s0 (link to /devices/...)
+   /vol/dev/rdsk/c0t6d0/??
+   /vol/rdsk/<name> */
+static char *sun_block2char( const char *path )
+{
+    char *new_path;
+
+    /* Must contain "/dsk/" */ 
+    if( !strstr( path, "/dsk/" ) ) return (char *) strdup( path );
+
+    /* Replace "/dsk/" with "/rdsk/" */
+    new_path = malloc( strlen(path) + 2 );
+    strcpy( new_path, path );
+    strcpy( strstr( new_path, "/dsk/" ), "" );
+    strcat( new_path, "/rdsk/" );
+    strcat( new_path, strstr( path, "/dsk/" ) + strlen( "/dsk/" ) );
+
+    return new_path;
+}
+#endif
+
+#if defined(SYS_BSD)
+/* FreeBSD /dev/(r)(a)cd0 (a is for atapi, should work without r)
+   OpenBSD /dev/rcd0c
+   NetBSD  /dev/rcd0d or /dev/rcd0c (for non x86)
+   BSD/OS  /dev/sr0 (if not mounted) or /dev/rsr0 */
+static char *bsd_block2char( const char *path )
+{
+    char *new_path;
+
+    /* If it doesn't start with "/dev/" or does start with "/dev/r" exit */ 
+    if( !strncmp( path, "/dev/",  5 ) || strncmp( path, "/dev/r", 6 ) ) 
+      return (char *) strdup( path );
+
+    /* Replace "/dev/" with "/dev/r" */
+    new_path = malloc( strlen(path) + 2 );
+    strcpy( new_path, "/dev/r" );
+    strcat( new_path, path + strlen( "/dev/" ) );
+
+    return new_path;
+}
+#endif
+
+dvd_reader_t *DVDOpen( const char *path )
+{
+    struct stat64 fileinfo;
+    int ret;
+
+    if( !path ) return 0;
+
+    ret = stat64( path, &fileinfo );
+    if( ret < 0 ) {
+       /* If we can't stat the file, give up */
+       fprintf( stderr, "libdvdread: Can't stat %s\n", path );
+       perror("");
+       return 0;
+    }
+
+    /* First check if this is a block/char device or a file*/
+    if( S_ISBLK( fileinfo.st_mode ) || 
+       S_ISCHR( fileinfo.st_mode ) || 
+       S_ISREG( fileinfo.st_mode ) ) {
+
+       /**
+        * Block devices and regular files are assumed to be DVD-Video images.
+        */
+#if defined(__sun)
+       return DVDOpenImageFile( sun_block2char( path ) );
+#elif defined(SYS_BSD)
+       return DVDOpenImageFile( bsd_block2char( path ) );
+#else
+       return DVDOpenImageFile( path );
+#endif
+
+    } else if( S_ISDIR( fileinfo.st_mode ) ) {
+       dvd_reader_t *auth_drive = 0;
+       char *path_copy;
+#if defined(SYS_BSD)
+       struct fstab* fe;
+#elif defined(__sun) || defined(__linux__)
+       FILE *mntfile;
+#endif
+
+       /* XXX: We should scream real loud here. */
+       if( !(path_copy = strdup( path ) ) ) return 0;
+
+       /* Resolve any symlinks and get the absolut dir name. */
+       {
+           char *new_path;
+           int cdir = open( ".", O_RDONLY );
+           
+           if( cdir >= 0 ) {
+               chdir( path_copy );
+               new_path = getcwd( NULL, PATH_MAX );
+               fchdir( cdir );
+               close( cdir );
+               if( new_path ) {
+                   free( path_copy );
+                   path_copy = new_path;
+               }
+           }
+       }
+
+       /**
+        * If we're being asked to open a directory, check if that directory
+        * is the mountpoint for a DVD-ROM which we can use instead.
+        */
+
+       if( strlen( path_copy ) > 1 ) {
+           if( path[ strlen( path_copy ) - 1 ] == '/' ) 
+               path_copy[ strlen( path_copy ) - 1 ] = '\0';
+       }
+
+       if( strlen( path_copy ) > 9 ) {
+           if( !strcasecmp( &(path_copy[ strlen( path_copy ) - 9 ]), 
+                            "/video_ts" ) ) {
+             path_copy[ strlen( path_copy ) - 9 ] = '\0';
+           }
+       }
+
+#if defined(SYS_BSD)
+       if( ( fe = getfsfile( path_copy ) ) ) {
+           char *dev_name = bsd_block2char( fe->fs_spec );
+           fprintf( stderr,
+                    "libdvdread: Attempting to use device %s"
+                    " mounted on %s for CSS authentication\n",
+                    dev_name,
+                    fe->fs_file );
+           auth_drive = DVDOpenImageFile( dev_name );
+           free( dev_name );
+       }
+#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 ) ) {
+                   char *dev_name = sun_block2char( mp.mnt_special );
+                   fprintf( stderr, 
+                            "libdvdread: Attempting to use device %s"
+                            " mounted on %s for CSS authentication\n",
+                            dev_name,
+                            mp.mnt_mountp );
+                   auth_drive = DVDOpenImageFile( dev_name );
+                   free( dev_name );
+                   break;
+               }
+           }
+           fclose( mntfile );
+       }
+#elif defined(__linux__)
+        mntfile = fopen( MOUNTED, "r" );
+        if( mntfile ) {
+            struct mntent *me;
+            while( ( me = getmntent( mntfile ) ) ) {
+                if( !strcmp( me->mnt_dir, path_copy ) ) {
+                   fprintf( stderr, 
+                            "libdvdread: Attempting to use device %s"
+                             " mounted on %s for CSS authentication\n",
+                             me->mnt_fsname,
+                            me->mnt_dir );
+                    auth_drive = DVDOpenImageFile( me->mnt_fsname );
+                    break;
+                }
+            }
+            fclose( mntfile );
+       }
+#endif
+       if( !auth_drive ) {
+           fprintf( stderr, "libdvdread: Device inaccessible, "
+                    "CSS authentication not available.\n" );
+       }
+
+       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.
+         */
+       fprintf( stderr, "libdvdread: Using normal filesystem access.\n" );
+        return DVDOpenPath( path );
+    }
+
+    /* If it's none of the above, screw it. */
+    fprintf( stderr, "libdvdread: Could not open %s\n", path );
+    return 0;
+}
+
+void DVDClose( dvd_reader_t *dvd )
+{
+    if( dvd ) {
+        if( dvd->dev ) dvdcss_close( dvd->dev );
+        if( dvd->fd >= 0 ) close( dvd->fd );
+        if( dvd->path_root ) free( dvd->path_root );
+        free( dvd );
+        dvd = 0;
+    }
+}
+
+/**
+ * Open an unencrypted file on a DVD image file.
+ */
+static dvd_file_t *DVDOpenFileUDF( dvd_reader_t *dvd, char *filename )
+{
+    uint32_t start, len;
+    dvd_file_t *dvd_file;
+
+    start = UDFFindFile( dvd, filename, &len );
+    if( !start ) return 0;
+
+    dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
+    if( !dvd_file ) return 0;
+    dvd_file->dvd = dvd;
+    dvd_file->lb_start = start;
+    dvd_file->seek_pos = 0;
+    memset( dvd_file->title_fds, -1, sizeof( dvd_file->title_fds ) );
+    dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
+
+    return dvd_file;
+}
+
+/**
+ * Searches for <file> in directory <path>, ignoring case.
+ * Returns 0 and full filename in <filename>.
+ *     or -1 on file not found.
+ *     or -2 on path not found.
+ */
+static int findDirFile( const char *path, const char *file, char *filename ) 
+{
+    DIR *dir;
+    struct dirent *ent;
+
+    dir = opendir( path );
+    if( !dir ) return -2;
+
+    while( ( ent = readdir( dir ) ) != NULL ) {
+        if( !strcasecmp( ent->d_name, file ) ) {
+            sprintf( filename, "%s%s%s", path,
+                     ( ( path[ strlen( path ) - 1 ] == '/' ) ? "" : "/" ),
+                     ent->d_name );
+            return 0;
+        }
+    }
+
+    return -1;
+}
+
+static int findDVDFile( dvd_reader_t *dvd, const char *file, char *filename )
+{
+    char video_path[ PATH_MAX + 1 ];
+    const char *nodirfile;
+    int ret;
+
+    /* Strip off the directory for our search */
+    if( !strncasecmp( "/VIDEO_TS/", file, 10 ) ) {
+        nodirfile = &(file[ 10 ]);
+    } else {
+        nodirfile = file;
+    }
+
+    ret = findDirFile( dvd->path_root, nodirfile, filename );
+    if( ret < 0 ) {
+        /* Try also with adding the path, just in case. */
+        sprintf( video_path, "%s/VIDEO_TS/", dvd->path_root );
+        ret = findDirFile( video_path, nodirfile, filename );
+        if( ret < 0 ) {
+            /* Try with the path, but in lower case. */
+            sprintf( video_path, "%s/video_ts/", dvd->path_root );
+            ret = findDirFile( video_path, nodirfile, filename );
+            if( ret < 0 ) {
+                return 0;
+            }
+        }
+    }
+
+    return 1;
+}
+
+/**
+ * Open an unencrypted file from a DVD directory tree.
+ */
+static dvd_file_t *DVDOpenFilePath( dvd_reader_t *dvd, char *filename )
+{
+    char full_path[ PATH_MAX + 1 ];
+    dvd_file_t *dvd_file;
+    struct stat fileinfo;
+    int fd;
+
+    /* Get the full path of the file. */
+    if( !findDVDFile( dvd, filename, full_path ) ) return 0;
+
+    fd = open( full_path, O_RDONLY );
+    if( fd < 0 ) 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_fds, -1, sizeof( dvd_file->title_fds ) );
+    dvd_file->filesize = 0;
+
+    if( stat( full_path, &fileinfo ) < 0 ) {
+        fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
+        free( dvd_file );
+        return 0;
+    }
+    dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+    dvd_file->title_fds[ 0 ] = fd;
+    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;
+    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_fds, -1, sizeof( dvd_file->title_fds ) );
+    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( dvdcss_seek( dvd_file->dvd->dev, (int)start, DVDCSS_SEEK_KEY ) < 0 ) {
+            fprintf( stderr, "libdvdread: Error cracking CSS key for %s\n",
+                     filename );
+    }
+
+    return dvd_file;
+}
+
+static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *dvd, 
+                                  int title, int menu )
+{
+    char filename[ MAX_UDF_FILE_NAME_LEN ];
+    char full_path[ PATH_MAX + 1 ];
+    struct stat fileinfo;
+    dvd_file_t *dvd_file;
+    int i;
+
+    dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
+    if( !dvd_file ) return 0;
+    dvd_file->dvd = dvd;
+    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_fds, -1, sizeof( dvd_file->title_fds ) );
+    dvd_file->filesize = 0;
+
+    if( menu ) {
+        int fd;
+
+        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;
+        }
+
+        fd = open( full_path, O_RDONLY );
+        if( fd < 0 ) {
+            free( dvd_file );
+            return 0;
+        }
+
+        if( stat( full_path, &fileinfo ) < 0 ) {
+            fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
+            free( dvd_file );
+            return 0;
+        }
+        dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+        dvd_file->title_fds[ 0 ] = fd;
+        dvd_file->filesize = dvd_file->title_sizes[ 0 ];
+
+    } else {
+        for( i = 0; i < 9; ++i ) {
+
+            sprintf( filename, "VTS_%02i_%i.VOB", title, i + 1 );
+            if( !findDVDFile( dvd, filename, full_path ) ) {
+                break;
+            }
+
+            if( stat( full_path, &fileinfo ) < 0 ) {
+                fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
+                break;
+            }
+
+            dvd_file->title_sizes[ i ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+            dvd_file->title_fds[ i ] = open( full_path, O_RDONLY );
+            dvd_file->filesize += dvd_file->title_sizes[ i ];
+        }
+        if( !(dvd_file->title_sizes[ 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 ];
+
+    switch( domain ) {
+    case DVD_READ_INFO_FILE:
+        if( titlenum == 0 ) {
+            sprintf( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
+        } else {
+            sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );
+        }
+        break;
+    case DVD_READ_INFO_BACKUP_FILE:
+        if( titlenum == 0 ) {
+            sprintf( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
+        } else {
+            sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );
+        }
+        break;
+    case DVD_READ_MENU_VOBS:
+        if( dvd->isImageFile ) {
+            return DVDOpenVOBUDF( dvd, titlenum, 1 );
+        } else {
+            return DVDOpenVOBPath( dvd, titlenum, 1 );
+        }
+        break;
+    case DVD_READ_TITLE_VOBS:
+        if( titlenum == 0 ) return 0;
+        if( dvd->isImageFile ) {
+            return DVDOpenVOBUDF( dvd, titlenum, 0 );
+        } else {
+            return DVDOpenVOBPath( dvd, titlenum, 0 );
+        }
+        break;
+    default:
+        fprintf( stderr, "libdvdread: Invalid domain for file open.\n" );
+        return 0;
+    }
+    
+    if( dvd->isImageFile ) {
+        return DVDOpenFileUDF( dvd, filename );
+    } else {
+        return DVDOpenFilePath( dvd, filename );
+    }
+}
+
+void DVDCloseFile( dvd_file_t *dvd_file )
+{
+    int i;
+
+    if( dvd_file ) {
+        if( !dvd_file->dvd->isImageFile ) {
+            for( i = 0; i < 9; ++i ) {
+                if( dvd_file->title_fds[ i ] >= 0 )
+                   close( dvd_file->title_fds[ i ] );
+            }
+        }
+
+        free( dvd_file );
+        dvd_file = 0;
+    }
+}
+
+int64_t DVDReadLBUDF( dvd_reader_t *device, uint32_t lb_number,
+                     size_t block_count, unsigned char *data, 
+                     int encrypted )
+{
+    int ret;
+
+    if( !device->dev ) {
+        fprintf( stderr, "libdvdread: Fatal error in block read.\n" );
+        return 0;
+    }
+
+    ret = dvdcss_seek( device->dev, (int) lb_number, DVDCSS_NOFLAGS );
+    if( ret != (int) lb_number ) {
+        fprintf( stderr, "libdvdread: Can't seek to block %u\n", 
+                lb_number );
+        return 0;
+    }
+
+    return (int64_t) ( dvdcss_read( device->dev, (char *) data, 
+                           (int) block_count, encrypted ) 
+                  * (uint64_t) DVD_VIDEO_LB_LEN );
+}
+
+static int64_t DVDReadBlocksUDF( dvd_file_t *dvd_file, uint32_t offset,
+                                size_t block_count, unsigned char *data )
+{
+    return DVDReadLBUDF( dvd_file->dvd, dvd_file->lb_start + offset,
+                         block_count, data, DVDCSS_READ_DECRYPT );
+}
+
+static int64_t DVDReadBlocksPath( dvd_file_t *dvd_file, size_t offset,
+                                 size_t block_count, unsigned char *data )
+{
+    int i;
+    ssize_t ret, ret2;
+    off64_t off;
+
+    ret = 0;
+    ret2 = 0;
+    for( i = 0; i < 9; ++i ) {
+        if( !dvd_file->title_sizes[ i ] ) return 0;
+
+        if( offset < dvd_file->title_sizes[ i ] ) {
+            if( ( offset + block_count ) <= dvd_file->title_sizes[ i ] ) {
+               off = lseek64( dvd_file->title_fds[ i ], 
+                              offset * (int64_t) DVD_VIDEO_LB_LEN, SEEK_SET );
+               if( off != ( offset * (int64_t) DVD_VIDEO_LB_LEN ) ) {
+                   fprintf( stderr, "libdvdread: Can't seek to block %d\n", 
+                            offset );
+                   return 0;
+               }
+                ret = read( dvd_file->title_fds[ i ], data,
+                            block_count * DVD_VIDEO_LB_LEN );
+                break;
+            } else {
+               size_t part1_size 
+                 = ( dvd_file->title_sizes[ i ] - offset ) * DVD_VIDEO_LB_LEN;
+               /* 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 = lseek64( dvd_file->title_fds[ i ], 
+                              offset * (int64_t) DVD_VIDEO_LB_LEN, SEEK_SET );
+               if( off != ( offset * (int64_t) DVD_VIDEO_LB_LEN ) ) {
+                   fprintf( stderr, "libdvdread: Can't seek to block %d\n", 
+                            offset );
+                   return 0;
+               }
+                ret = read( dvd_file->title_fds[ i ], data, part1_size );
+               if( ret < 0 ) return ret;
+               /* FIXME: This is wrong if i is the last file in the set. 
+                         also error from this read will not show in ret. */
+               
+                /* Read part 2 */
+                lseek64( dvd_file->title_fds[ i + 1 ], (off64_t)0, SEEK_SET );
+                ret2 = read( dvd_file->title_fds[ i + 1 ], data + part1_size,
+                             block_count * DVD_VIDEO_LB_LEN - part1_size );
+                if( ret2 < 0 ) return ret2;
+               break;
+            }
+        } else {
+            offset -= dvd_file->title_sizes[ i ];
+        }
+    }
+
+    return ( (int64_t) ret + (int64_t) ret2 );
+}
+
+/* These are broken for some cases reading more than 2Gb at a time. */
+ssize_t DVDReadBlocks( dvd_file_t *dvd_file, int offset, 
+                      size_t block_count, unsigned char *data )
+{
+    int64_t ret;
+  
+    if( dvd_file->dvd->isImageFile ) {
+       ret = DVDReadBlocksUDF( dvd_file, (uint32_t)offset, 
+                               block_count, data );
+    } else {
+       ret = DVDReadBlocksPath( dvd_file, (size_t) offset, 
+                                block_count, data );
+    }
+    if( ret <= 0 ) {
+        return (ssize_t) ret;
+    }
+    {
+      ssize_t sret = (ssize_t) (ret / (int64_t)DVD_VIDEO_LB_LEN );
+      if( sret == 0 ) {
+       fprintf(stderr, "libdvdread: DVDReadBlocks got %d bytes\n", (int)ret );
+      }
+      return sret;
+    }
+}
+
+int32_t DVDFileSeek( dvd_file_t *dvd_file, int32_t offset )
+{
+    if( dvd_file->dvd->isImageFile ) {
+        dvd_file->seek_pos = (uint32_t) offset;
+        return offset;
+    } else {
+        return (int32_t) ( lseek( dvd_file->title_fds[ 0 ], 
+                                 (off_t) offset, SEEK_SET ) );
+    }
+}
+
+static ssize_t DVDReadBytesUDF( dvd_file_t *dvd_file, void *data, 
+                               size_t byte_size )
+{
+    unsigned char *secbuf;
+    unsigned int numsec, seek_sector, seek_byte;
+    int64_t len;
+    
+    seek_sector = dvd_file->seek_pos / DVD_VIDEO_LB_LEN;
+    seek_byte   = dvd_file->seek_pos % DVD_VIDEO_LB_LEN;
+
+    numsec = ( ( seek_byte + byte_size ) / DVD_VIDEO_LB_LEN ) + 1;
+    secbuf = (unsigned char *) malloc( numsec * DVD_VIDEO_LB_LEN );
+    if( !secbuf ) {
+       fprintf( stderr, "libdvdread: Can't allocate memory " 
+                "for file read!\n" );
+        return 0;
+    }
+
+    len = DVDReadLBUDF( dvd_file->dvd, dvd_file->lb_start + seek_sector,
+                        numsec, secbuf, DVDCSS_NOFLAGS );
+    if( len != numsec * (int64_t) DVD_VIDEO_LB_LEN ) {
+        free( secbuf );
+        return 0;
+    }
+
+    dvd_file->seek_pos += byte_size;
+
+    memcpy( data, &(secbuf[ seek_byte ]), byte_size );
+    free( secbuf );
+
+    return byte_size;
+}
+
+static ssize_t DVDReadBytesPath( dvd_file_t *dvd_file, void *data, 
+                                size_t byte_size )
+{
+    return read( dvd_file->title_fds[ 0 ], data, byte_size );
+}
+
+ssize_t DVDReadBytes( dvd_file_t *dvd_file, void *data, size_t byte_size )
+{
+    if( dvd_file->dvd->isImageFile ) {
+        return DVDReadBytesUDF( dvd_file, data, byte_size );
+    } else {
+        return DVDReadBytesPath( dvd_file, data, byte_size );
+    }
+}
+
+ssize_t DVDFileSize( dvd_file_t *dvd_file )
+{
+    return dvd_file->filesize;
+}
+
+int64_t DVDReadVLBUDF( dvd_reader_t *device, uint32_t lb_number,
+               size_t block_count, struct iovec * vector,
+              int encrypted )
+{
+    int ret;
+
+    if( !device->dev ) {
+        fprintf( stderr, "libdvdread: Fatal error in block read.\n" );
+        return 0;
+    }
+
+    ret = dvdcss_seek( device->dev, (int) lb_number, 0 );
+    if( ret != (int) lb_number ) {
+        fprintf( stderr, "libdvdread: Can't seek to block %u\n",
+                 lb_number );
+        return 0;
+    }
+
+    return (int64_t) ( dvdcss_readv( device->dev, vector,
+                           (int)block_count, encrypted )
+                   * (uint64_t) DVD_VIDEO_LB_LEN );
+}
+
+static int64_t DVDReadVBlocksUDF( dvd_file_t *dvd_file, uint32_t offset,
+                 size_t block_count, struct iovec *vector )
+{
+    return DVDReadVLBUDF( dvd_file->dvd, dvd_file->lb_start + offset,
+                         block_count, vector, DVDCSS_READ_DECRYPT );
+}
+
+
+ssize_t DVDReadVBlocks( dvd_file_t *dvd_file, int offset,
+               size_t block_count, struct iovec * vector )
+{
+    int64_t ret;
+
+    if( dvd_file->dvd->isImageFile ) {
+    ret = DVDReadVBlocksUDF( dvd_file, (uint32_t)offset,
+                block_count, vector );
+    } else {
+  ret = 0;//= DVDReadVBlocksPath( dvd_file, (size_t) offset, 
+//               block_count, vector );
+    }
+    if( ret <= 0 ) {
+        return (ssize_t) ret;
+    }
+    {
+      ssize_t sret = (ssize_t) (ret / (int64_t)DVD_VIDEO_LB_LEN );
+      if( sret == 0 ) {
+    fprintf(stderr, "libdvdread: DVDReadVBlocks got %d bytes\n", (int)ret );
+      }
+      return sret;
+    }
+
+}
+
diff --git a/extras/libdvdread/videolan/dvdread.h b/extras/libdvdread/videolan/dvdread.h
new file mode 100644 (file)
index 0000000..04b5c8a
--- /dev/null
@@ -0,0 +1,25 @@
+/*****************************************************************************
+ * dvdread.h: DVD reading library, exported functions.
+ *****************************************************************************
+ * Copyright (C) 1998-2001 VideoLAN
+ * $Id: dvdread.h,v 1.1 2001/11/25 05:04:38 stef Exp $
+ *
+ * Authors: Stéphane Borel <stef@via.ecp.fr>
+ *
+ * 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.
+ *****************************************************************************/
+
+ssize_t DVDReadVBlocks( dvd_file_t *dvd_file, int offset,
+                       size_t block_count, struct iovec * vector );
diff --git a/plugins/dvdread/.cvsignore b/plugins/dvdread/.cvsignore
new file mode 100644 (file)
index 0000000..63e7180
--- /dev/null
@@ -0,0 +1 @@
+.dep
diff --git a/plugins/dvdread/Makefile b/plugins/dvdread/Makefile
new file mode 100644 (file)
index 0000000..e284f0a
--- /dev/null
@@ -0,0 +1,53 @@
+###############################################################################
+# vlc (VideoLAN Client) dvdread module Makefile
+# (c)2001 VideoLAN
+###############################################################################
+
+#
+# Objects
+#
+
+PLUGIN_DVDREAD = dvdread.o input_dvdread.o $(OBJ_DVDREAD)
+BUILTIN_DVDREAD = $(PLUGIN_DVDREAD:%.o=BUILTIN_%.o)
+
+ALL_OBJ = $(PLUGIN_DVDREAD) $(BUILTIN_DVDREAD)
+
+#
+# Virtual targets
+#
+
+include ../../Makefile.modules
+
+$(PLUGIN_DVDREAD): %.o: .dep/%.d
+$(PLUGIN_DVDREAD): %.o: %.c
+       $(CC) $(CFLAGS_DVD) $(CFLAGS_DVDREAD) $(CFLAGS) $(PCFLAGS) -DPLUGIN -c -o $@ $<
+
+$(BUILTIN_DVDREAD): BUILTIN_%.o: .dep/%.d
+$(BUILTIN_DVDREAD): BUILTIN_%.o: %.c
+       $(CC) $(CFLAGS_DVD) $(CFLAGS_DVDREAD) $(CFLAGS) -DBUILTIN -c -o $@ $<
+
+#
+# Real targets
+#
+
+ifeq (1,$(NEED_LIBDVDCSS))
+../dvdread.so: libdvdcss libdvdread $(PLUGIN_DVDREAD)
+       $(CC) $(PCFLAGS) -o $@ $(PLUGIN_DVDREAD) $(PLCFLAGS) $(LIB_DVD_PLUGIN) $(LIB_DVDREAD_PLUGIN)
+
+../dvdread.a: libdvdcss libdvdread $(BUILTIN_DVDREAD)
+       ar r $@ $(BUILTIN_DVDREAD)
+       $(RANLIB) $@
+else
+../dvdread.so: libdvdread $(PLUGIN_DVDREAD)
+       $(CC) $(PCFLAGS) -o $@ $(PLUGIN_DVD) $(PLCFLAGS) $(LIB_DVD_PLUGIN) $(LIB_DVDREAD_PLUGIN)
+
+../dvdread.a: libdvdread $(BUILTIN_DVDREAD)
+       ar r $@ $(BUILTIN_DVDREAD)
+       $(RANLIB) $@
+endif
+
+libdvdcss:
+       cd ../../ && $(MAKE) libdvdcss
+
+libdvdread:
+       cd ../../ && $(MAKE) libdvdread
diff --git a/plugins/dvdread/dvdread.c b/plugins/dvdread/dvdread.c
new file mode 100644 (file)
index 0000000..24eff27
--- /dev/null
@@ -0,0 +1,197 @@
+/*****************************************************************************
+ * dvdread.c : DvdRead input module for vlc
+ *****************************************************************************
+ * Copyright (C) 2001 VideoLAN
+ * $Id: dvdread.c,v 1.1 2001/11/25 05:04:38 stef Exp $
+ *
+ * Authors: Samuel Hocevar <sam@zoy.org>
+ *
+ * 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.
+ *****************************************************************************/
+
+#define MODULE_NAME dvdread
+#include "modules_inner.h"
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include "defs.h"
+
+#include <stdlib.h>                                      /* malloc(), free() */
+#include <string.h>                                              /* strdup() */
+
+#ifdef GOD_DAMN_DMCA
+#   include <dlfcn.h>
+#   include "dummy_dvdcss.h"
+#endif
+
+#include "config.h"
+#include "common.h"                                     /* boolean_t, byte_t */
+#include "threads.h"
+#include "mtime.h"
+
+#include "intf_msg.h"
+
+#include "modules.h"
+#include "modules_export.h"
+
+/*****************************************************************************
+ * Capabilities defined in the other files.
+ *****************************************************************************/
+void _M( input_getfunctions )( function_list_t * p_function_list );
+
+/*****************************************************************************
+ * Local prototypes.
+ *****************************************************************************/
+#ifdef GOD_DAMN_DMCA
+static void *p_libdvdcss;
+static void ProbeLibDVDCSS  ( void );
+static void UnprobeLibDVDCSS( void );
+#endif
+
+/*****************************************************************************
+ * Build configuration tree.
+ *****************************************************************************/
+MODULE_CONFIG_START
+ADD_WINDOW( "Configuration for DVD module" )
+    ADD_COMMENT( "foobar !" )
+MODULE_CONFIG_STOP
+
+MODULE_INIT_START
+    p_module->i_capabilities = MODULE_CAPABILITY_NULL
+                                | MODULE_CAPABILITY_INPUT;
+#ifdef GOD_DAMN_DMCA
+    p_module->psz_longname = "DVD input module, uses libdvdcss if present";
+#else
+    p_module->psz_longname = "DVD input module, linked with libdvdcss";
+#endif
+MODULE_INIT_STOP
+
+MODULE_ACTIVATE_START
+    _M( input_getfunctions )( &p_module->p_functions->input );
+#ifdef GOD_DAMN_DMCA
+    ProbeLibDVDCSS();
+#endif
+MODULE_ACTIVATE_STOP
+
+MODULE_DEACTIVATE_START
+#ifdef GOD_DAMN_DMCA
+    UnprobeLibDVDCSS();
+#endif
+MODULE_DEACTIVATE_STOP
+
+
+/* Following functions are local */
+
+#ifdef GOD_DAMN_DMCA
+/*****************************************************************************
+ * ProbeLibDVDCSS: look for a libdvdcss object.
+ *****************************************************************************
+ * This functions looks for libdvdcss, using dlopen(), and fills function
+ * pointers with what it finds. On failure, uses the dummy libdvdcss
+ * replacement provided by vlc.
+ *****************************************************************************/
+static void ProbeLibDVDCSS( void )
+{
+    char *pp_filelist[4] = { "libdvdcss.so.0",
+                             "./libdvdcss.so.0",
+                             "./lib/libdvdcss.so.0",
+                             NULL };
+    char **pp_file = pp_filelist;
+
+    /* Try to open the dynamic object */
+    do
+    {
+        p_libdvdcss = dlopen( *pp_file, RTLD_LAZY );
+        if( p_libdvdcss != NULL )
+        {
+            intf_WarnMsg( 2, "module: builtin module `dvd' found libdvdcss "
+                             "in `%s'", *pp_file );
+            break;
+        }
+        pp_file++;
+
+    } while( *pp_file != NULL );
+
+    /* If libdvdcss.so was found, check that it's valid */
+    if( p_libdvdcss == NULL )
+    {
+        intf_ErrMsg( "dvd warning: libdvdcss.so.0 not present" );
+    }
+    else
+    {
+        /* Check for libdvdcss 0.0.1 */
+        if( dlsym( p_libdvdcss, "dvdcss_crack" ) != NULL )
+        {
+            intf_ErrMsg( "dvd warning: libdvdcss.so.0 has deprecated symbol "
+                         "dvdcss_crack(), please upgrade" );
+            dlclose( p_libdvdcss );
+            p_libdvdcss = NULL;
+        }
+        else
+        {
+            dvdcss_open = dlsym( p_libdvdcss, "dvdcss_open" );
+            dvdcss_close = dlsym( p_libdvdcss, "dvdcss_close" );
+            dvdcss_title = dlsym( p_libdvdcss, "dvdcss_title" );
+            dvdcss_seek = dlsym( p_libdvdcss, "dvdcss_seek" );
+            dvdcss_read = dlsym( p_libdvdcss, "dvdcss_read" );
+            dvdcss_readv = dlsym( p_libdvdcss, "dvdcss_readv" );
+            dvdcss_error = dlsym( p_libdvdcss, "dvdcss_error" );
+
+            if( dvdcss_open == NULL || dvdcss_close == NULL
+                 || dvdcss_title == NULL || dvdcss_seek == NULL
+                 || dvdcss_read == NULL || dvdcss_readv == NULL
+                 || dvdcss_error == NULL )
+            {
+                intf_ErrMsg( "dvd warning: missing symbols in libdvdcss.so.0, "
+                             "please upgrade libdvdcss or vlc" );
+                dlclose( p_libdvdcss );
+                p_libdvdcss = NULL;
+            }
+        }
+    }
+
+    /* If libdvdcss was not found or was not valid, use the dummy
+     * replacement functions. */
+    if( p_libdvdcss == NULL )
+    {
+        intf_ErrMsg( "dvd warning: no valid libdvdcss found, "
+                     "I will only play unencrypted DVDs" );
+        intf_ErrMsg( "dvd warning: get libdvdcss at "
+                     "http://www.videolan.org/libdvdcss/" );
+
+        dvdcss_open = dummy_dvdcss_open;
+        dvdcss_close = dummy_dvdcss_close;
+        dvdcss_title = dummy_dvdcss_title;
+        dvdcss_seek = dummy_dvdcss_seek;
+        dvdcss_read = dummy_dvdcss_read;
+        dvdcss_readv = dummy_dvdcss_readv;
+        dvdcss_error = dummy_dvdcss_error;
+    }
+}
+
+/*****************************************************************************
+ * UnprobeLibDVDCSS: free resources allocated by ProbeLibDVDCSS, if any.
+ *****************************************************************************/
+static void UnprobeLibDVDCSS( void )
+{
+    if( p_libdvdcss != NULL )
+    {
+        dlclose( p_libdvdcss );
+        p_libdvdcss = NULL;
+    }
+}
+#endif
+
diff --git a/plugins/dvdread/input_dvdread.c b/plugins/dvdread/input_dvdread.c
new file mode 100644 (file)
index 0000000..a284a0c
--- /dev/null
@@ -0,0 +1,1169 @@
+/*****************************************************************************
+ * input_dvdread.c: DvdRead plugin.
+ *****************************************************************************
+ * This plugins should handle all the known specificities of the DVD format,
+ * especially the 2048 bytes logical block size.
+ * It depends on: libdvdread for ifo files and block reading.
+ *****************************************************************************
+ * Copyright (C) 2001 VideoLAN
+ * $Id: input_dvdread.c,v 1.1 2001/11/25 05:04:38 stef Exp $
+ *
+ * Author: Stéphane Borel <stef@via.ecp.fr>
+ *
+ * Some code taken form the play_title.c by Billy Biggs <vektor@dumbterm.net>
+ * in libdvdread.
+ *
+ * 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.
+ *****************************************************************************/
+
+#define MODULE_NAME dvdread
+#include "modules_inner.h"
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include "defs.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_UNISTD_H
+#   include <unistd.h>
+#endif
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#ifdef STRNCASECMP_IN_STRINGS_H
+#   include <strings.h>
+#endif
+
+#if defined( WIN32 )
+#   include <io.h>                                                 /* read() */
+#else
+#   include <sys/uio.h>                                      /* struct iovec */
+#endif
+
+
+#include "config.h"
+#include "common.h"
+#include "threads.h"
+#include "mtime.h"
+#include "iso_lang.h"
+#include "tests.h"
+
+#if defined( WIN32 )
+#   include "input_iovec.h"
+#endif
+
+#include "intf_msg.h"
+
+#include "main.h"
+
+#include "stream_control.h"
+#include "input_ext-intf.h"
+#include "input_ext-dec.h"
+#include "input_ext-plugins.h"
+
+#include "input_dvdread.h"
+
+#include "debug.h"
+
+#include "modules.h"
+#include "modules_export.h"
+
+/* how many blocks DVDRead will read in each loop */
+#define DVD_BLOCK_READ_ONCE 64
+#define DVD_DATA_READ_ONCE  (4 * DVD_BLOCK_READ_ONCE)
+
+/* Size of netlist */
+#define DVD_NETLIST_SIZE    512
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+/* called from outside */
+static int  DvdReadProbe    ( probedata_t *p_data );
+static void DvdReadInit     ( struct input_thread_s * );
+static void DvdReadEnd      ( struct input_thread_s * );
+static void DvdReadOpen     ( struct input_thread_s * );
+static void DvdReadClose    ( struct input_thread_s * );
+static int  DvdReadSetArea  ( struct input_thread_s *, struct input_area_s * );
+static int  DvdReadRead     ( struct input_thread_s *, data_packet_t ** );
+static void DvdReadSeek     ( struct input_thread_s *, off_t );
+static int  DvdReadRewind   ( struct input_thread_s * );
+
+/* called only from here */
+static void DvdReadHandleDSI( thread_dvd_data_t * p_dvd, u8 * p_data );
+static void DvdReadFindCell ( thread_dvd_data_t * p_dvd );
+
+/*****************************************************************************
+ * Functions exported as capabilities. They are declared as static so that
+ * we don't pollute the namespace too much.
+ *****************************************************************************/
+void _M( input_getfunctions )( function_list_t * p_function_list )
+{
+#define input p_function_list->functions.input
+    p_function_list->pf_probe = DvdReadProbe;
+    input.pf_init             = DvdReadInit;
+    input.pf_open             = DvdReadOpen;
+    input.pf_close            = DvdReadClose;
+    input.pf_end              = DvdReadEnd;
+    input.pf_init_bit_stream  = InitBitstream;
+    input.pf_read             = DvdReadRead;
+    input.pf_set_area         = DvdReadSetArea;
+    input.pf_demux            = input_DemuxPS;
+    input.pf_new_packet       = input_NetlistNewPacket;
+    input.pf_new_pes          = input_NetlistNewPES;
+    input.pf_delete_packet    = input_NetlistDeletePacket;
+    input.pf_delete_pes       = input_NetlistDeletePES;
+    input.pf_rewind           = DvdReadRewind;
+    input.pf_seek             = DvdReadSeek;
+#undef input
+}
+
+/*
+ * Data reading functions
+ */
+
+/*****************************************************************************
+ * DvdReadProbe: verifies that the stream is a PS stream
+ *****************************************************************************/
+static int DvdReadProbe( probedata_t *p_data )
+{
+    input_thread_t * p_input = (input_thread_t *)p_data;
+
+    char * psz_name = p_input->p_source;
+    int i_score = 5;
+
+    if( TestMethod( INPUT_METHOD_VAR, "dvdread" ) )
+    {
+        return( 999 );
+    }
+
+    if( ( strlen(psz_name) > 8 ) && !strncasecmp( psz_name, "dvdread:", 8 ) )
+    {
+        /* If the user specified "dvdread:" then he probably wants
+         * to use libdvdread */
+        i_score = 100;
+        psz_name += 4;
+    }
+
+    return( i_score );
+}
+
+/*****************************************************************************
+ * DvdReadInit: initializes DVD structures
+ *****************************************************************************/
+static void DvdReadInit( input_thread_t * p_input )
+{
+    thread_dvd_data_t *  p_dvd;
+    input_area_t *       p_area;
+    int                  i_title;
+    int                  i_chapter;
+    int                  i;
+
+    p_dvd = malloc( sizeof(thread_dvd_data_t) );
+    if( p_dvd == NULL )
+    {
+        intf_ErrMsg( "dvdread error: out of memory" );
+        p_input->b_error = 1;
+        return;
+    }
+
+    /* we take the pointer to dvd_reader_t back  */
+    p_dvd->p_dvdread = (dvd_reader_t *)p_input->p_plugin_data;
+    p_dvd->p_title = NULL;
+    p_dvd->p_vts_file = NULL;
+
+    p_input->p_plugin_data = (void *)p_dvd;
+    p_input->p_method_data = NULL;
+
+    /* We read DVD_BLOCK_READ_ONCE in each loop, so the input will receive
+     * DVD_DATA_READ_ONCE at most */
+    p_dvd->i_block_once = DVD_BLOCK_READ_ONCE;
+    /* this value mustn't be modifed */
+    p_input->i_read_once = DVD_DATA_READ_ONCE;
+
+    /* Reading structures initialisation */
+    input_NetlistInit( p_input,  DVD_NETLIST_SIZE, 2 * DVD_NETLIST_SIZE,
+                   DVD_NETLIST_SIZE, DVD_VIDEO_LB_LEN, p_dvd->i_block_once );
+    intf_WarnMsg( 2, "dvdread info: netlist initialized" );
+
+    /* Ifo allocation & initialisation */
+    if( ! ( p_dvd->p_vmg_file = ifoOpen( p_dvd->p_dvdread, 0 ) ) )
+    {
+        intf_ErrMsg( "dvdread error: can't open VMG info" );
+        DVDClose( p_dvd->p_dvdread );
+        free( p_dvd );
+        p_input->b_error = 1;
+        return;
+    }
+    intf_WarnMsg( 2, "dvdread info: VMG opened" );
+
+    /* Set stream and area data */
+    vlc_mutex_lock( &p_input->stream.stream_lock );
+
+    /* Initialize ES structures */
+    input_InitStream( p_input, sizeof( stream_ps_data_t ) );
+
+    /* disc input method */
+    p_input->stream.i_method = INPUT_METHOD_DVD;
+
+#define tt_srpt p_dvd->p_vmg_file->tt_srpt
+    intf_WarnMsg( 2, "dvdread info: number of titles: %d", tt_srpt->nr_of_srpts );
+
+#define area p_input->stream.pp_areas
+    /* We start from 1 here since the default area 0
+     * is reserved for video_ts.vob */
+    for( i = 1 ; i <= tt_srpt->nr_of_srpts ; i++ )
+    {
+        input_AddArea( p_input );
+
+        /* Titles are Program Chains */
+        area[i]->i_id = i;
+
+        /* Absolute start offset and size
+         * We can only set that with vts ifo, so we do it during the
+         * first call to DVDSetArea */
+        area[i]->i_start = 0;
+        area[i]->i_size = 0;
+
+        /* Number of chapters */
+        area[i]->i_part_nb = tt_srpt->title[i-1].nr_of_ptts;
+        area[i]->i_part = 1;
+
+        /* Number of angles */
+        area[i]->i_angle_nb = 0;
+        area[i]->i_angle = 1;
+
+        area[i]->i_plugin_data = tt_srpt->title[i-1].title_set_nr;
+    }
+#undef area
+
+    /* Get requested title - if none try the first title */
+    i_title = main_GetIntVariable( INPUT_TITLE_VAR, 1 );
+    if( i_title <= 0 || i_title > tt_srpt->nr_of_srpts )
+    {
+        i_title = 1;
+    }
+
+#undef tt_srpt
+
+    /* Get requested chapter - if none defaults to first one */
+    i_chapter = main_GetIntVariable( INPUT_CHAPTER_VAR, 1 );
+    if( i_chapter <= 0 )
+    {
+        i_chapter = 1;
+    }
+
+    p_input->stream.pp_areas[i_title]->i_part = i_chapter;
+
+    p_area = p_input->stream.pp_areas[i_title];
+
+    /* set title, chapter, audio and subpic */
+    DvdReadSetArea( p_input, p_area );
+
+    vlc_mutex_unlock( &p_input->stream.stream_lock );
+
+    return;
+}
+
+/*****************************************************************************
+ * DvdReadOpen: open libdvdread
+ *****************************************************************************/
+static void DvdReadOpen( struct input_thread_s *p_input )
+{
+    dvd_reader_t    *p_dvdread;
+
+    vlc_mutex_lock( &p_input->stream.stream_lock );
+
+    /* If we are here we can control the pace... */
+    p_input->stream.b_pace_control = 1;
+
+    p_input->stream.b_seekable = 1;
+    p_input->stream.p_selected_area->i_size = 0;
+
+    p_input->stream.p_selected_area->i_tell = 0;
+
+    vlc_mutex_unlock( &p_input->stream.stream_lock );
+
+    /* XXX: put this shit in an access plugin */
+    if( strlen( p_input->p_source ) > 8
+         && !strncasecmp( p_input->p_source, "dvdread:", 8 ) )
+    {
+        p_dvdread = DVDOpen( p_input->p_source + 8 );
+    }
+    else
+    {
+        p_dvdread = DVDOpen( p_input->p_source );
+    }
+
+    if( ! p_dvdread )
+    {
+        p_input->b_error = 1;
+        return;
+    }
+
+    /* the vlc input doesn't handle the file descriptor with libdvdread */
+    p_input->i_handle = -1;
+
+    /* p_dvdread is stored in p_plugin_data until we have
+     * malloc'ed a dvd_thread_data_t */
+    p_input->p_plugin_data = (void *) p_dvdread;
+}
+
+/*****************************************************************************
+ * DvdReadClose: close libdvdread
+ *****************************************************************************/
+static void DvdReadClose( struct input_thread_s *p_input )
+{
+    DVDClose( ((thread_dvd_data_t*)p_input->p_plugin_data)->p_dvdread );
+    free( p_input->p_plugin_data );
+    p_input->p_plugin_data = NULL;
+
+}
+
+/*****************************************************************************
+ * DvdReadEnd: frees unused data
+ *****************************************************************************/
+static void DvdReadEnd( input_thread_t * p_input )
+{
+    thread_dvd_data_t *     p_dvd;
+
+    p_dvd = (thread_dvd_data_t *)p_input->p_plugin_data;
+
+    /* close libdvdread */
+    DVDCloseFile( p_dvd->p_title );
+    ifoClose( p_dvd->p_vts_file );
+    ifoClose( p_dvd->p_vmg_file );
+
+    /* Close netlist */
+    input_NetlistEnd( p_input );
+    p_input->p_method_data = NULL;
+}
+
+#define p_pgc         p_dvd->p_cur_pgc
+
+/*****************************************************************************
+ * DvdReadSetArea: initialize input data for title x, chapter y.
+ * It should be called for each user navigation request.
+ *****************************************************************************
+ * Take care that i_title starts from 0 (vmg) and i_chapter start from 1.
+ * Note that you have to take the lock before entering here.
+ *****************************************************************************/
+static int DvdReadSetArea( input_thread_t * p_input, input_area_t * p_area )
+{
+    thread_dvd_data_t *  p_dvd;
+    int                  pgc_id = 0;
+    int                  pgn = 0;
+
+    p_dvd = (thread_dvd_data_t*)p_input->p_plugin_data;
+
+    /* we can't use the interface slider until initilization is complete */
+    p_input->stream.b_seekable = 0;
+
+    if( p_area != p_input->stream.p_selected_area )
+    {
+        es_descriptor_t *    p_es;
+        int                  i_cell = 0;
+        int                  i_audio_nb = 0;
+        int                  i_spu_nb = 0;
+        int                  i;
+
+#define p_vmg         p_dvd->p_vmg_file
+#define p_vts         p_dvd->p_vts_file
+        if( p_dvd->p_title != NULL )
+        {
+            DVDCloseFile( p_dvd->p_title );
+        }
+
+        if( p_vts != NULL )
+        {
+            ifoClose( p_vts );
+        }
+
+        /* Reset the Chapter position of the old title */
+        p_input->stream.p_selected_area->i_part = 1;
+
+        /*
+         *  We have to load all title information
+         */
+        /* Change the default area */
+        p_input->stream.p_selected_area = p_area;
+
+        intf_WarnMsg( 12, "dvdread: open VTS %d, for title %d",
+            p_vmg->tt_srpt->title[ p_area->i_id - 1 ].title_set_nr,
+            p_area->i_id );
+
+        /* ifo vts */
+        if( ! ( p_vts = ifoOpen( p_dvd->p_dvdread,
+                p_vmg->tt_srpt->title[ p_area->i_id - 1 ].title_set_nr ) ) )
+        {
+            intf_ErrMsg( "dvdread error: fatal error in vts ifo" );
+            ifoClose( p_vmg );
+            DVDClose( p_dvd->p_dvdread );
+            p_input->b_error = 1;
+            return -1;
+        }
+
+        /* title position inside the selected vts */
+        p_dvd->i_ttn = p_vmg->tt_srpt->title[ p_area->i_id - 1 ].vts_ttn;
+
+        /*
+         * Set selected title start
+         */
+        pgc_id = p_vts->vts_ptt_srpt->title[p_dvd->i_ttn-1].ptt[0].pgcn;
+        pgn = p_vts->vts_ptt_srpt->title[p_dvd->i_ttn-1].ptt[0].pgn;
+        p_pgc = p_vts->vts_pgcit->pgci_srp[ pgc_id - 1 ].pgc;
+        i_cell = p_pgc->program_map[ pgn - 1 ] - 1;
+
+        p_area->i_start =
+            LB2OFF( p_dvd->p_cur_pgc->cell_playback[ i_cell ].first_sector );
+
+        intf_WarnMsg( 3, "dvdread: start %d vts_title %d pgc %d pgn %d",
+                         p_area->i_id, p_dvd->i_ttn, pgc_id, pgn );
+
+        /*
+         * Find title end
+         */
+        i_cell = p_dvd->p_cur_pgc->nr_of_cells - 1;
+
+        p_dvd->i_end_block = p_pgc->cell_playback[ i_cell ].last_sector;
+        p_area->i_size = LB2OFF( p_dvd->i_end_block )- p_area->i_start;
+
+        intf_WarnMsg( 12, "dvdread: start %lld size %lld end %d",
+                          p_area->i_start , p_area->i_size, p_dvd->i_end_block );
+
+        /*
+         * Set properties for current chapter
+         */
+        /* Remeber current chapter */
+        p_dvd->i_chapter = p_area->i_part;
+        p_dvd->b_eoc = 0;
+
+        pgc_id = p_vts->vts_ptt_srpt->title[
+                    p_dvd->i_ttn-1].ptt[p_area->i_part-1].pgcn;
+        pgn = p_vts->vts_ptt_srpt->title[
+                    p_dvd->i_ttn-1].ptt[p_area->i_part-1].pgn;
+
+        p_pgc = p_vts->vts_pgcit->pgci_srp[pgc_id-1].pgc;
+        p_dvd->i_pack_len = 0;
+        p_dvd->i_next_cell = p_dvd->i_cur_cell = p_pgc->program_map[pgn-1] - 1;
+        DvdReadFindCell( p_dvd );
+
+        p_dvd->i_next_vobu = p_dvd->i_cur_block =
+            p_pgc->cell_playback[p_dvd->i_cur_cell].first_sector;
+
+        /*
+         * Angle management
+         */
+        p_area->i_angle_nb = p_vmg->tt_srpt->title[p_area->i_id-1].nr_of_angles;
+        p_area->i_angle = main_GetIntVariable( INPUT_ANGLE_VAR, 1 );
+
+        if( ( p_area->i_angle <= 0 ) || p_area->i_angle > p_area->i_angle_nb )
+        {
+            p_area->i_angle = 1;
+        }
+        p_dvd->i_angle = p_area->i_angle;
+        p_dvd->i_angle_nb = p_area->i_angle_nb;
+
+        /*
+         * We've got enough info, time to open the title set data.
+         */
+        if( ! ( p_dvd->p_title = DVDOpenFile( p_dvd->p_dvdread,
+            p_vmg->tt_srpt->title[ p_area->i_id - 1 ].title_set_nr,
+            DVD_READ_TITLE_VOBS ) ) )
+        {
+            intf_ErrMsg( "dvdread error: can't open title (VTS_%02d_1.VOB)",
+                         p_vmg->tt_srpt->title[p_area->i_id-1].title_set_nr );
+            ifoClose( p_vts );
+            ifoClose( p_vmg );
+            DVDClose( p_dvd->p_dvdread );
+            return -1;
+        }
+
+//        IfoPrintTitle( p_dvd );
+
+        /*
+         * Destroy obsolete ES by reinitializing program 0
+         * and find all ES in title with ifo data
+         */
+        if( p_input->stream.pp_programs != NULL )
+        {
+            /* We don't use input_EndStream here since
+             * we keep area structures */
+
+            for( i = 0 ; i < p_input->stream.i_selected_es_number ; i++ )
+            {
+                input_UnselectES( p_input, p_input->stream.pp_selected_es[i] );
+            }
+
+            free( p_input->stream.pp_selected_es );
+            input_DelProgram( p_input, p_input->stream.pp_programs[0] );
+
+            p_input->stream.pp_selected_es = NULL;
+            p_input->stream.i_selected_es_number = 0;
+        }
+
+        input_AddProgram( p_input, 0, sizeof( stream_ps_data_t ) );
+
+        /* No PSM to read in DVD mode, we already have all information */
+        p_input->stream.pp_programs[0]->b_is_ok = 1;
+
+        p_es = NULL;
+
+        /* ES 0 -> video MPEG2 */
+//        IfoPrintVideo( p_dvd );
+
+        p_es = input_AddES( p_input, p_input->stream.pp_programs[0], 0xe0, 0 );
+        p_es->i_stream_id = 0xe0;
+        p_es->i_type = MPEG2_VIDEO_ES;
+        p_es->i_cat = VIDEO_ES;
+        if( p_main->b_video )
+        {
+            input_SelectES( p_input, p_es );
+        }
+
+#define audio_control \
+    p_dvd->p_vts_file->vts_pgcit->pgci_srp[pgc_id-1].pgc->audio_control[i-1]
+        /* Audio ES, in the order they appear in .ifo */
+        for( i = 1 ; i <= p_vts->vtsi_mat->nr_of_vts_audio_streams ; i++ )
+        {
+            int i_position = 0;
+            u16 i_id;
+
+//            IfoPrintAudio( p_dvd, i );
+
+            /* audio channel is active if first byte is 0x80 */
+            if( audio_control & 0x8000 )
+            {
+                i_audio_nb++;
+                i_position = ( audio_control & 0x7F00 ) >> 8;
+
+            intf_WarnMsg( 12, "dvd audio position  %d", i_position );
+                switch( p_vts->vtsi_mat->vts_audio_attr[i-1].audio_format )
+                {
+                case 0x00:              /* AC3 */
+                    i_id = ( ( 0x80 + i_position ) << 8 ) | 0xbd;
+                    p_es = input_AddES( p_input,
+                               p_input->stream.pp_programs[0], i_id, 0 );
+                    p_es->i_stream_id = 0xbd;
+                    p_es->i_type = AC3_AUDIO_ES;
+                    p_es->b_audio = 1;
+                    p_es->i_cat = AUDIO_ES;
+                    strcpy( p_es->psz_desc, DecodeLanguage( hton16(
+                        p_vts->vtsi_mat->vts_audio_attr[i-1].lang_code ) ) ); 
+                    strcat( p_es->psz_desc, " (ac3)" );
+
+                    break;
+                case 0x02:
+                case 0x03:              /* MPEG audio */
+                    i_id = 0xc0 + i_position;
+                    p_es = input_AddES( p_input,
+                                    p_input->stream.pp_programs[0], i_id, 0 );
+                    p_es->i_stream_id = i_id;
+                    p_es->i_type = MPEG2_AUDIO_ES;
+                    p_es->b_audio = 1;
+                    p_es->i_cat = AUDIO_ES;
+                    strcpy( p_es->psz_desc, DecodeLanguage( hton16(
+                        p_vts->vtsi_mat->vts_audio_attr[i-1].lang_code ) ) ); 
+                    strcat( p_es->psz_desc, " (mpeg)" );
+
+                    break;
+                case 0x04:              /* LPCM */
+
+                    i_id = ( ( 0xa0 + i_position ) << 8 ) | 0xbd;
+                    p_es = input_AddES( p_input,
+                                    p_input->stream.pp_programs[0], i_id, 0 );
+                    p_es->i_stream_id = i_id;
+                    p_es->i_type = LPCM_AUDIO_ES;
+                    p_es->b_audio = 1;
+                    p_es->i_cat = AUDIO_ES;
+                    strcpy( p_es->psz_desc, DecodeLanguage( hton16(
+                        p_vts->vtsi_mat->vts_audio_attr[i-1].lang_code ) ) ); 
+                    strcat( p_es->psz_desc, " (lpcm)" );
+
+                    break;
+                case 0x06:              /* DTS */
+                    i_id = ( ( 0x88 + i_position ) << 8 ) | 0xbd;
+                    intf_ErrMsg( "dvd warning: DTS audio not handled yet"
+                                 "(0x%x)", i_id );
+                    break;
+                default:
+                    i_id = 0;
+                    intf_ErrMsg( "dvd warning: unknown audio type %.2x",
+                             p_vts->vtsi_mat->vts_audio_attr[i-1].audio_format );
+                }
+            }
+        }
+#undef audio_control
+#define spu_control \
+    p_dvd->p_vts_file->vts_pgcit->pgci_srp[pgc_id-1].pgc->subp_control[i-1]
+
+        /* Sub Picture ES */
+
+        for( i = 1 ; i <= p_vts->vtsi_mat->nr_of_vts_subp_streams; i++ )
+        {
+            int i_position = 0;
+            u16 i_id;
+
+//            IfoPrintSpu( p_dvd, i );
+            intf_WarnMsg( 12, "dvd spu %d 0x%02x", i, spu_control );
+
+            if( spu_control & 0x80000000 )
+            {
+                i_spu_nb++;
+
+                /*  there are several streams for one spu */
+                if(  p_vts->vtsi_mat->vts_video_attr.display_aspect_ratio )
+                {
+                    /* 16:9 */
+                    switch( p_vts->vtsi_mat->vts_video_attr.permitted_df )
+                    {
+                    case 1:
+                        i_position = spu_control & 0xff;
+                        break;
+                    case 2:
+                        i_position = ( spu_control >> 8 ) & 0xff;
+                        break;
+                    default:
+                        i_position = ( spu_control >> 16 ) & 0xff;
+                        break;
+                    }
+                }
+                else
+                {
+                    /* 4:3 */
+                    i_position = ( spu_control >> 24 ) & 0x7F;
+                }
+
+                i_id = ( ( 0x20 + i_position ) << 8 ) | 0xbd;
+                p_es = input_AddES( p_input,
+                                    p_input->stream.pp_programs[0], i_id, 0 );
+                p_es->i_stream_id = 0xbd;
+                p_es->i_type = DVD_SPU_ES;
+                p_es->i_cat = SPU_ES;
+                strcpy( p_es->psz_desc, DecodeLanguage( hton16(
+                    p_vts->vtsi_mat->vts_subp_attr[i-1].lang_code ) ) ); 
+            }
+        }
+#undef spu_control
+        if( p_main->b_audio )
+        {
+            /* For audio: first one if none or a not existing one specified */
+            int i_audio = main_GetIntVariable( INPUT_CHANNEL_VAR, 1 );
+            if( i_audio < 0 || i_audio > i_audio_nb )
+            {
+                main_PutIntVariable( INPUT_CHANNEL_VAR, 1 );
+                i_audio = 1;
+            }
+            if( i_audio > 0 && i_audio_nb > 0 )
+            {
+                if( main_GetIntVariable( AOUT_SPDIF_VAR, 0 ) ||
+                    ( main_GetIntVariable( INPUT_AUDIO_VAR, 0 ) ==
+                      REQUESTED_AC3 ) )
+                {
+                    int     i_ac3 = i_audio;
+                    while( ( p_input->stream.pp_es[i_ac3]->i_type !=
+                             AC3_AUDIO_ES ) && ( i_ac3 <=
+                             p_vts->vtsi_mat->nr_of_vts_audio_streams ) )
+                    {
+                        i_ac3++;
+                    }
+                    if( p_input->stream.pp_es[i_ac3]->i_type == AC3_AUDIO_ES )
+                    {
+                        input_SelectES( p_input,
+                                        p_input->stream.pp_es[i_ac3] );
+                    }
+                }
+                else
+                {
+                    input_SelectES( p_input,
+                                    p_input->stream.pp_es[i_audio] );
+                }
+            }
+        }
+
+        if( p_main->b_video )
+        {
+            /* for spu, default is none */
+            int i_spu = main_GetIntVariable( INPUT_SUBTITLE_VAR, 0 );
+            if( i_spu < 0 || i_spu > i_spu_nb )
+            {
+                main_PutIntVariable( INPUT_SUBTITLE_VAR, 0 );
+                i_spu = 0;
+            }
+            if( i_spu > 0 && i_spu_nb > 0 )
+            {
+                i_spu += p_vts->vtsi_mat->nr_of_vts_audio_streams;
+                input_SelectES( p_input, p_input->stream.pp_es[i_spu] );
+            }
+        }
+    } /* i_title >= 0 */
+    else
+    {
+        p_area = p_input->stream.p_selected_area;
+    }
+
+    /*
+     * Chapter selection
+     */
+
+    if( p_area->i_part != p_dvd->i_chapter )
+    {
+        if( ( p_area->i_part > 0 ) &&
+            ( p_area->i_part <= p_area->i_part_nb ))
+        {
+            p_dvd->i_ttn = p_vmg->tt_srpt->title[p_area->i_id-1].vts_ttn;
+            pgc_id = p_vts->vts_ptt_srpt->title[
+                        p_dvd->i_ttn-1].ptt[p_area->i_part-1].pgcn;
+            pgn = p_vts->vts_ptt_srpt->title[
+                        p_dvd->i_ttn-1].ptt[p_area->i_part-1].pgn;
+
+            p_pgc = p_vts->vts_pgcit->pgci_srp[ pgc_id - 1 ].pgc;
+
+            p_dvd->i_cur_cell = p_pgc->program_map[ pgn - 1 ] - 1;
+            p_dvd->i_chapter = p_area->i_part;
+            DvdReadFindCell( p_dvd );
+
+            p_dvd->i_pack_len = 0;
+            p_dvd->i_next_vobu = p_dvd->i_cur_block =
+                    p_pgc->cell_playback[p_dvd->i_cur_cell].first_sector;
+        }
+        else
+        {
+            p_area->i_part = p_dvd->i_chapter;
+        }
+    }
+#undef p_vts
+#undef p_vmg
+
+    if( p_area->i_angle != p_dvd->i_angle )
+    {
+        p_dvd->i_angle = p_area->i_angle;
+
+        intf_WarnMsg( 3, "dvd info: angle %d selected", p_area->i_angle );
+    }
+    /* warn interface that something has changed */
+    p_area->i_tell = LB2OFF( p_dvd->i_next_vobu ) - p_area->i_start;
+    p_input->stream.b_seekable = 1;
+    p_input->stream.b_changed = 1;
+
+    return 0;
+}
+
+
+/*****************************************************************************
+ * DvdReadRead: reads data packets into the netlist.
+ *****************************************************************************
+ * Returns -1 in case of error, 0 if everything went well, and 1 in case of
+ * EOF.
+ *****************************************************************************/
+static int DvdReadRead( input_thread_t * p_input,
+                        data_packet_t ** pp_packets )
+{
+    thread_dvd_data_t *     p_dvd;
+    netlist_t *             p_netlist;
+    u8                      p_data[DVD_VIDEO_LB_LEN];
+    struct iovec *          p_vec;
+    struct data_packet_s *  pp_data[DVD_DATA_READ_ONCE];
+    u8 *                    pi_cur;
+    int                     i_blocks;
+    int                     i_read;
+    int                     i_iovec;
+    int                     i_packet_size;
+    int                     i_packet;
+    int                     i_pos;
+
+    p_dvd = (thread_dvd_data_t *)p_input->p_plugin_data;
+    p_netlist = (netlist_t *)p_input->p_method_data;
+
+    /*
+     * Playback by cell in this pgc, starting at the cell for our chapter.
+     */
+
+    /* 
+     * End of pack, we select the following one
+     */
+    if( ! p_dvd->i_pack_len )
+    {
+        /*
+         * Read NAV packet.
+         */
+        if( ( i_read = DVDReadBlocks( p_dvd->p_title, p_dvd->i_next_vobu,
+                       1, p_data ) ) != 1 )
+        {
+            intf_ErrMsg( "dvdread error: read failed for block %d",
+                         p_dvd->i_next_vobu );
+            return -1;
+        }
+
+        assert( p_data[41] == 0xbf && p_data[1027] == 0xbf );
+
+        /*
+         * Parse the contained dsi packet.
+         */
+
+        DvdReadHandleDSI( p_dvd, p_data );
+
+        /* End of File */
+        if( p_dvd->i_next_vobu >= p_dvd->i_end_block + 1 )
+        {
+            return 1;
+        }
+
+        assert( p_dvd->i_pack_len < 1024 );
+        /* Ugly kludge: we send the pack block to the input for it
+         * sometimes has a zero scr and restart the sync */
+//        p_dvd->i_cur_block ++;
+        p_dvd->i_pack_len++;
+
+    }
+
+    /*
+     * Compute the number of blocks to read
+     */
+    i_blocks = p_dvd->i_pack_len >= DVD_BLOCK_READ_ONCE
+             ? DVD_BLOCK_READ_ONCE : p_dvd->i_pack_len;
+    p_dvd->i_pack_len -= i_blocks;
+    p_netlist->i_read_once = i_blocks;
+
+    /* Get an iovec pointer */
+    if( ( p_vec = input_NetlistGetiovec( p_netlist ) ) == NULL )
+    {
+        intf_ErrMsg( "dvdread error: can't get iovec" );
+        return -1;
+    }
+
+    /* Reads from DVD */
+    i_read = DVDReadVBlocks( p_dvd->p_title, p_dvd->i_cur_block,
+                             i_blocks, p_vec );
+    if( i_read != i_blocks )
+    {
+        intf_ErrMsg( "dvdread error: read failed for %d/%d blocks at 0x%02x",
+                     i_read, i_blocks, p_dvd->i_cur_block );
+        return -1;
+    }
+
+    p_dvd->i_cur_block += i_read;
+/*
+    intf_WarnMsg( 12, "dvdread i_blocks: %d len: %d current: 0x%02x", i_read, p_dvd->i_pack_len, p_dvd->i_cur_block );
+*/
+    /* Update netlist indexes: we don't do it in DVDGetiovec since we
+     * need know the real number of blocks read */
+    input_NetlistMviovec( p_netlist, i_read, pp_data );
+
+    i_packet = 0;
+
+    /* Read headers to compute payload length */
+    for( i_iovec = 0 ; i_iovec < i_read ; i_iovec++ )
+    {
+        i_pos = 0;
+
+        while( i_pos < p_netlist->i_buffer_size )
+        {
+            pi_cur = (u8*)p_vec[i_iovec].iov_base + i_pos;
+
+            /*default header */
+            if( U32_AT( pi_cur ) != 0x1BA )
+            {
+                /* That's the case for all packets, except pack header. */
+                i_packet_size = U16_AT( pi_cur + 4 );
+                pp_packets[i_packet] = input_NetlistNewPtr( p_netlist );
+                (*pp_data[i_iovec]->pi_refcount)++;
+                pp_packets[i_packet]->pi_refcount =
+                    pp_data[i_iovec]->pi_refcount;
+                pp_packets[i_packet]->p_buffer = pp_data[i_iovec]->p_buffer;
+            }
+            else
+            {
+                /* MPEG-2 Pack header. */
+                i_packet_size = 8;
+                pp_packets[i_packet] = pp_data[i_iovec];
+
+            }
+
+            pp_packets[i_packet]->p_payload_start =
+                    pp_packets[i_packet]->p_buffer + i_pos;
+
+            pp_packets[i_packet]->p_payload_end =
+                    pp_packets[i_packet]->p_payload_start + i_packet_size + 6;
+
+            pp_packets[i_packet]->p_next = NULL;
+            pp_packets[i_packet]->b_discard_payload = 0;
+
+            i_packet++;
+            i_pos += i_packet_size + 6;
+        }
+    }
+
+    pp_packets[i_packet] = NULL;
+
+    vlc_mutex_lock( &p_input->stream.stream_lock );
+
+    p_input->stream.p_selected_area->i_tell =
+        LB2OFF( p_dvd->i_cur_block ) -
+            p_input->stream.p_selected_area->i_start;
+
+    if( p_dvd->b_eoc )
+    {
+        /* We modify i_part only at end of chapter not to erase
+         * some modification from the interface */
+        p_input->stream.p_selected_area->i_part = p_dvd->i_chapter;
+        p_dvd->b_eoc = 0;
+    }
+
+
+    vlc_mutex_unlock( &p_input->stream.stream_lock );
+
+    return 0;
+}
+#undef p_pgc
+
+/*****************************************************************************
+ * DVDRewind : reads a stream backward
+ *****************************************************************************/
+static int DvdReadRewind( input_thread_t * p_input )
+{
+    return( -1 );
+}
+
+/*****************************************************************************
+ * DvdReadSeek : Goes to a given position on the stream.
+ *****************************************************************************
+ * This one is used by the input and translate chronological position from
+ * input to logical position on the device.
+ * The lock should be taken before calling this function.
+ *****************************************************************************/
+static void DvdReadSeek( input_thread_t * p_input, off_t i_off )
+{
+    thread_dvd_data_t *     p_dvd;
+    int                     i_lb;
+    int                     i_tmp;
+    int                     i_chapter = 0;
+    int                     i_cell = 0;
+    int                     i_vobu = 0;
+    int                     i_sub_cell = 0;
+
+    i_off += p_input->stream.p_selected_area->i_start;
+    i_lb = OFF2LB( i_off );
+    p_dvd = ( thread_dvd_data_t * )p_input->p_plugin_data;
+
+    /* find cell */
+    while( p_dvd->p_cur_pgc->cell_playback[i_cell].last_sector < i_lb )
+    {
+        i_cell++;
+    }
+
+    /* find chapter */
+    do
+    {
+        pgc_t *     p_pgc;
+        int         pgc_id, pgn;
+
+        i_chapter++;
+        pgc_id = p_dvd->p_vts_file->vts_ptt_srpt->title[
+                    p_dvd->i_ttn-1].ptt[i_chapter].pgcn;
+        pgn = p_dvd->p_vts_file->vts_ptt_srpt->title[
+                    p_dvd->i_ttn-1].ptt[i_chapter].pgn;
+
+        p_pgc = p_dvd->p_vts_file->vts_pgcit->pgci_srp[pgc_id-1].pgc;
+        i_tmp = p_pgc->program_map[pgn-1];
+
+    } while( i_tmp <= i_cell );
+
+    /* find vobu */
+    while( p_dvd->p_vts_file->vts_vobu_admap->vobu_start_sectors[i_vobu]
+            <= i_lb )
+    {
+        i_vobu++;
+    }
+
+    /* find sub_cell */
+    while( p_dvd->p_vts_file->vts_c_adt->cell_adr_table[i_sub_cell].start_sector <
+            p_dvd->p_vts_file->vts_vobu_admap->vobu_start_sectors[i_vobu-1] )
+    {
+        i_sub_cell++;
+    }
+
+/*
+    intf_WarnMsg(12, "cell %d i_sub_cell %d chapter %d vobu %d cell_sector %d vobu_sector %d sub_cell_sector %d",
+            i_cell, i_sub_cell,i_chapter, i_vobu,
+            p_dvd->p_cur_pgc->cell_playback[i_cell].first_sector,
+            p_dvd->p_vts_file->vts_vobu_admap->vobu_start_sectors[i_vobu],
+            p_dvd->p_vts_file->vts_c_adt->cell_adr_table[i_sub_cell-1].start_sector);
+*/
+    p_dvd->i_cur_block = i_lb;
+    p_dvd->i_next_vobu =
+        p_dvd->p_vts_file->vts_vobu_admap->vobu_start_sectors[i_vobu];
+    p_dvd->i_pack_len = p_dvd->i_next_vobu - i_lb;
+    p_dvd->i_cur_cell = i_cell;
+    p_dvd->i_chapter = i_chapter;
+    DvdReadFindCell( p_dvd );
+
+    p_input->stream.p_selected_area->i_tell =
+        LB2OFF ( p_dvd->i_cur_block )
+         - p_input->stream.p_selected_area->i_start;
+    p_input->stream.p_selected_area->i_part = p_dvd->i_chapter;
+
+    return;
+}
+
+/*****************************************************************************
+ * DvdReadHandleDSI
+ *****************************************************************************/
+static void DvdReadHandleDSI( thread_dvd_data_t * p_dvd, u8 * p_data )
+{
+    navRead_DSI( &(p_dvd->dsi_pack), &(p_data[ DSI_START_BYTE ]) );
+
+    /*
+     * Determine where we go next.  These values are the ones we mostly
+     * care about.
+     */
+    p_dvd->i_cur_block = p_dvd->dsi_pack.dsi_gi.nv_pck_lbn;
+
+    /*
+     * If we're not at the end of this cell, we can determine the next
+     * VOBU to display using the VOBU_SRI information section of the
+     * DSI.  Using this value correctly follows the current angle,
+     * avoiding the doubled scenes in The Matrix, and makes our life
+     * really happy.
+     */
+    if( p_dvd->dsi_pack.vobu_sri.next_vobu != SRI_END_OF_CELL )
+    {
+#if 1
+        switch( ( p_dvd->dsi_pack.sml_pbi.category & 0xf000 ) >> 12 )
+        {
+            case 0x4:
+                /* interleaved unit with no angle */
+                if( p_dvd->dsi_pack.sml_pbi.ilvu_sa != -1 )
+                {
+                    p_dvd->i_next_vobu = p_dvd->i_cur_block +
+                        p_dvd->dsi_pack.sml_pbi.ilvu_sa;
+                    p_dvd->i_pack_len = p_dvd->dsi_pack.sml_pbi.ilvu_ea;
+                }
+                else
+                {
+                    p_dvd->i_next_vobu = p_dvd->i_cur_block +
+                        p_dvd->dsi_pack.dsi_gi.vobu_ea + 1;
+                    p_dvd->i_pack_len = p_dvd->dsi_pack.dsi_gi.vobu_ea;
+                }
+                break;
+            case 0x5:
+                /* vobu is end of ilvu */
+                if( p_dvd->dsi_pack.sml_agli.data[p_dvd->i_angle-1].address )
+                {
+                    p_dvd->i_next_vobu = p_dvd->i_cur_block +
+                        p_dvd->dsi_pack.sml_agli.data[p_dvd->i_angle-1].address;
+                    p_dvd->i_pack_len = p_dvd->dsi_pack.sml_pbi.ilvu_ea;
+
+                    break;
+                }
+            case 0x6:
+                /* vobu is beginning of ilvu */
+            case 0x9:
+                /* next scr is 0 */
+            case 0xa:
+                /* entering interleaved section */
+            case 0x8:
+                /* non interleaved cells in interleaved section */
+            default:
+                p_dvd->i_next_vobu = p_dvd->i_cur_block +
+                    ( p_dvd->dsi_pack.vobu_sri.next_vobu & 0x7fffffff );
+                p_dvd->i_pack_len = p_dvd->dsi_pack.dsi_gi.vobu_ea;
+                break;
+        }
+#else
+        p_dvd->i_next_vobu = p_dvd->i_cur_block +
+            ( p_dvd->dsi_pack.vobu_sri.next_vobu & 0x7fffffff );
+        p_dvd->i_pack_len = p_dvd->dsi_pack.dsi_gi.vobu_ea;
+#endif
+    }
+    else
+    {
+        p_dvd->i_cur_cell = p_dvd->i_next_cell;
+        DvdReadFindCell( p_dvd );
+
+        p_dvd->i_pack_len = p_dvd->dsi_pack.dsi_gi.vobu_ea;
+        p_dvd->i_next_vobu =
+            p_dvd->p_cur_pgc->cell_playback[p_dvd->i_cur_cell].first_sector;
+    }
+
+#if 0
+    intf_WarnMsg( 12, "scr %d lbn 0x%02x vobu_ea %d vob_id %d c_id %d",
+            p_dvd->dsi_pack.dsi_gi.nv_pck_scr,
+            p_dvd->dsi_pack.dsi_gi.nv_pck_lbn,
+            p_dvd->dsi_pack.dsi_gi.vobu_ea,
+            p_dvd->dsi_pack.dsi_gi.vobu_vob_idn,
+            p_dvd->dsi_pack.dsi_gi.vobu_c_idn );
+
+    intf_WarnMsg( 12, "cat 0x%02x ilvu_ea %d ilvu_sa %d size %d", 
+            p_dvd->dsi_pack.sml_pbi.category,
+            p_dvd->dsi_pack.sml_pbi.ilvu_ea,
+            p_dvd->dsi_pack.sml_pbi.ilvu_sa,
+            p_dvd->dsi_pack.sml_pbi.size );
+
+    intf_WarnMsg( 12, "next_vobu %d next_ilvu1 %d next_ilvu2 %d",
+            p_dvd->dsi_pack.vobu_sri.next_vobu & 0x7fffffff,
+            p_dvd->dsi_pack.sml_agli.data[ p_dvd->i_angle - 1 ].address,
+            p_dvd->dsi_pack.sml_agli.data[ p_dvd->i_angle ].address);
+#endif
+}
+
+/*****************************************************************************
+ * DvdReadFindCell
+ *****************************************************************************/
+static void DvdReadFindCell( thread_dvd_data_t * p_dvd )
+{
+    int         pgc_id, pgn;
+    int         i = 0;
+    pgc_t *     p_pgc;
+#define cell p_dvd->p_cur_pgc->cell_playback
+    if( cell[p_dvd->i_cur_cell].block_type == BLOCK_TYPE_ANGLE_BLOCK )
+    {
+#if 0
+        p_dvd->i_next_cell = p_dvd->i_cur_cell + p_dvd->i_angle_nb;
+        p_dvd->i_cur_cell += p_dvd->i_angle - 1;
+#else
+        p_dvd->i_cur_cell += p_dvd->i_angle - 1;
+
+        while( cell[p_dvd->i_cur_cell+i].block_mode != BLOCK_MODE_LAST_CELL )
+        {
+            i++;
+        }
+        p_dvd->i_next_cell = p_dvd->i_cur_cell + i + 1;
+#endif
+    }
+    else
+    {
+        p_dvd->i_next_cell = p_dvd->i_cur_cell + 1;
+    }
+#undef cell
+    pgc_id = p_dvd->p_vts_file->vts_ptt_srpt->title[
+                p_dvd->i_ttn-1].ptt[p_dvd->i_chapter].pgcn;
+    pgn = p_dvd->p_vts_file->vts_ptt_srpt->title[
+                p_dvd->i_ttn-1].ptt[p_dvd->i_chapter].pgn;
+    p_pgc = p_dvd->p_vts_file->vts_pgcit->pgci_srp[pgc_id-1].pgc;
+
+    if( p_pgc->program_map[pgn-1] <= p_dvd->i_cur_cell )
+    {
+        p_dvd->i_chapter++;
+       p_dvd->b_eoc = 1;
+    }
+}
diff --git a/plugins/dvdread/input_dvdread.h b/plugins/dvdread/input_dvdread.h
new file mode 100644 (file)
index 0000000..0f66a6f
--- /dev/null
@@ -0,0 +1,73 @@
+/*****************************************************************************
+ * input_dvdread.h: thread structure of the DVD plugin
+ *****************************************************************************
+ * Copyright (C) 1999-2001 VideoLAN
+ * $Id: input_dvdread.h,v 1.1 2001/11/25 05:04:38 stef Exp $
+ *
+ * Author: Stéphane Borel <stef@via.ecp.fr>
+ *
+ * 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.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+/* dvdread includes */
+#include "dvd_reader.h"
+#include "ifo_types.h"
+#include "ifo_read.h"
+#include "dvd_udf.h"
+#include "nav_read.h"
+#include "nav_print.h"
+#include "videolan/dvdread.h"
+
+/* Logical block size for DVD-VIDEO */
+#define LB2OFF(x) ((off_t)(x) * (off_t)(DVD_VIDEO_LB_LEN))
+#define OFF2LB(x) ((x) >> 11)
+
+/*****************************************************************************
+ * thread_dvd_data_t: extension of input_thread_t for DVD specificity.
+ *****************************************************************************/
+typedef struct thread_dvd_data_s
+{
+    dvd_reader_t *          p_dvdread;
+    dvd_file_t *            p_title;
+
+    ifo_handle_t *          p_vmg_file;
+    ifo_handle_t *          p_vts_file;
+            
+    tt_srpt_t *             p_tt_srpt;
+    pgc_t *                 p_cur_pgc;
+
+    dsi_t                   dsi_pack;
+
+    int                     i_ttn;
+    
+    unsigned int            i_pack_len;
+    unsigned int            i_cur_block;
+    unsigned int            i_next_vobu;
+    unsigned int            i_end_block;
+
+    int                     i_cur_cell;
+    int                     i_next_cell;
+    int                     i_chapter;
+    boolean_t               b_eoc;
+    int                     i_angle_nb;
+    int                     i_angle;
+
+    int                     i_block_once;       // Nb of block read once
+} thread_dvd_data_t;
+
index be51d8a5456d74f971d66331f6fb7a8047fc2c54..e69cab1d19e521e54e657ebc48c272ed3b4a9910 100644 (file)
@@ -4,7 +4,7 @@
  * decoders.
  *****************************************************************************
  * Copyright (C) 1998, 1999, 2000 VideoLAN
- * $Id: input.c,v 1.158 2001/11/23 18:47:51 massiot Exp $
+ * $Id: input.c,v 1.159 2001/11/25 05:04:38 stef Exp $
  *
  * Authors: Christophe Massiot <massiot@via.ecp.fr>
  *
@@ -445,6 +445,14 @@ static int InitThread( input_thread_t * p_input )
         f.pf_open( p_input );
         p_input->stream.i_method = INPUT_METHOD_DVD;
     }
+    else if( ( ( strlen( p_input->p_source ) > 8 )
+                 && !strncasecmp( p_input->p_source, "dvdread:", 8 ) )
+              || TestMethod( INPUT_METHOD_VAR, "dvdread" ) )
+    {
+        /* DVDRead - this is THE kludge */
+        f.pf_open( p_input );
+        p_input->stream.i_method = INPUT_METHOD_DVD;
+    }
     else if( ( strlen( p_input->p_source ) > 4 )
                && !strncasecmp( p_input->p_source, "vlc:", 4 ) )
     {
@@ -637,7 +645,14 @@ static void FileOpen( input_thread_t * p_input )
     {
         int i_size = strlen( psz_name );
 
-        if( ( i_size > 4 )
+        if( ( i_size > 8 )
+            && !strncasecmp( psz_name, "dvdread:", 8 ) )
+        {
+            /* get rid of the 'dvdread:' stuff and try again */
+            psz_name += 8;
+            i_stat = stat( psz_name, &stat_info );
+        }
+       else if( ( i_size > 4 )
             && !strncasecmp( psz_name, "dvd:", 4 ) )
         {
             /* get rid of the 'dvd:' stuff and try again */