aboutsummaryrefslogtreecommitdiffhomepage
path: root/dvdread
diff options
context:
space:
mode:
authorGravatar diego <diego@b3059339-0415-0410-9bf9-f77b7e298cf2>2006-11-18 00:33:01 +0000
committerGravatar diego <diego@b3059339-0415-0410-9bf9-f77b7e298cf2>2006-11-18 00:33:01 +0000
commit8cf4397274d9f5cf35f1961d154ebb811f4f0f2f (patch)
treea1f3c9c8a20486295a018a3f82b688c1dc562ddd /dvdread
parent2f67bb7b34ce9e81b62c16c849cf4cd48753c1df (diff)
Rename libdvdread to dvdread. We really only include only the dvdread
subdirectory of libdvdread. This will also allow getting rid of some local modifications. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@20997 b3059339-0415-0410-9bf9-f77b7e298cf2
Diffstat (limited to 'dvdread')
-rw-r--r--dvdread/Makefile53
-rw-r--r--dvdread/bswap.h146
-rw-r--r--dvdread/dvd_input.c353
-rw-r--r--dvdread/dvd_input.h47
-rw-r--r--dvdread/dvd_reader.c1149
-rw-r--r--dvdread/dvd_reader.h265
-rw-r--r--dvdread/dvd_udf.c1002
-rw-r--r--dvdread/dvd_udf.h59
-rw-r--r--dvdread/dvdread_internal.h8
-rw-r--r--dvdread/ifo_print.c1152
-rw-r--r--dvdread/ifo_print.h63
-rw-r--r--dvdread/ifo_read.c2022
-rw-r--r--dvdread/ifo_read.h231
-rw-r--r--dvdread/ifo_types.h937
-rw-r--r--dvdread/libdvdread_changes.diff778
-rw-r--r--dvdread/md5.c417
-rw-r--r--dvdread/md5.h161
-rw-r--r--dvdread/nav_print.c279
-rw-r--r--dvdread/nav_print.h54
-rw-r--r--dvdread/nav_read.c204
-rw-r--r--dvdread/nav_read.h55
-rw-r--r--dvdread/nav_types.h311
22 files changed, 9746 insertions, 0 deletions
diff --git a/dvdread/Makefile b/dvdread/Makefile
new file mode 100644
index 0000000000..3db80769b5
--- /dev/null
+++ b/dvdread/Makefile
@@ -0,0 +1,53 @@
+
+ifneq ($(wildcard ../config.mak),)
+include ../config.mak
+endif
+
+SRCS = dvd_input.c \
+ dvd_reader.c \
+ dvd_udf.c \
+ ifo_print.c \
+ ifo_read.c \
+ nav_print.c \
+ nav_read.c \
+ md5.c \
+
+OBJS = $(SRCS:.c=.o)
+
+LIB=libdvdread
+
+.SUFFIXES: .c .o
+
+CFLAGS= -I.. $(OPTFLAGS) -D__USE_UNIX98 -D_GNU_SOURCE -DSTDC_HEADERS \
+ -DHAVE_LIMITS_H -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+
+ifeq ($(DVDCSS_INTERNAL),yes)
+CFLAGS += -I../libdvdcss -DHAVE_DVDCSS_DVDCSS_H
+endif
+
+.c.o:
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+all: $(LIB).a
+
+$(LIB).a: $(OBJS)
+ $(AR) rc $(LIB).a $(OBJS)
+ $(RANLIB) $(LIB).a
+
+clean:
+ rm -f *.o *.a *~ *.so
+
+distclean: clean
+ rm -f .depend
+
+dep: depend
+
+depend:
+ $(CC) -MM $(CFLAGS) $(SRCS) 1>.depend
+
+#
+# include dependency files if they exist
+#
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/dvdread/bswap.h b/dvdread/bswap.h
new file mode 100644
index 0000000000..a30061b323
--- /dev/null
+++ b/dvdread/bswap.h
@@ -0,0 +1,146 @@
+#ifndef BSWAP_H_INCLUDED
+#define BSWAP_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001 Billy Biggs <vektor@dumbterm.net>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
+ * detailed changelog at http://svn.mplayerhq.hu/mplayer/trunk/
+ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <config.h>
+
+#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
+
+/* For __FreeBSD_version */
+#if defined(HAVE_SYS_PARAM_H)
+#include <sys/param.h>
+#endif
+
+#if defined(__linux__)
+#include <byteswap.h>
+#define B2N_16(x) x = bswap_16(x)
+#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)
+
+#elif defined(__FreeBSD__) && __FreeBSD_version >= 470000
+#include <sys/endian.h>
+#define B2N_16(x) x = be16toh(x)
+#define B2N_32(x) x = be32toh(x)
+#define B2N_64(x) x = be64toh(x)
+
+#elif defined(__DragonFly__)
+#include <sys/endian.h>
+#define B2N_16(x) x = be16toh(x)
+#define B2N_32(x) x = be32toh(x)
+#define B2N_64(x) x = be64toh(x)
+
+#elif defined(ARCH_X86)
+inline static unsigned short bswap_16(unsigned short x)
+{
+ __asm("xchgb %b0,%h0" :
+ "=q" (x) :
+ "0" (x));
+ return x;
+}
+#define B2N_16(x) x = bswap_16(x)
+
+inline static unsigned int bswap_32(unsigned int x)
+{
+ __asm(
+#if __CPU__ > 386
+ "bswap %0":
+ "=r" (x) :
+#else
+ "xchgb %b0,%h0\n"
+ " rorl $16,%0\n"
+ " xchgb %b0,%h0":
+ "=q" (x) :
+#endif
+ "0" (x));
+ return x;
+}
+#define B2N_32(x) x = bswap_32(x)
+
+inline static unsigned long long int bswap_64(unsigned long long int x)
+{
+ register union { __extension__ uint64_t __ll;
+ uint32_t __l[2]; } __x;
+ asm("xchgl %0,%1":
+ "=r"(__x.__l[0]),"=r"(__x.__l[1]):
+ "0"(bswap_32((unsigned long)x)),"1"(bswap_32((unsigned long)(x>>32))));
+ return __x.__ll;
+}
+#define B2N_64(x) x = bswap_64(x)
+
+/* This is a slow but portable implementation, it has multiple evaluation
+ * problems so beware.
+ * Old FreeBSD's and Solaris don't have <byteswap.h> or any other such
+ * functionality!
+ */
+
+#elif defined(__FreeBSD__) || defined(__sun) || defined(__bsdi__) || defined(__CYGWIN__)
+#define B2N_16(x) \
+ x = ((((x) & 0xff00) >> 8) | \
+ (((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/dvdread/dvd_input.c b/dvdread/dvd_input.c
new file mode 100644
index 0000000000..8478970a6d
--- /dev/null
+++ b/dvdread/dvd_input.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2002 Samuel Hocevar <sam@zoy.org>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
+ * detailed changelog at http://svn.mplayerhq.hu/mplayer/trunk/
+ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "dvd_reader.h"
+#include "dvd_input.h"
+
+/* The function pointers that is the exported interface of this file. */
+dvd_input_t (*dvdinput_open) (const char *);
+int (*dvdinput_close) (dvd_input_t);
+int (*dvdinput_seek) (dvd_input_t, int);
+int (*dvdinput_title) (dvd_input_t, int);
+int (*dvdinput_read) (dvd_input_t, void *, int, int);
+char * (*dvdinput_error) (dvd_input_t);
+
+#ifdef HAVE_DVDCSS_DVDCSS_H
+/* linking to libdvdcss */
+#include "dvdcss/dvdcss.h"
+#define DVDcss_open(a) dvdcss_open((char*)(a))
+#define DVDcss_close dvdcss_close
+#define DVDcss_seek dvdcss_seek
+#define DVDcss_title dvdcss_title
+#define DVDcss_read dvdcss_read
+#define DVDcss_error dvdcss_error
+#else
+/* dlopening libdvdcss */
+#include <dlfcn.h>
+typedef struct dvdcss_s *dvdcss_handle;
+static dvdcss_handle (*DVDcss_open) (const char *);
+static int (*DVDcss_close) (dvdcss_handle);
+static int (*DVDcss_seek) (dvdcss_handle, int, int);
+static int (*DVDcss_title) (dvdcss_handle, int);
+static int (*DVDcss_read) (dvdcss_handle, void *, int, int);
+static char * (*DVDcss_error) (dvdcss_handle);
+#endif
+
+/* The DVDinput handle, add stuff here for new input methods. */
+struct dvd_input_s {
+ /* libdvdcss handle */
+ dvdcss_handle dvdcss;
+
+ /* dummy file input */
+ int fd;
+};
+
+
+/**
+ * initialize and open a DVD device or file.
+ */
+static dvd_input_t css_open(const char *target)
+{
+ dvd_input_t dev;
+
+ /* Allocate the handle structure */
+ dev = (dvd_input_t) malloc(sizeof(struct dvd_input_s));
+ if(dev == NULL) {
+ fprintf(stderr, "libdvdread: Could not allocate memory.\n");
+ return NULL;
+ }
+
+ /* Really open it with libdvdcss */
+ dev->dvdcss = DVDcss_open(target);
+ if(dev->dvdcss == 0) {
+ fprintf(stderr, "libdvdread: Could not open %s with libdvdcss.\n", target);
+ free(dev);
+ return NULL;
+ }
+
+ return dev;
+}
+
+/**
+ * return the last error message
+ */
+static char *css_error(dvd_input_t dev)
+{
+ return DVDcss_error(dev->dvdcss);
+}
+
+/**
+ * seek into the device.
+ */
+static int css_seek(dvd_input_t dev, int blocks)
+{
+ /* DVDINPUT_NOFLAGS should match the DVDCSS_NOFLAGS value. */
+ return DVDcss_seek(dev->dvdcss, blocks, DVDINPUT_NOFLAGS);
+}
+
+/**
+ * set the block for the begining of a new title (key).
+ */
+static int css_title(dvd_input_t dev, int block)
+{
+ return DVDcss_title(dev->dvdcss, block);
+}
+
+/**
+ * read data from the device.
+ */
+static int css_read(dvd_input_t dev, void *buffer, int blocks, int flags)
+{
+ return DVDcss_read(dev->dvdcss, buffer, blocks, flags);
+}
+
+/**
+ * close the DVD device and clean up the library.
+ */
+static int css_close(dvd_input_t dev)
+{
+ int ret;
+
+ ret = DVDcss_close(dev->dvdcss);
+
+ if(ret < 0)
+ return ret;
+
+ free(dev);
+
+ return 0;
+}
+
+
+
+
+
+
+/**
+ * initialize and open a DVD device or file.
+ */
+static dvd_input_t file_open(const char *target)
+{
+ dvd_input_t dev;
+
+ /* Allocate the library structure */
+ dev = (dvd_input_t) malloc(sizeof(struct dvd_input_s));
+ if(dev == NULL) {
+ fprintf(stderr, "libdvdread: Could not allocate memory.\n");
+ return NULL;
+ }
+
+ /* Open the device */
+ dev->fd = open(target, O_RDONLY);
+ if(dev->fd < 0) {
+ perror("libdvdread: Could not open input");
+ free(dev);
+ return NULL;
+ }
+
+ return dev;
+}
+
+/**
+ * return the last error message
+ */
+static char *file_error(dvd_input_t dev)
+{
+ /* use strerror(errno)? */
+ return (char *)"unknown error";
+}
+
+/**
+ * seek into the device.
+ */
+static int file_seek(dvd_input_t dev, int blocks)
+{
+ off_t pos;
+
+ pos = lseek(dev->fd, (off_t)blocks * (off_t)DVD_VIDEO_LB_LEN, SEEK_SET);
+ if(pos < 0) {
+ return pos;
+ }
+ /* assert pos % DVD_VIDEO_LB_LEN == 0 */
+ return (int) (pos / DVD_VIDEO_LB_LEN);
+}
+
+/**
+ * set the block for the begining of a new title (key).
+ */
+static int file_title(dvd_input_t dev, int block)
+{
+ return -1;
+}
+
+/**
+ * read data from the device.
+ */
+static int file_read(dvd_input_t dev, void *buffer, int blocks, int flags)
+{
+ size_t len;
+ ssize_t ret;
+
+ len = (size_t)blocks * DVD_VIDEO_LB_LEN;
+
+ while(len > 0) {
+
+ ret = read(dev->fd, buffer, len);
+
+ if(ret < 0) {
+ /* One of the reads failed, too bad. We won't even bother
+ * returning the reads that went ok, and as in the posix spec
+ * the file postition is left unspecified after a failure. */
+ return ret;
+ }
+
+ if(ret == 0) {
+ /* Nothing more to read. Return the whole blocks, if any, that we got.
+ and adjust the file possition back to the previous block boundary. */
+ size_t bytes = (size_t)blocks * DVD_VIDEO_LB_LEN - len;
+ off_t over_read = -(bytes % DVD_VIDEO_LB_LEN);
+ /*off_t pos =*/ lseek(dev->fd, over_read, SEEK_CUR);
+ /* should have pos % 2048 == 0 */
+ return (int) (bytes / DVD_VIDEO_LB_LEN);
+ }
+
+ len -= ret;
+ }
+
+ return blocks;
+}
+
+/**
+ * close the DVD device and clean up.
+ */
+static int file_close(dvd_input_t dev)
+{
+ int ret;
+
+ ret = close(dev->fd);
+
+ if(ret < 0)
+ return ret;
+
+ free(dev);
+
+ return 0;
+}
+
+
+/**
+ * Setup read functions with either libdvdcss or minimal DVD access.
+ */
+int dvdinput_setup(void)
+{
+ void *dvdcss_library = NULL;
+ char **dvdcss_version = NULL;
+
+#ifdef HAVE_DVDCSS_DVDCSS_H
+ /* linking to libdvdcss */
+ dvdcss_library = &dvdcss_library; /* Give it some value != NULL */
+ /* the DVDcss_* functions have been #defined at the top */
+ dvdcss_version = &dvdcss_interface_2;
+
+#else
+ /* dlopening libdvdcss */
+ dvdcss_library = dlopen("libdvdcss.so.2", RTLD_LAZY);
+
+ if(dvdcss_library != NULL) {
+#if defined(__OpenBSD__) && !defined(__ELF__)
+#define U_S "_"
+#else
+#define U_S
+#endif
+ DVDcss_open = (dvdcss_handle (*)(const char*))
+ dlsym(dvdcss_library, U_S "dvdcss_open");
+ DVDcss_close = (int (*)(dvdcss_handle))
+ dlsym(dvdcss_library, U_S "dvdcss_close");
+ DVDcss_title = (int (*)(dvdcss_handle, int))
+ dlsym(dvdcss_library, U_S "dvdcss_title");
+ DVDcss_seek = (int (*)(dvdcss_handle, int, int))
+ dlsym(dvdcss_library, U_S "dvdcss_seek");
+ DVDcss_read = (int (*)(dvdcss_handle, void*, int, int))
+ dlsym(dvdcss_library, U_S "dvdcss_read");
+ DVDcss_error = (char* (*)(dvdcss_handle))
+ dlsym(dvdcss_library, U_S "dvdcss_error");
+
+ dvdcss_version = (char **)dlsym(dvdcss_library, U_S "dvdcss_interface_2");
+
+ if(dlsym(dvdcss_library, U_S "dvdcss_crack")) {
+ fprintf(stderr,
+ "libdvdread: Old (pre-0.0.2) version of libdvdcss found.\n"
+ "libdvdread: You should get the latest version from "
+ "http://www.videolan.org/\n" );
+ dlclose(dvdcss_library);
+ dvdcss_library = NULL;
+ } else if(!DVDcss_open || !DVDcss_close || !DVDcss_title || !DVDcss_seek
+ || !DVDcss_read || !DVDcss_error || !dvdcss_version) {
+ fprintf(stderr, "libdvdread: Missing symbols in libdvdcss.so.2, "
+ "this shouldn't happen !\n");
+ dlclose(dvdcss_library);
+ }
+ }
+#endif /* HAVE_DVDCSS_DVDCSS_H */
+
+ if(dvdcss_library != NULL) {
+ /*
+ char *psz_method = getenv( "DVDCSS_METHOD" );
+ char *psz_verbose = getenv( "DVDCSS_VERBOSE" );
+ fprintf(stderr, "DVDCSS_METHOD %s\n", psz_method);
+ fprintf(stderr, "DVDCSS_VERBOSE %s\n", psz_verbose);
+ */
+ /*
+ fprintf(stderr, "libdvdread: Using libdvdcss version %s for DVD access\n",
+ *dvdcss_version);
+ */
+
+ /* libdvdcss wrapper functions */
+ dvdinput_open = css_open;
+ dvdinput_close = css_close;
+ dvdinput_seek = css_seek;
+ dvdinput_title = css_title;
+ dvdinput_read = css_read;
+ dvdinput_error = css_error;
+ return 1;
+
+ } else {
+ fprintf(stderr, "libdvdread: Encrypted DVD support unavailable.\n");
+
+ /* libdvdcss replacement functions */
+ dvdinput_open = file_open;
+ dvdinput_close = file_close;
+ dvdinput_seek = file_seek;
+ dvdinput_title = file_title;
+ dvdinput_read = file_read;
+ dvdinput_error = file_error;
+ return 0;
+ }
+}
diff --git a/dvdread/dvd_input.h b/dvdread/dvd_input.h
new file mode 100644
index 0000000000..e9e84917ea
--- /dev/null
+++ b/dvdread/dvd_input.h
@@ -0,0 +1,47 @@
+#ifndef DVD_INPUT_H_INCLUDED
+#define DVD_INPUT_H_INCLUDED
+
+/*
+ * Copyright (C) 2001, 2002 Samuel Hocevar <sam@zoy.org>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+ */
+
+/**
+ * Defines and flags. Make sure they fit the libdvdcss API!
+ */
+#define DVDINPUT_NOFLAGS 0
+
+#define DVDINPUT_READ_DECRYPT (1 << 0)
+
+typedef struct dvd_input_s *dvd_input_t;
+
+/**
+ * Pointers which will be filled either the input methods functions.
+ */
+extern dvd_input_t (*dvdinput_open) (const char *);
+extern int (*dvdinput_close) (dvd_input_t);
+extern int (*dvdinput_seek) (dvd_input_t, int);
+extern int (*dvdinput_title) (dvd_input_t, int);
+extern int (*dvdinput_read) (dvd_input_t, void *, int, int);
+extern char * (*dvdinput_error) (dvd_input_t);
+
+/**
+ * Setup function accessed by dvd_reader.c. Returns 1 if there is CSS support.
+ */
+int dvdinput_setup(void);
+
+#endif /* DVD_INPUT_H_INCLUDED */
diff --git a/dvdread/dvd_reader.c b/dvdread/dvd_reader.c
new file mode 100644
index 0000000000..d944696c15
--- /dev/null
+++ b/dvdread/dvd_reader.c
@@ -0,0 +1,1149 @@
+/*
+ * Copyright (C) 2001, 2002, 2003 Billy Biggs <vektor@dumbterm.net>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>,
+ * Björn Englund <d4bjorn@dtek.chalmers.se>
+ *
+ * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
+ * detailed changelog at http://svn.mplayerhq.hu/mplayer/trunk/
+ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h> /* For the timing of dvdcss_title crack. */
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <dirent.h>
+
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__)|| defined(__DARWIN__) || defined(__DragonFly__)
+#define SYS_BSD 1
+#endif
+
+#if defined(__sun)
+#include <sys/mnttab.h>
+#elif defined(hpux)
+#include </usr/conf/h/mnttab.h>
+#elif defined(SYS_BSD)
+#include <fstab.h>
+#elif defined(__linux__) || defined(__CYGWIN__)
+#include <mntent.h>
+#endif
+
+#if defined(__MINGW32__) && (__MINGW32_MAJOR_VERSION <= 3) && (__MINGW32_MINOR_VERSION < 10)
+#include <sys/timeb.h>
+static void gettimeofday(struct timeval* t,void* timezone){
+ struct timeb timebuffer;
+ ftime( &timebuffer );
+ t->tv_sec=timebuffer.time;
+ t->tv_usec=1000*timebuffer.millitm;
+}
+#endif
+
+#include "dvd_udf.h"
+#include "dvd_input.h"
+#include "dvd_reader.h"
+#include "md5.h"
+
+#define DEFAULT_UDF_CACHE_LEVEL 0
+
+struct dvd_reader_s {
+ /* Basic information. */
+ int isImageFile;
+
+ /* Hack for keeping track of the css status.
+ * 0: no css, 1: perhaps (need init of keys), 2: have done init */
+ int css_state;
+ int css_title; /* Last title that we have called dvdinpute_title for. */
+
+ /* Information required for an image file. */
+ dvd_input_t dev;
+
+ /* Information required for a directory path drive. */
+ char *path_root;
+
+ /* Filesystem cache */
+ int udfcache_level; /* 0 - turned off, 1 - on */
+ void *udfcache;
+};
+
+struct dvd_file_s {
+ /* Basic information. */
+ dvd_reader_t *dvd;
+
+ /* Hack for selecting the right css title. */
+ int css_title;
+
+ /* Information required for an image file. */
+ uint32_t lb_start;
+ uint32_t seek_pos;
+
+ /* Information required for a directory path drive. */
+ size_t title_sizes[ 9 ];
+ dvd_input_t title_devs[ 9 ];
+
+ /* Calculated at open-time, size in blocks. */
+ ssize_t filesize;
+};
+
+/**
+ * Set the level of caching on udf
+ * level = 0 (no caching)
+ * level = 1 (caching filesystem info)
+ */
+int DVDUDFCacheLevel(dvd_reader_t *device, int level)
+{
+ struct dvd_reader_s *dev = (struct dvd_reader_s *)device;
+
+ if(level > 0) {
+ level = 1;
+ } else if(level < 0) {
+ return dev->udfcache_level;
+ }
+
+ dev->udfcache_level = level;
+
+ return level;
+}
+
+void *GetUDFCacheHandle(dvd_reader_t *device)
+{
+ struct dvd_reader_s *dev = (struct dvd_reader_s *)device;
+
+ return dev->udfcache;
+}
+
+void SetUDFCacheHandle(dvd_reader_t *device, void *cache)
+{
+ struct dvd_reader_s *dev = (struct dvd_reader_s *)device;
+
+ dev->udfcache = cache;
+}
+
+
+
+/* Loop over all titles and call dvdcss_title to crack the keys. */
+static int initAllCSSKeys( dvd_reader_t *dvd )
+{
+ struct timeval all_s, all_e;
+ struct timeval t_s, t_e;
+ char filename[ MAX_UDF_FILE_NAME_LEN ];
+ uint32_t start, len;
+ int title;
+
+ char *nokeys_str = getenv("DVDREAD_NOKEYS");
+ if(nokeys_str != NULL)
+ return 0;
+
+ fprintf( stderr, "\n" );
+ fprintf( stderr, "libdvdread: Attempting to retrieve all CSS keys\n" );
+ fprintf( stderr, "libdvdread: This can take a _long_ time, "
+ "please be patient\n\n" );
+
+ gettimeofday(&all_s, NULL);
+
+ for( title = 0; title < 100; title++ ) {
+ gettimeofday( &t_s, NULL );
+ if( title == 0 ) {
+ sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
+ } else {
+ sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 0 );
+ }
+ start = UDFFindFile( dvd, filename, &len );
+ if( start != 0 && len != 0 ) {
+ /* Perform CSS key cracking for this title. */
+ fprintf( stderr, "libdvdread: Get key for %s at 0x%08x\n",
+ filename, start );
+ if( dvdinput_title( dvd->dev, (int)start ) < 0 ) {
+ fprintf( stderr, "libdvdread: Error cracking CSS key for %s (0x%08x)\n", filename, start);
+ }
+ gettimeofday( &t_e, NULL );
+ fprintf( stderr, "libdvdread: Elapsed time %ld\n",
+ (long int) t_e.tv_sec - t_s.tv_sec );
+ }
+
+ if( title == 0 ) continue;
+
+ gettimeofday( &t_s, NULL );
+ sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 1 );
+ start = UDFFindFile( dvd, filename, &len );
+ if( start == 0 || len == 0 ) break;
+
+ /* Perform CSS key cracking for this title. */
+ fprintf( stderr, "libdvdread: Get key for %s at 0x%08x\n",
+ filename, start );
+ if( dvdinput_title( dvd->dev, (int)start ) < 0 ) {
+ fprintf( stderr, "libdvdread: Error cracking CSS key for %s (0x%08x)!!\n", filename, start);
+ }
+ gettimeofday( &t_e, NULL );
+ fprintf( stderr, "libdvdread: Elapsed time %ld\n",
+ (long int) t_e.tv_sec - t_s.tv_sec );
+ }
+ title--;
+
+ fprintf( stderr, "libdvdread: Found %d VTS's\n", title );
+ gettimeofday(&all_e, NULL);
+ fprintf( stderr, "libdvdread: Elapsed time %ld\n",
+ (long int) all_e.tv_sec - all_s.tv_sec );
+
+ return 0;
+}
+
+
+
+/**
+ * Open a DVD image or block device file.
+ */
+static dvd_reader_t *DVDOpenImageFile( const char *location, int have_css )
+{
+ dvd_reader_t *dvd;
+ dvd_input_t dev;
+
+ dev = dvdinput_open( location );
+ if( !dev ) {
+ fprintf( stderr, "libdvdread: Can't open %s for reading\n", location );
+ return 0;
+ }
+
+ dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
+ if( !dvd ) return 0;
+ dvd->isImageFile = 1;
+ dvd->dev = dev;
+ dvd->path_root = 0;
+
+ dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
+ dvd->udfcache = NULL;
+
+ if( have_css ) {
+ /* Only if DVDCSS_METHOD = title, a bit if it's disc or if
+ * DVDCSS_METHOD = key but region missmatch. Unfortunaly we
+ * don't have that information. */
+
+ dvd->css_state = 1; /* Need key init. */
+ }
+ dvd->css_title = 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->path_root = strdup( path_root );
+
+ dvd->udfcache_level = DEFAULT_UDF_CACHE_LEVEL;
+ dvd->udfcache = NULL;
+
+ dvd->css_state = 0; /* Only used in the UDF path */
+ dvd->css_title = 0; /* Only matters in the UDF path */
+
+ return dvd;
+}
+
+#if defined(__sun)
+/* /dev/rdsk/c0t6d0s0 (link to /devices/...)
+ /vol/dev/rdsk/c0t6d0/??
+ /vol/rdsk/<name> */
+static char *sun_block2char( const char *path )
+{
+ char *new_path;
+
+ /* Must contain "/dsk/" */
+ if( !strstr( path, "/dsk/" ) ) return (char *) strdup( path );
+
+ /* Replace "/dsk/" with "/rdsk/" */
+ new_path = malloc( strlen(path) + 2 );
+ strcpy( new_path, path );
+ strcpy( strstr( new_path, "/dsk/" ), "" );
+ strcat( new_path, "/rdsk/" );
+ strcat( new_path, strstr( path, "/dsk/" ) + strlen( "/dsk/" ) );
+
+ return new_path;
+}
+#endif
+
+#if defined(SYS_BSD)
+/* FreeBSD /dev/(r)(a)cd0c (a is for atapi), recomended to _not_ use r
+ OpenBSD /dev/rcd0c, it needs to be the raw device
+ NetBSD /dev/rcd0[d|c|..] d for x86, c (for non x86), perhaps others
+ Darwin /dev/rdisk0, it needs to be the raw device
+ BSD/OS /dev/sr0c (if not mounted) or /dev/rsr0c ('c' any letter will do) */
+static char *bsd_block2char( const char *path )
+#if defined(__FreeBSD__)
+{
+ return (char *) strdup( path );
+}
+#else
+{
+ char *new_path;
+
+ /* If it doesn't start with "/dev/" or does start with "/dev/r" exit */
+ if( strncmp( path, "/dev/", 5 ) || !strncmp( path, "/dev/r", 6 ) )
+ return (char *) strdup( path );
+
+ /* Replace "/dev/" with "/dev/r" */
+ new_path = malloc( strlen(path) + 2 );
+ strcpy( new_path, "/dev/r" );
+ strcat( new_path, path + strlen( "/dev/" ) );
+
+ return new_path;
+}
+#endif /* __FreeBSD__ */
+#endif
+
+dvd_reader_t *DVDOpen( const char *path )
+{
+ struct stat fileinfo;
+ int ret, have_css;
+ char *dev_name = 0;
+
+ if( path == NULL )
+ return 0;
+
+#ifdef WIN32
+ /* Stat doesn't work on devices under mingwin/cygwin. */
+ if( path[0] && path[1] == ':' && path[2] == '\0' )
+ {
+ /* Don't try to stat the file */
+ fileinfo.st_mode = S_IFBLK;
+ }
+ else
+#endif
+ {
+ ret = stat( path, &fileinfo );
+ if( ret < 0 ) {
+ /* If we can't stat the file, give up */
+ fprintf( stderr, "libdvdread: Can't stat %s\n", path );
+ perror("");
+ return 0;
+ }
+ }
+
+ /* Try to open libdvdcss or fall back to standard functions */
+ have_css = dvdinput_setup();
+
+ /* First check if this is a block/char device or a file*/
+ if( S_ISBLK( fileinfo.st_mode ) ||
+ S_ISCHR( fileinfo.st_mode ) ||
+ S_ISREG( fileinfo.st_mode ) ) {
+
+ /**
+ * Block devices and regular files are assumed to be DVD-Video images.
+ */
+#if defined(__sun)
+ return DVDOpenImageFile( sun_block2char( path ), have_css );
+#elif defined(SYS_BSD)
+ return DVDOpenImageFile( bsd_block2char( path ), have_css );
+#else
+ return DVDOpenImageFile( path, have_css );
+#endif
+
+ } else if( S_ISDIR( fileinfo.st_mode ) ) {
+ dvd_reader_t *auth_drive = 0;
+ char *path_copy;
+#if defined(SYS_BSD)
+ struct fstab* fe;
+#elif defined(__sun) || defined(__linux__) || defined(__CYGWIN__)
+ 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 );
+#ifndef __MINGW32__
+ fchdir( cdir );
+#endif
+ close( cdir );
+ if( new_path ) {
+ free( path_copy );
+ path_copy = new_path;
+ }
+ }
+ }
+
+ /**
+ * If we're being asked to open a directory, check if that directory
+ * is the mountpoint for a DVD-ROM which we can use instead.
+ */
+
+ if( strlen( path_copy ) > 1 ) {
+ if( path_copy[ strlen( path_copy ) - 1 ] == '/' )
+ path_copy[ strlen( path_copy ) - 1 ] = '\0';
+ }
+
+ if( strlen( path_copy ) > 9 ) {
+ if( !strcasecmp( &(path_copy[ strlen( path_copy ) - 9 ]),
+ "/video_ts" ) ) {
+ path_copy[ strlen( path_copy ) - 9 ] = '\0';
+ }
+ }
+
+#if defined(SYS_BSD)
+ if( ( fe = getfsfile( path_copy ) ) ) {
+ dev_name = bsd_block2char( fe->fs_spec );
+ fprintf( stderr,
+ "libdvdread: Attempting to use device %s"
+ " mounted on %s for CSS authentication\n",
+ dev_name,
+ fe->fs_file );
+ auth_drive = DVDOpenImageFile( dev_name, have_css );
+ }
+#elif defined(__sun)
+ mntfile = fopen( MNTTAB, "r" );
+ if( mntfile ) {
+ struct mnttab mp;
+ int res;
+
+ while( ( res = getmntent( mntfile, &mp ) ) != -1 ) {
+ if( res == 0 && !strcmp( mp.mnt_mountp, path_copy ) ) {
+ dev_name = sun_block2char( mp.mnt_special );
+ fprintf( stderr,
+ "libdvdread: Attempting to use device %s"
+ " mounted on %s for CSS authentication\n",
+ dev_name,
+ mp.mnt_mountp );
+ auth_drive = DVDOpenImageFile( dev_name, have_css );
+ break;
+ }
+ }
+ fclose( mntfile );
+ }
+#elif defined(__linux__) || defined(__CYGWIN__)
+ mntfile = fopen( MOUNTED, "r" );
+ if( mntfile ) {
+ struct mntent *me;
+
+ while( ( me = getmntent( mntfile ) ) ) {
+ if( !strcmp( me->mnt_dir, path_copy ) ) {
+ fprintf( stderr,
+ "libdvdread: Attempting to use device %s"
+ " mounted on %s for CSS authentication\n",
+ me->mnt_fsname,
+ me->mnt_dir );
+ auth_drive = DVDOpenImageFile( me->mnt_fsname, have_css );
+ dev_name = strdup(me->mnt_fsname);
+ break;
+ }
+ }
+ fclose( mntfile );
+ }
+#elif defined(__MINGW32__)
+ dev_name = strdup(path);
+ auth_drive = DVDOpenImageFile( path, have_css );
+#endif
+ if( !dev_name ) {
+ fprintf( stderr, "libdvdread: Couldn't find device name.\n" );
+ } else if( !auth_drive ) {
+ fprintf( stderr, "libdvdread: Device %s inaccessible, "
+ "CSS authentication not available.\n", dev_name );
+ }
+
+ free( dev_name );
+ free( path_copy );
+
+ /**
+ * If we've opened a drive, just use that.
+ */
+ if( auth_drive ) return auth_drive;
+
+ /**
+ * Otherwise, we now try to open the directory tree instead.
+ */
+ return DVDOpenPath( path );
+ }
+
+ /* If it's none of the above, screw it. */
+ fprintf( stderr, "libdvdread: Could not open %s\n", path );
+ return 0;
+}
+
+void DVDClose( dvd_reader_t *dvd )
+{
+ if( dvd ) {
+ if( dvd->dev ) dvdinput_close( dvd->dev );
+ if( dvd->path_root ) free( dvd->path_root );
+ if( dvd->udfcache ) FreeUDFCache( dvd->udfcache );
+ free( dvd );
+ }
+}
+
+/**
+ * Open an unencrypted file on a DVD image file.
+ */
+static dvd_file_t *DVDOpenFileUDF( dvd_reader_t *dvd, char *filename )
+{
+ uint32_t start, len;
+ dvd_file_t *dvd_file;
+
+ start = UDFFindFile( dvd, filename, &len );
+ if( !start ) return 0;
+
+ dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
+ if( !dvd_file ) return 0;
+ dvd_file->dvd = dvd;
+ dvd_file->lb_start = start;
+ dvd_file->seek_pos = 0;
+ memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
+ memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
+ dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
+
+ return dvd_file;
+}
+
+/**
+ * Searches for <file> in directory <path>, ignoring case.
+ * Returns 0 and full filename in <filename>.
+ * or -1 on file not found.
+ * or -2 on path not found.
+ */
+static int findDirFile( const char *path, const char *file, char *filename )
+{
+ DIR *dir;
+ struct dirent *ent;
+
+ dir = opendir( path );
+ if( !dir ) return -2;
+
+ while( ( ent = readdir( dir ) ) != NULL ) {
+ if( !strcasecmp( ent->d_name, file ) ) {
+ sprintf( filename, "%s%s%s", path,
+ ( ( path[ strlen( path ) - 1 ] == '/' ) ? "" : "/" ),
+ ent->d_name );
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int findDVDFile( dvd_reader_t *dvd, const char *file, char *filename )
+{
+ char video_path[ PATH_MAX + 1 ];
+ const char *nodirfile;
+ int ret;
+
+ /* Strip off the directory for our search */
+ if( !strncasecmp( "/VIDEO_TS/", file, 10 ) ) {
+ nodirfile = &(file[ 10 ]);
+ } else {
+ nodirfile = file;
+ }
+
+ ret = findDirFile( dvd->path_root, nodirfile, filename );
+ if( ret < 0 ) {
+ /* Try also with adding the path, just in case. */
+ sprintf( video_path, "%s/VIDEO_TS/", dvd->path_root );
+ ret = findDirFile( video_path, nodirfile, filename );
+ if( ret < 0 ) {
+ /* Try with the path, but in lower case. */
+ sprintf( video_path, "%s/video_ts/", dvd->path_root );
+ ret = findDirFile( video_path, nodirfile, filename );
+ if( ret < 0 ) {
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * Open an unencrypted file from a DVD directory tree.
+ */
+static dvd_file_t *DVDOpenFilePath( dvd_reader_t *dvd, char *filename )
+{
+ char full_path[ PATH_MAX + 1 ];
+ dvd_file_t *dvd_file;
+ struct stat fileinfo;
+ dvd_input_t dev;
+
+ /* Get the full path of the file. */
+ if( !findDVDFile( dvd, filename, full_path ) ) return 0;
+
+ dev = dvdinput_open( full_path );
+ if( !dev ) return 0;
+
+ dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
+ if( !dvd_file ) return 0;
+ dvd_file->dvd = dvd;
+ dvd_file->lb_start = 0;
+ dvd_file->seek_pos = 0;
+ memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
+ memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
+ dvd_file->filesize = 0;
+
+ if( stat( full_path, &fileinfo ) < 0 ) {
+ fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
+ free( dvd_file );
+ return 0;
+ }
+ dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+ dvd_file->title_devs[ 0 ] = dev;
+ dvd_file->filesize = dvd_file->title_sizes[ 0 ];
+
+ return dvd_file;
+}
+
+static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *dvd, int title, int menu )
+{
+ char filename[ MAX_UDF_FILE_NAME_LEN ];
+ uint32_t start, len;
+ dvd_file_t *dvd_file;
+
+ if( title == 0 ) {
+ sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
+ } else {
+ sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
+ }
+ start = UDFFindFile( dvd, filename, &len );
+ if( start == 0 ) return 0;
+
+ dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
+ if( !dvd_file ) return 0;
+ dvd_file->dvd = dvd;
+ /*Hack*/ dvd_file->css_title = title << 1 | menu;
+ dvd_file->lb_start = start;
+ dvd_file->seek_pos = 0;
+ memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
+ memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
+ dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
+
+ /* Calculate the complete file size for every file in the VOBS */
+ if( !menu ) {
+ int cur;
+
+ for( cur = 2; cur < 10; cur++ ) {
+ sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur );
+ if( !UDFFindFile( dvd, filename, &len ) ) break;
+ dvd_file->filesize += len / DVD_VIDEO_LB_LEN;
+ }
+ }
+
+ if( dvd->css_state == 1 /* Need key init */ ) {
+// initAllCSSKeys( dvd );
+// dvd->css_state = 2;
+ }
+ /*
+ if( dvdinput_title( dvd_file->dvd->dev, (int)start ) < 0 ) {
+ fprintf( stderr, "libdvdread: Error cracking CSS key for %s\n",
+ filename );
+ }
+ */
+
+ return dvd_file;
+}
+
+static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *dvd, int title, int menu )
+{
+ char filename[ MAX_UDF_FILE_NAME_LEN ];
+ char full_path[ PATH_MAX + 1 ];
+ struct stat fileinfo;
+ dvd_file_t *dvd_file;
+ int i;
+
+ dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
+ if( !dvd_file ) return 0;
+ dvd_file->dvd = dvd;
+ /*Hack*/ dvd_file->css_title = title << 1 | menu;
+ dvd_file->lb_start = 0;
+ dvd_file->seek_pos = 0;
+ memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
+ memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
+ dvd_file->filesize = 0;
+
+ if( menu ) {
+ dvd_input_t dev;
+
+ if( title == 0 ) {
+ sprintf( filename, "VIDEO_TS.VOB" );
+ } else {
+ sprintf( filename, "VTS_%02i_0.VOB", title );
+ }
+ if( !findDVDFile( dvd, filename, full_path ) ) {
+ free( dvd_file );
+ return 0;
+ }
+
+ dev = dvdinput_open( full_path );
+ if( dev == NULL ) {
+ free( dvd_file );
+ return 0;
+ }
+
+ if( stat( full_path, &fileinfo ) < 0 ) {
+ fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
+ free( dvd_file );
+ return 0;
+ }
+ dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+ dvd_file->title_devs[ 0 ] = dev;
+ dvdinput_title( dvd_file->title_devs[0], 0);
+ dvd_file->filesize = dvd_file->title_sizes[ 0 ];
+
+ } else {
+ for( i = 0; i < 9; ++i ) {
+
+ sprintf( filename, "VTS_%02i_%i.VOB", title, i + 1 );
+ if( !findDVDFile( dvd, filename, full_path ) ) {
+ break;
+ }
+
+ if( stat( full_path, &fileinfo ) < 0 ) {
+ fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
+ break;
+ }
+
+ dvd_file->title_sizes[ i ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+ dvd_file->title_devs[ i ] = dvdinput_open( full_path );
+ dvdinput_title( dvd_file->title_devs[ i ], 0 );
+ dvd_file->filesize += dvd_file->title_sizes[ i ];
+ }
+ if( !dvd_file->title_devs[ 0 ] ) {
+ free( dvd_file );
+ return 0;
+ }
+ }
+
+ return dvd_file;
+}
+
+dvd_file_t *DVDOpenFile( dvd_reader_t *dvd, int titlenum,
+ dvd_read_domain_t domain )
+{
+ char filename[ MAX_UDF_FILE_NAME_LEN ];
+
+ /* Check arguments. */
+ if( dvd == NULL || titlenum < 0 )
+ return NULL;
+
+ switch( domain ) {
+ case DVD_READ_INFO_FILE:
+ if( titlenum == 0 ) {
+ sprintf( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
+ } else {
+ sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );
+ }
+ break;
+ case DVD_READ_INFO_BACKUP_FILE:
+ if( titlenum == 0 ) {
+ sprintf( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
+ } else {
+ sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );
+ }
+ break;
+ case DVD_READ_MENU_VOBS:
+ if( dvd->isImageFile ) {
+ return DVDOpenVOBUDF( dvd, titlenum, 1 );
+ } else {
+ return DVDOpenVOBPath( dvd, titlenum, 1 );
+ }
+ break;
+ case DVD_READ_TITLE_VOBS:
+ if( titlenum == 0 ) return 0;
+ if( dvd->isImageFile ) {
+ return DVDOpenVOBUDF( dvd, titlenum, 0 );
+ } else {
+ return DVDOpenVOBPath( dvd, titlenum, 0 );
+ }
+ break;
+ default:
+ fprintf( stderr, "libdvdread: Invalid domain for file open.\n" );
+ return NULL;
+ }
+
+ if( dvd->isImageFile ) {
+ return DVDOpenFileUDF( dvd, filename );
+ } else {
+ return DVDOpenFilePath( dvd, filename );
+ }
+}
+
+void DVDCloseFile( dvd_file_t *dvd_file )
+{
+ int i;
+
+ if( dvd_file ) {
+ if( dvd_file->dvd->isImageFile ) {
+ ;
+ } else {
+ for( i = 0; i < 9; ++i ) {
+ if( dvd_file->title_devs[ i ] ) {
+ dvdinput_close( dvd_file->title_devs[i] );
+ }
+ }
+ }
+
+ free( dvd_file );
+ dvd_file = 0;
+ }
+}
+
+/* Internal, but used from dvd_udf.c */
+int UDFReadBlocksRaw( dvd_reader_t *device, uint32_t lb_number,
+ size_t block_count, unsigned char *data,
+ int encrypted )
+{
+ int ret;
+
+ if( !device->dev ) {
+ fprintf( stderr, "libdvdread: Fatal error in block read.\n" );
+ return 0;
+ }
+
+ ret = dvdinput_seek( device->dev, (int) lb_number );
+ if( ret != (int) lb_number ) {
+ fprintf( stderr, "libdvdread: Can't seek to block %u\n", lb_number );
+ return 0;
+ }
+
+ return dvdinput_read( device->dev, (char *) data,
+ (int) block_count, encrypted );
+}
+
+/* This is using a single input and starting from 'dvd_file->lb_start' offset.
+ *
+ * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
+ * into the buffer located at 'data' and if 'encrypted' is set
+ * descramble the data if it's encrypted. Returning either an
+ * negative error or the number of blocks read. */
+static int DVDReadBlocksUDF( dvd_file_t *dvd_file, uint32_t offset,
+ size_t block_count, unsigned char *data,
+ int encrypted )
+{
+ return UDFReadBlocksRaw( dvd_file->dvd, dvd_file->lb_start + offset,
+ block_count, data, encrypted );
+}
+
+/* This is using possibly several inputs and starting from an offset of '0'.
+ *
+ * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
+ * into the buffer located at 'data' and if 'encrypted' is set
+ * descramble the data if it's encrypted. Returning either an
+ * negative error or the number of blocks read. */
+static int DVDReadBlocksPath( dvd_file_t *dvd_file, unsigned int offset,
+ size_t block_count, unsigned char *data,
+ int encrypted )
+{
+ int i;
+ int ret, ret2, off;
+
+ ret = 0;
+ ret2 = 0;
+ for( i = 0; i < 9; ++i ) {
+ if( !dvd_file->title_sizes[ i ] ) return 0; /* Past end of file */
+
+ if( offset < dvd_file->title_sizes[ i ] ) {
+ if( ( offset + block_count ) <= dvd_file->title_sizes[ i ] ) {
+ off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );
+ if( off < 0 || off != (int)offset ) {
+ fprintf( stderr, "libdvdread: Can't seek to block %d\n",
+ offset );
+ return off < 0 ? off : 0;
+ }
+ ret = dvdinput_read( dvd_file->title_devs[ i ], data,
+ (int)block_count, encrypted );
+ break;
+ } else {
+ size_t part1_size = dvd_file->title_sizes[ i ] - offset;
+ /* FIXME: Really needs to be a while loop.
+ * (This is only true if you try and read >1GB at a time) */
+
+ /* Read part 1 */
+ off = dvdinput_seek( dvd_file->title_devs[ i ], (int)offset );
+ if( off < 0 || off != (int)offset ) {
+ fprintf( stderr, "libdvdread: Can't seek to block %d\n",
+ offset );
+ return off < 0 ? off : 0;
+ }
+ ret = dvdinput_read( dvd_file->title_devs[ i ], data,
+ (int)part1_size, encrypted );
+ if( ret < 0 ) return ret;
+ /* FIXME: This is wrong if i is the last file in the set.
+ * also error from this read will not show in ret. */
+
+ /* Does the next part exist? If not then return now. */
+ if( !dvd_file->title_devs[ i + 1 ] ) return ret;
+
+ /* Read part 2 */
+ off = dvdinput_seek( dvd_file->title_devs[ i + 1 ], 0 );
+ if( off < 0 || off != 0 ) {
+ fprintf( stderr, "libdvdread: Can't seek to block %d\n",
+ 0 );
+ return off < 0 ? off : 0;
+ }
+ ret2 = dvdinput_read( dvd_file->title_devs[ i + 1 ],
+ data + ( part1_size
+ * (int64_t)DVD_VIDEO_LB_LEN ),
+ (int)(block_count - part1_size),
+ encrypted );
+ if( ret2 < 0 ) return ret2;
+ break;
+ }
+ } else {
+ offset -= dvd_file->title_sizes[ i ];
+ }
+ }
+
+ return ret + ret2;
+}
+
+/* This is broken reading more than 2Gb at a time is ssize_t is 32-bit. */
+ssize_t DVDReadBlocks( dvd_file_t *dvd_file, int offset,
+ size_t block_count, unsigned char *data )
+{
+ int ret;
+
+ /* Check arguments. */
+ if( dvd_file == NULL || offset < 0 || data == NULL )
+ return -1;
+
+ /* Hack, and it will still fail for multiple opens in a threaded app ! */
+ if( dvd_file->dvd->css_title != dvd_file->css_title ) {
+ dvd_file->dvd->css_title = dvd_file->css_title;
+ if( dvd_file->dvd->isImageFile ) {
+ dvdinput_title( dvd_file->dvd->dev, (int)dvd_file->lb_start );
+ }
+ /* Here each vobu has it's own dvdcss handle, so no need to update
+ else {
+ dvdinput_title( dvd_file->title_devs[ 0 ], (int)dvd_file->lb_start );
+ }*/
+ }
+
+ if( dvd_file->dvd->isImageFile ) {
+ ret = DVDReadBlocksUDF( dvd_file, (uint32_t)offset,
+ block_count, data, DVDINPUT_READ_DECRYPT );
+ } else {
+ ret = DVDReadBlocksPath( dvd_file, (unsigned int)offset,
+ block_count, data, DVDINPUT_READ_DECRYPT );
+ }
+
+ return (ssize_t)ret;
+}
+
+int DVDFileSeek( dvd_file_t *dvd_file, int offset )
+{
+ /* Check arguments. */
+ if( dvd_file == NULL || offset < 0 )
+ return -1;
+
+ if( offset > dvd_file->filesize * DVD_VIDEO_LB_LEN ) {
+ return -1;
+ }
+ dvd_file->seek_pos = (uint32_t) offset;
+ return offset;
+}
+
+ssize_t DVDReadBytes( dvd_file_t *dvd_file, void *data, size_t byte_size )
+{
+ unsigned char *secbuf;
+ unsigned int numsec, seek_sector, seek_byte;
+ int ret;
+
+ /* Check arguments. */
+ if( dvd_file == NULL || data == NULL )
+ return -1;
+
+ seek_sector = dvd_file->seek_pos / DVD_VIDEO_LB_LEN;
+ seek_byte = dvd_file->seek_pos % DVD_VIDEO_LB_LEN;
+
+ numsec = ( ( seek_byte + byte_size ) / DVD_VIDEO_LB_LEN ) +
+ ( ( ( seek_byte + byte_size ) % DVD_VIDEO_LB_LEN ) ? 1 : 0 );
+
+ secbuf = (unsigned char *) malloc( numsec * DVD_VIDEO_LB_LEN );
+ if( !secbuf ) {
+ fprintf( stderr, "libdvdread: Can't allocate memory "
+ "for file read!\n" );
+ return 0;
+ }
+
+ if( dvd_file->dvd->isImageFile ) {
+ ret = DVDReadBlocksUDF( dvd_file, (uint32_t) seek_sector,
+ (size_t) numsec, secbuf, DVDINPUT_NOFLAGS );
+ } else {
+ ret = DVDReadBlocksPath( dvd_file, seek_sector,
+ (size_t) numsec, secbuf, DVDINPUT_NOFLAGS );
+ }
+
+ if( ret != (int) numsec ) {
+ free( secbuf );
+ return ret < 0 ? ret : 0;
+ }
+
+ memcpy( data, &(secbuf[ seek_byte ]), byte_size );
+ free( secbuf );
+
+ dvd_file->seek_pos += byte_size;
+ return byte_size;
+}
+
+ssize_t DVDFileSize( dvd_file_t *dvd_file )
+{
+ /* Check arguments. */
+ if( dvd_file == NULL )
+ return -1;
+
+ return dvd_file->filesize;
+}
+
+int DVDDiscID( dvd_reader_t *dvd, unsigned char *discid )
+{
+ struct md5_ctx ctx;
+ int title;
+
+ /* Check arguments. */
+ if( dvd == NULL || discid == NULL )
+ return 0;
+
+ /* Go through the first 10 IFO:s, in order,
+ * and md5sum them, i.e VIDEO_TS.IFO and VTS_0?_0.IFO */
+ md5_init_ctx( &ctx );
+ for( title = 0; title < 10; title++ ) {
+ dvd_file_t *dvd_file = DVDOpenFile( dvd, title, DVD_READ_INFO_FILE );
+ if( dvd_file != NULL ) {
+ ssize_t bytes_read;
+ size_t file_size = dvd_file->filesize * DVD_VIDEO_LB_LEN;
+ char *buffer = malloc( file_size );
+
+ if( buffer == NULL ) {
+ fprintf( stderr, "libdvdread: DVDDiscId, failed to "
+ "allocate memory for file read!\n" );
+ return -1;
+ }
+ bytes_read = DVDReadBytes( dvd_file, buffer, file_size );
+ if( bytes_read != file_size ) {
+ fprintf( stderr, "libdvdread: DVDDiscId read returned %d bytes"
+ ", wanted %d\n", bytes_read, file_size );
+ DVDCloseFile( dvd_file );
+ return -1;
+ }
+
+ md5_process_bytes( buffer, file_size, &ctx );
+
+ DVDCloseFile( dvd_file );
+ free( buffer );
+ }
+ }
+ md5_finish_ctx( &ctx, discid );
+
+ return 0;
+}
+
+
+int DVDISOVolumeInfo( dvd_reader_t *dvd,
+ char *volid, unsigned int volid_size,
+ unsigned char *volsetid, unsigned int volsetid_size )
+{
+ unsigned char *buffer;
+ int ret;
+
+ /* Check arguments. */
+ if( dvd == NULL )
+ return 0;
+
+ if( dvd->dev == NULL ) {
+ /* No block access, so no ISO... */
+ return -1;
+ }
+
+ buffer = malloc( DVD_VIDEO_LB_LEN );
+ if( buffer == NULL ) {
+ fprintf( stderr, "libdvdread: DVDISOVolumeInfo, failed to "
+ "allocate memory for file read!\n" );
+ return -1;
+ }
+
+ ret = UDFReadBlocksRaw( dvd, 16, 1, buffer, 0 );
+ if( ret != 1 ) {
+ fprintf( stderr, "libdvdread: DVDISOVolumeInfo, failed to "
+ "read ISO9660 Primary Volume Descriptor!\n" );
+ return -1;
+ }
+
+ if( (volid != NULL) && (volid_size > 0) ) {
+ unsigned int n;
+ for(n = 0; n < 32; n++) {
+ if(buffer[40+n] == 0x20) {
+ break;
+ }
+ }
+
+ if(volid_size > n+1) {
+ volid_size = n+1;
+ }
+
+ memcpy(volid, &buffer[40], volid_size-1);
+ volid[volid_size-1] = '\0';
+ }
+
+ if( (volsetid != NULL) && (volsetid_size > 0) ) {
+ if(volsetid_size > 128) {
+ volsetid_size = 128;
+ }
+ memcpy(volsetid, &buffer[190], volsetid_size);
+ }
+ return 0;
+}
+
+
+int DVDUDFVolumeInfo( dvd_reader_t *dvd,
+ char *volid, unsigned int volid_size,
+ unsigned char *volsetid, unsigned int volsetid_size )
+{
+ int ret;
+ /* Check arguments. */
+ if( dvd == NULL )
+ return -1;
+
+ if( dvd->dev == NULL ) {
+ /* No block access, so no UDF VolumeSet Identifier */
+ return -1;
+ }
+
+ if( (volid != NULL) && (volid_size > 0) ) {
+ ret = UDFGetVolumeIdentifier(dvd, volid, volid_size);
+ if(!ret) {
+ return -1;
+ }
+ }
+ if( (volsetid != NULL) && (volsetid_size > 0) ) {
+ ret = UDFGetVolumeSetIdentifier(dvd, volsetid, volsetid_size);
+ if(!ret) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/dvdread/dvd_reader.h b/dvdread/dvd_reader.h
new file mode 100644
index 0000000000..cc4ba54895
--- /dev/null
+++ b/dvdread/dvd_reader.h
@@ -0,0 +1,265 @@
+#ifndef DVD_READER_H_INCLUDED
+#define DVD_READER_H_INCLUDED
+
+/*
+ * Copyright (C) 2001, 2002 Billy Biggs <vektor@dumbterm.net>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>,
+ * Björn Englund <d4bjorn@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sys/types.h>
+
+/**
+ * The DVD access interface.
+ *
+ * This file contains the functions that form the interface to to
+ * reading files located on a DVD.
+ */
+
+/**
+ * The current version.
+ */
+#define DVDREAD_VERSION 904
+
+/**
+ * The length of one Logical Block of a DVD.
+ */
+#define DVD_VIDEO_LB_LEN 2048
+
+/**
+ * Maximum length of filenames allowed in UDF.
+ */
+#define MAX_UDF_FILE_NAME_LEN 2048
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Opaque type that is used as a handle for one instance of an opened DVD.
+ */
+typedef struct dvd_reader_s dvd_reader_t;
+
+/**
+ * Opaque type for a file read handle, much like a normal fd or FILE *.
+ */
+typedef struct dvd_file_s dvd_file_t;
+
+/**
+ * Opens a block device of a DVD-ROM file, or an image file, or a directory
+ * name for a mounted DVD or HD copy of a DVD.
+ *
+ * If the given file is a block device, or is the mountpoint for a block
+ * device, then that device is used for CSS authentication using libdvdcss.
+ * If no device is available, then no CSS authentication is performed,
+ * and we hope that the image is decrypted.
+ *
+ * If the path given is a directory, then the files in that directory may be
+ * in any one of these formats:
+ *
+ * path/VIDEO_TS/VTS_01_1.VOB
+ * path/video_ts/vts_01_1.vob
+ * path/VTS_01_1.VOB
+ * path/vts_01_1.vob
+ *
+ * @param path Specifies the the device, file or directory to be used.
+ * @return If successful a a read handle is returned. Otherwise 0 is returned.
+ *
+ * dvd = DVDOpen(path);
+ */
+dvd_reader_t *DVDOpen( const char * );
+
+/**
+ * Closes and cleans up the DVD reader object.
+ *
+ * You must close all open files before calling this function.
+ *
+ * @param dvd A read handle that should be closed.
+ *
+ * DVDClose(dvd);
+ */
+void DVDClose( dvd_reader_t * );
+
+/**
+ *
+ */
+typedef enum {
+ DVD_READ_INFO_FILE, /**< VIDEO_TS.IFO or VTS_XX_0.IFO (title) */
+ DVD_READ_INFO_BACKUP_FILE, /**< VIDEO_TS.BUP or VTS_XX_0.BUP (title) */
+ DVD_READ_MENU_VOBS, /**< VIDEO_TS.VOB or VTS_XX_0.VOB (title) */
+ DVD_READ_TITLE_VOBS /**< VTS_XX_[1-9].VOB (title). All files in
+ the title set are opened and read as a
+ single file. */
+} dvd_read_domain_t;
+
+/**
+ * Opens a file on the DVD given the title number and domain.
+ *
+ * If the title number is 0, the video manager information is opened
+ * (VIDEO_TS.[IFO,BUP,VOB]). Returns a file structure which may be
+ * used for reads, or 0 if the file was not found.
+ *
+ * @param dvd A dvd read handle.
+ * @param titlenum Which Video Title Set should be used, VIDEO_TS is 0.
+ * @param domain Which domain.
+ * @return If successful a a file read handle is returned, otherwise 0.
+ *
+ * dvd_file = DVDOpenFile(dvd, titlenum, domain); */
+dvd_file_t *DVDOpenFile( dvd_reader_t *, int, dvd_read_domain_t );
+
+/**
+ * Closes a file and frees the associated structure.
+ *
+ * @param dvd_file The file read handle to be closed.
+ *
+ * DVDCloseFile(dvd_file);
+ */
+void DVDCloseFile( dvd_file_t * );
+
+/**
+ * Reads block_count number of blocks from the file at the given block offset.
+ * Returns number of blocks read on success, -1 on error. This call is only
+ * for reading VOB data, and should not be used when reading the IFO files.
+ * When reading from an encrypted drive, blocks are decrypted using libdvdcss
+ * where required.
+ *
+ * @param dvd_file A file read handle.
+ * @param offset Block offset from the start of the file to start reading at.
+ * @param block_count Number of block to read.
+ * @param data Pointer to a buffer to write the data into.
+ * @return Returns number of blocks read on success, -1 on error.
+ *
+ * blocks_read = DVDReadBlocks(dvd_file, offset, block_count, data);
+ */
+ssize_t DVDReadBlocks( dvd_file_t *, int, size_t, unsigned char * );
+
+/**
+ * Seek to the given position in the file. Returns the resulting position in
+ * bytes from the beginning of the file. The seek position is only used for
+ * byte reads from the file, the block read call always reads from the given
+ * offset.
+ *
+ * @param dvd_file A file read handle.
+ * @param seek_offset Byte offset from the start of the file to seek to.
+ * @return The resulting position in bytes from the beginning of the file.
+ *
+ * offset_set = DVDFileSeek(dvd_file, seek_offset);
+ */
+int DVDFileSeek( dvd_file_t *, int );
+
+/**
+ * Reads the given number of bytes from the file. This call can only be used
+ * on the information files, and may not be used for reading from a VOB. This
+ * reads from and increments the currrent seek position for the file.
+ *
+ * @param dvd_file A file read handle.
+ * @param data Pointer to a buffer to write the data into.
+ * @param bytes Number of bytes to read.
+ * @return Returns number of bytes read on success, -1 on error.
+ *
+ * bytes_read = DVDReadBytes(dvd_file, data, bytes);
+ */
+ssize_t DVDReadBytes( dvd_file_t *, void *, size_t );
+
+/**
+ * Returns the file size in blocks.
+ *
+ * @param dvd_file A file read handle.
+ * @return The size of the file in blocks, -1 on error.
+ *
+ * blocks = DVDFileSize(dvd_file);
+ */
+ssize_t DVDFileSize( dvd_file_t * );
+
+/**
+ * Get a unique 128 bit disc ID.
+ * This is the MD5 sum of VIDEO_TS.IFO and the VTS_0?_0.IFO files
+ * in title order (those that exist).
+ * If you need a 'text' representation of the id, print it as a
+ * hexadecimal number, using lowercase letters, discid[0] first.
+ * I.e. the same format as the command-line 'md5sum' program uses.
+ *
+ * @param dvd A read handle to get the disc ID from
+ * @param discid The buffer to put the disc ID into. The buffer must
+ * have room for 128 bits (16 chars).
+ * @return 0 on success, -1 on error.
+ */
+int DVDDiscID( dvd_reader_t *, unsigned char * );
+
+/**
+ * Get the UDF VolumeIdentifier and VolumeSetIdentifier
+ * from the PrimaryVolumeDescriptor.
+ *
+ * @param dvd A read handle to get the disc ID from
+ * @param volid The buffer to put the VolumeIdentifier into.
+ * The VolumeIdentifier is latin-1 encoded (8bit unicode)
+ * null terminated and max 32 bytes (including '\0')
+ * @param volid_size No more than volid_size bytes will be copied to volid.
+ * If the VolumeIdentifier is truncated because of this
+ * it will still be null terminated.
+ * @param volsetid The buffer to put the VolumeSetIdentifier into.
+ * The VolumeIdentifier is 128 bytes as
+ * stored in the UDF PrimaryVolumeDescriptor.
+ * Note that this is not a null terminated string.
+ * @param volsetid_size At most volsetid_size bytes will be copied to volsetid.
+ * @return 0 on success, -1 on error.
+ */
+int DVDUDFVolumeInfo( dvd_reader_t *, char *, unsigned int,
+ unsigned char *, unsigned int );
+
+/**
+ * Get the ISO9660 VolumeIdentifier and VolumeSetIdentifier
+ *
+ * * Only use this function as fallback if DVDUDFVolumeInfo returns 0 *
+ * * this will happen on a disc mastered only with a iso9660 filesystem *
+ * * All video DVD discs have UDF filesystem *
+ *
+ * @param dvd A read handle to get the disc ID from
+ * @param volid The buffer to put the VolumeIdentifier into.
+ * The VolumeIdentifier is coded with '0-9','A-Z','_'
+ * null terminated and max 33 bytes (including '\0')
+ * @param volid_size No more than volid_size bytes will be copied to volid.
+ * If the VolumeIdentifier is truncated because of this
+ * it will still be null terminated.
+ * @param volsetid The buffer to put the VolumeSetIdentifier into.
+ * The VolumeIdentifier is 128 bytes as
+ * stored in the ISO9660 PrimaryVolumeDescriptor.
+ * Note that this is not a null terminated string.
+ * @param volsetid_size At most volsetid_size bytes will be copied to volsetid.
+ * @return 0 on success, -1 on error.
+ */
+int DVDISOVolumeInfo( dvd_reader_t *, char *, unsigned int,
+ unsigned char *, unsigned int );
+
+/**
+ * Sets the level of caching that is done when reading from a device
+ *
+ * @param dvd A read handle to get the disc ID from
+ * @param level The level of caching wanted.
+ * -1 - returns the current setting.
+ * 0 - UDF Cache turned off.
+ * 1 - (default level) Pointers to IFO files and some data from
+ * PrimaryVolumeDescriptor are cached.
+ *
+ * @return The level of caching.
+ */
+int DVDUDFCacheLevel( dvd_reader_t *, int );
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* DVD_READER_H_INCLUDED */
diff --git a/dvdread/dvd_udf.c b/dvdread/dvd_udf.c
new file mode 100644
index 0000000000..56682e7405
--- /dev/null
+++ b/dvdread/dvd_udf.c
@@ -0,0 +1,1002 @@
+/*
+ * This code is based on dvdudf by:
+ * Christian Wolff <scarabaeus@convergence.de>.
+ *
+ * Modifications by:
+ * Billy Biggs <vektor@dumbterm.net>.
+ * Björn Englund <d4bjorn@dtek.chalmers.se>.
+ *
+ * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
+ * detailed changelog at http://svn.mplayerhq.hu/mplayer/trunk/
+ * $Id$
+ *
+ * dvdudf: parse and read the UDF volume information of a DVD Video
+ * Copyright (C) 1999 Christian Wolff for convergence integrated media
+ * GmbH The author can be reached at scarabaeus@convergence.de, the
+ * project's page is at http://linuxtv.org/dvd/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA. Or, point your browser to
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef __MINGW32__
+#include <sys/ioctl.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "dvd_reader.h"
+#include "dvd_udf.h"
+
+/* Private but located in/shared with dvd_reader.c */
+extern int UDFReadBlocksRaw( dvd_reader_t *device, uint32_t lb_number,
+ size_t block_count, unsigned char *data,
+ int encrypted );
+
+/* It's required to either fail or deliver all the blocks asked for. */
+static int DVDReadLBUDF( dvd_reader_t *device, uint32_t lb_number,
+ size_t block_count, unsigned char *data,
+ int encrypted )
+{
+ int ret;
+ size_t count = block_count;
+
+ while(count > 0) {
+
+ ret = UDFReadBlocksRaw(device, lb_number, count, data, encrypted);
+
+ if(ret <= 0) {
+ /* One of the reads failed or nothing more to read, too bad.
+ * We won't even bother returning the reads that went ok. */
+ return ret;
+ }
+
+ count -= (size_t)ret;
+ lb_number += (uint32_t)ret;
+ }
+
+ return block_count;
+}
+
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+struct Partition {
+ int valid;
+ char VolumeDesc[128];
+ uint16_t Flags;
+ uint16_t Number;
+ char Contents[32];
+ uint32_t AccessType;
+ uint32_t Start;
+ uint32_t Length;
+};
+
+struct AD {
+ uint32_t Location;
+ uint32_t Length;
+ uint8_t Flags;
+ uint16_t Partition;
+};
+
+struct extent_ad {
+ uint32_t location;
+ uint32_t length;
+};
+
+struct avdp_t {
+ struct extent_ad mvds;
+ struct extent_ad rvds;
+};
+
+struct pvd_t {
+ uint8_t VolumeIdentifier[32];
+ uint8_t VolumeSetIdentifier[128];
+};
+
+struct lbudf {
+ uint32_t lb;
+ uint8_t *data;
+};
+
+struct icbmap {
+ uint32_t lbn;
+ struct AD file;
+ uint8_t filetype;
+};
+
+struct udf_cache {
+ int avdp_valid;
+ struct avdp_t avdp;
+ int pvd_valid;
+ struct pvd_t pvd;
+ int partition_valid;
+ struct Partition partition;
+ int rooticb_valid;
+ struct AD rooticb;
+ int lb_num;
+ struct lbudf *lbs;
+ int map_num;
+ struct icbmap *maps;
+};
+
+typedef enum {
+ PartitionCache, RootICBCache, LBUDFCache, MapCache, AVDPCache, PVDCache
+} UDFCacheType;
+
+extern void *GetUDFCacheHandle(dvd_reader_t *device);
+extern void SetUDFCacheHandle(dvd_reader_t *device, void *cache);
+
+void FreeUDFCache(void *cache)
+{
+ struct udf_cache *c = (struct udf_cache *)cache;
+ if(c == NULL) {
+ return;
+ }
+ if(c->lbs) {
+ free(c->lbs);
+ }
+ if(c->maps) {
+ free(c->maps);
+ }
+ free(c);
+}
+
+
+static int GetUDFCache(dvd_reader_t *device, UDFCacheType type,
+ uint32_t nr, void *data)
+{
+ int n;
+ struct udf_cache *c;
+
+ if(DVDUDFCacheLevel(device, -1) <= 0) {
+ return 0;
+ }
+
+ c = (struct udf_cache *)GetUDFCacheHandle(device);
+
+ if(c == NULL) {
+ return 0;
+ }
+
+ switch(type) {
+ case AVDPCache:
+ if(c->avdp_valid) {
+ *(struct avdp_t *)data = c->avdp;
+ return 1;
+ }
+ break;
+ case PVDCache:
+ if(c->pvd_valid) {
+ *(struct pvd_t *)data = c->pvd;
+ return 1;
+ }
+ break;
+ case PartitionCache:
+ if(c->partition_valid) {
+ *(struct Partition *)data = c->partition;
+ return 1;
+ }
+ break;
+ case RootICBCache:
+ if(c->rooticb_valid) {
+ *(struct AD *)data = c->rooticb;
+ return 1;
+ }
+ break;
+ case LBUDFCache:
+ for(n = 0; n < c->lb_num; n++) {
+ if(c->lbs[n].lb == nr) {
+ *(uint8_t **)data = c->lbs[n].data;
+ return 1;
+ }
+ }
+ break;
+ case MapCache:
+ for(n = 0; n < c->map_num; n++) {
+ if(c->maps[n].lbn == nr) {
+ *(struct icbmap *)data = c->maps[n];
+ return 1;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int SetUDFCache(dvd_reader_t *device, UDFCacheType type,
+ uint32_t nr, void *data)
+{
+ int n;
+ struct udf_cache *c;
+
+ if(DVDUDFCacheLevel(device, -1) <= 0) {
+ return 0;
+ }
+
+ c = (struct udf_cache *)GetUDFCacheHandle(device);
+
+ if(c == NULL) {
+ c = calloc(1, sizeof(struct udf_cache));
+ // fprintf(stderr, "calloc: %d\n", sizeof(struct udf_cache));
+ if(c == NULL) {
+ return 0;
+ }
+ SetUDFCacheHandle(device, c);
+ }
+
+
+ switch(type) {
+ case AVDPCache:
+ c->avdp = *(struct avdp_t *)data;
+ c->avdp_valid = 1;
+ break;
+ case PVDCache:
+ c->pvd = *(struct pvd_t *)data;
+ c->pvd_valid = 1;
+ break;
+ case PartitionCache:
+ c->partition = *(struct Partition *)data;
+ c->partition_valid = 1;
+ break;
+ case RootICBCache:
+ c->rooticb = *(struct AD *)data;
+ c->rooticb_valid = 1;
+ break;
+ case LBUDFCache:
+ for(n = 0; n < c->lb_num; n++) {
+ if(c->lbs[n].lb == nr) {
+ /* replace with new data */
+ c->lbs[n].data = *(uint8_t **)data;
+ c->lbs[n].lb = nr;
+ return 1;
+ }
+ }
+ c->lb_num++;
+ c->lbs = realloc(c->lbs, c->lb_num * sizeof(struct lbudf));
+ /*
+ fprintf(stderr, "realloc lb: %d * %d = %d\n",
+ c->lb_num, sizeof(struct lbudf),
+ c->lb_num * sizeof(struct lbudf));
+ */
+ if(c->lbs == NULL) {
+ c->lb_num = 0;
+ return 0;
+ }
+ c->lbs[n].data = *(uint8_t **)data;
+ c->lbs[n].lb = nr;
+ break;
+ case MapCache:
+ for(n = 0; n < c->map_num; n++) {
+ if(c->maps[n].lbn == nr) {
+ /* replace with new data */
+ c->maps[n] = *(struct icbmap *)data;
+ c->maps[n].lbn = nr;
+ return 1;
+ }
+ }
+ c->map_num++;
+ c->maps = realloc(c->maps, c->map_num * sizeof(struct icbmap));
+ /*
+ fprintf(stderr, "realloc maps: %d * %d = %d\n",
+ c->map_num, sizeof(struct icbmap),
+ c->map_num * sizeof(struct icbmap));
+ */
+ if(c->maps == NULL) {
+ c->map_num = 0;
+ return 0;
+ }
+ c->maps[n] = *(struct icbmap *)data;
+ c->maps[n].lbn = nr;
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/* For direct data access, LSB first */
+#define GETN1(p) ((uint8_t)data[p])
+#define GETN2(p) ((uint16_t)data[p] | ((uint16_t)data[(p) + 1] << 8))
+#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;
+ struct icbmap tmpmap;
+
+ lbnum = partition->Start + ICB.Location;
+ tmpmap.lbn = lbnum;
+ if(GetUDFCache(device, MapCache, lbnum, &tmpmap)) {
+ *FileType = tmpmap.filetype;
+ *File = tmpmap.file;
+ return 1;
+ }
+
+ do {
+ if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) {
+ TagID = 0;
+ } else {
+ UDFDescriptor( LogBlock, &TagID );
+ }
+
+ if( TagID == 261 ) {
+ UDFFileEntry( LogBlock, FileType, partition, File );
+ tmpmap.file = *File;
+ tmpmap.filetype = *FileType;
+ SetUDFCache(device, MapCache, tmpmap.lbn, &tmpmap);
+ return 1;
+ };
+ } while( ( lbnum <= partition->Start + ICB.Location + ( ICB.Length - 1 )
+ / 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,
+ int cache_file_info)
+{
+ 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;
+ uint8_t *cached_dir = NULL;
+ uint32_t dir_lba;
+ struct AD tmpICB;
+ int found = 0;
+ int in_cache = 0;
+
+ /* Scan dir for ICB of file */
+ lbnum = partition->Start + Dir.Location;
+
+ if(DVDUDFCacheLevel(device, -1) > 0) {
+ /* caching */
+
+ if(!GetUDFCache(device, LBUDFCache, lbnum, &cached_dir)) {
+ dir_lba = (Dir.Length + DVD_VIDEO_LB_LEN) / DVD_VIDEO_LB_LEN;
+ if((cached_dir = malloc(dir_lba * DVD_VIDEO_LB_LEN)) == NULL) {
+ return 0;
+ }
+ if( DVDReadLBUDF( device, lbnum, dir_lba, cached_dir, 0) <= 0 ) {
+ free(cached_dir);
+ cached_dir = NULL;
+ }
+ /*
+ if(cached_dir) {
+ fprintf(stderr, "malloc dir: %d\n",
+ dir_lba * DVD_VIDEO_LB_LEN);
+ }
+ */
+ SetUDFCache(device, LBUDFCache, lbnum, &cached_dir);
+ } else {
+ in_cache = 1;
+ }
+
+ if(cached_dir == NULL) {
+ return 0;
+ }
+
+ p = 0;
+
+ while( p < Dir.Length ) {
+ UDFDescriptor( &cached_dir[ p ], &TagID );
+ if( TagID == 257 ) {
+ p += UDFFileIdentifier( &cached_dir[ p ], &filechar,
+ filename, &tmpICB );
+ if(cache_file_info && !in_cache) {
+ uint8_t tmpFiletype;
+ struct AD tmpFile;
+
+ if( !strcasecmp( FileName, filename ) ) {
+ *FileICB = tmpICB;
+ found = 1;
+
+ }
+ UDFMapICB(device, tmpICB, &tmpFiletype,
+ partition, &tmpFile);
+ } else {
+ if( !strcasecmp( FileName, filename ) ) {
+ *FileICB = tmpICB;
+ return 1;
+ }
+ }
+ } else {
+ if(cache_file_info && (!in_cache) && found) {
+ return 1;
+ }
+ return 0;
+ }
+ }
+ if(cache_file_info && (!in_cache) && found) {
+ return 1;
+ }
+ return 0;
+ }
+
+ if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) {
+ return 0;
+ }
+
+ 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;
+}
+
+
+static int UDFGetAVDP( dvd_reader_t *device,
+ struct avdp_t *avdp)
+{
+ uint8_t Anchor[ DVD_VIDEO_LB_LEN ];
+ uint32_t lbnum, MVDS_location, MVDS_length;
+ uint16_t TagID;
+ uint32_t lastsector;
+ int terminate;
+ struct avdp_t;
+
+ if(GetUDFCache(device, AVDPCache, 0, avdp)) {
+ return 1;
+ }
+
+ /* Find Anchor */
+ lastsector = 0;
+ lbnum = 256; /* Try #1, prime anchor */
+ terminate = 0;
+
+ 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 );
+ avdp->mvds.location = MVDS_location;
+ avdp->mvds.length = MVDS_length;
+
+ /* Backup volume descriptor */
+ UDFExtentAD( &Anchor[ 24 ], &MVDS_length, &MVDS_location );
+ avdp->rvds.location = MVDS_location;
+ avdp->rvds.length = MVDS_length;
+
+ SetUDFCache(device, AVDPCache, 0, avdp);
+
+ return 1;
+}
+
+/**
+ * Looks for partition on the disc. Returns 1 if partition found, 0 on error.
+ * partnum: Number of the partition, starting at 0.
+ * part: structure to fill with the partition information
+ */
+static int UDFFindPartition( dvd_reader_t *device, int partnum,
+ struct Partition *part )
+{
+ uint8_t LogBlock[ DVD_VIDEO_LB_LEN ];
+ uint32_t lbnum, MVDS_location, MVDS_length;
+ uint16_t TagID;
+ int i, volvalid;
+ struct avdp_t avdp;
+
+
+ if(!UDFGetAVDP(device, &avdp)) {
+ return 0;
+ }
+
+ /* Main volume descriptor */
+ MVDS_location = avdp.mvds.location;
+ MVDS_length = avdp.mvds.length;
+
+ part->valid = 0;
+ volvalid = 0;
+ part->VolumeDesc[ 0 ] = '\0';
+ i = 1;
+ do {
+ /* Find Volume Descriptor */
+ lbnum = MVDS_location;
+ do {
+
+ if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) {
+ TagID = 0;
+ } else {
+ UDFDescriptor( LogBlock, &TagID );
+ }
+
+ if( ( TagID == 5 ) && ( !part->valid ) ) {
+ /* Partition Descriptor */
+ UDFPartition( LogBlock, &part->Flags, &part->Number,
+ part->Contents, &part->Start, &part->Length );
+ part->valid = ( partnum == part->Number );
+ } else if( ( TagID == 6 ) && ( !volvalid ) ) {
+ /* Logical Volume Descriptor */
+ if( UDFLogVolume( LogBlock, part->VolumeDesc ) ) {
+ /* TODO: sector size wrong! */
+ } else {
+ volvalid = 1;
+ }
+ }
+
+ } while( ( lbnum <= MVDS_location + ( MVDS_length - 1 )
+ / DVD_VIDEO_LB_LEN ) && ( TagID != 8 )
+ && ( ( !part->valid ) || ( !volvalid ) ) );
+
+ if( ( !part->valid) || ( !volvalid ) ) {
+ /* Backup volume descriptor */
+ MVDS_location = avdp.mvds.location;
+ MVDS_length = avdp.mvds.length;
+ }
+ } while( i-- && ( ( !part->valid ) || ( !volvalid ) ) );
+
+ /* 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 );
+
+
+ if(!(GetUDFCache(device, PartitionCache, 0, &partition) &&
+ GetUDFCache(device, RootICBCache, 0, &RootICB))) {
+ /* Find partition, 0 is the standard location for DVD Video.*/
+ if( !UDFFindPartition( device, 0, &partition ) ) return 0;
+ SetUDFCache(device, PartitionCache, 0, &partition);
+
+ /* Find root dir ICB */
+ lbnum = partition.Start;
+ 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;
+ SetUDFCache(device, RootICBCache, 0, &RootICB);
+ }
+
+ /* Find root dir */
+ if( !UDFMapICB( device, RootICB, &filetype, &partition, &File ) ) return 0;
+ if( filetype != 4 ) return 0; /* Root dir should be dir */
+
+ {
+ int cache_file_info = 0;
+ /* Tokenize filepath */
+ token = strtok(tokenline, "/");
+
+ while( token != NULL ) {
+
+ if( !UDFScanDir( device, File, token, &partition, &ICB,
+ cache_file_info)) {
+ return 0;
+ }
+ if( !UDFMapICB( device, ICB, &filetype, &partition, &File ) ) {
+ return 0;
+ }
+ if(!strcmp(token, "VIDEO_TS")) {
+ cache_file_info = 1;
+ }
+ token = strtok( NULL, "/" );
+ }
+ }
+
+ /* Sanity check. */
+ if( File.Partition != 0 ) return 0;
+
+ *filesize = File.Length;
+ /* Hack to not return partition.Start for empty files. */
+ if( !File.Location )
+ return 0;
+ else
+ return partition.Start + File.Location;
+}
+
+
+
+/**
+ * Gets a Descriptor .
+ * Returns 1 if descriptor found, 0 on error.
+ * id, tagid of descriptor
+ * bufsize, size of BlockBuf (must be >= DVD_VIDEO_LB_LEN).
+ */
+static int UDFGetDescriptor( dvd_reader_t *device, int id,
+ uint8_t *descriptor, int bufsize)
+{
+ uint32_t lbnum, MVDS_location, MVDS_length;
+ struct avdp_t avdp;
+ uint16_t TagID;
+ uint32_t lastsector;
+ int i, terminate;
+ int desc_found = 0;
+ /* Find Anchor */
+ lastsector = 0;
+ lbnum = 256; /* Try #1, prime anchor */
+ terminate = 0;
+ if(bufsize < DVD_VIDEO_LB_LEN) {
+ return 0;
+ }
+
+ if(!UDFGetAVDP(device, &avdp)) {
+ return 0;
+ }
+
+ /* Main volume descriptor */
+ MVDS_location = avdp.mvds.location;
+ MVDS_length = avdp.mvds.length;
+
+ i = 1;
+ do {
+ /* Find Descriptor */
+ lbnum = MVDS_location;
+ do {
+
+ if( DVDReadLBUDF( device, lbnum++, 1, descriptor, 0 ) <= 0 ) {
+ TagID = 0;
+ } else {
+ UDFDescriptor( descriptor, &TagID );
+ }
+
+ if( (TagID == id) && ( !desc_found ) ) {
+ /* Descriptor */
+ desc_found = 1;
+ }
+ } while( ( lbnum <= MVDS_location + ( MVDS_length - 1 )
+ / DVD_VIDEO_LB_LEN ) && ( TagID != 8 )
+ && ( !desc_found) );
+
+ if( !desc_found ) {
+ /* Backup volume descriptor */
+ MVDS_location = avdp.rvds.location;
+ MVDS_length = avdp.rvds.length;
+ }
+ } while( i-- && ( !desc_found ) );
+
+ return desc_found;
+}
+
+
+static int UDFGetPVD(dvd_reader_t *device, struct pvd_t *pvd)
+{
+ uint8_t pvd_buf[DVD_VIDEO_LB_LEN];
+
+ if(GetUDFCache(device, PVDCache, 0, pvd)) {
+ return 1;
+ }
+
+ if(!UDFGetDescriptor( device, 1, pvd_buf, sizeof(pvd_buf))) {
+ return 0;
+ }
+
+ memcpy(pvd->VolumeIdentifier, &pvd_buf[24], 32);
+ memcpy(pvd->VolumeSetIdentifier, &pvd_buf[72], 128);
+ SetUDFCache(device, PVDCache, 0, pvd);
+
+ return 1;
+}
+
+/**
+ * Gets the Volume Identifier string, in 8bit unicode (latin-1)
+ * volid, place to put the string
+ * volid_size, size of the buffer volid points to
+ * returns the size of buffer needed for all data
+ */
+int UDFGetVolumeIdentifier(dvd_reader_t *device, char *volid,
+ unsigned int volid_size)
+{
+ struct pvd_t pvd;
+ unsigned int volid_len;
+
+ /* get primary volume descriptor */
+ if(!UDFGetPVD(device, &pvd)) {
+ return 0;
+ }
+
+ volid_len = pvd.VolumeIdentifier[31];
+ if(volid_len > 31) {
+ /* this field is only 32 bytes something is wrong */
+ volid_len = 31;
+ }
+ if(volid_size > volid_len) {
+ volid_size = volid_len;
+ }
+ Unicodedecode(pvd.VolumeIdentifier, volid_size, volid);
+
+ return volid_len;
+}
+
+/**
+ * Gets the Volume Set Identifier, as a 128-byte dstring (not decoded)
+ * WARNING This is not a null terminated string
+ * volsetid, place to put the data
+ * volsetid_size, size of the buffer volsetid points to
+ * the buffer should be >=128 bytes to store the whole volumesetidentifier
+ * returns the size of the available volsetid information (128)
+ * or 0 on error
+ */
+int UDFGetVolumeSetIdentifier(dvd_reader_t *device, uint8_t *volsetid,
+ unsigned int volsetid_size)
+{
+ struct pvd_t pvd;
+
+ /* get primary volume descriptor */
+ if(!UDFGetPVD(device, &pvd)) {
+ return 0;
+ }
+
+
+ if(volsetid_size > 128) {
+ volsetid_size = 128;
+ }
+
+ memcpy(volsetid, pvd.VolumeSetIdentifier, volsetid_size);
+
+ return 128;
+}
diff --git a/dvdread/dvd_udf.h b/dvdread/dvd_udf.h
new file mode 100644
index 0000000000..b600983d9a
--- /dev/null
+++ b/dvdread/dvd_udf.h
@@ -0,0 +1,59 @@
+#ifndef DVD_UDF_H_INCLUDED
+#define DVD_UDF_H_INCLUDED
+
+/*
+ * This code is based on dvdudf by:
+ * Christian Wolff <scarabaeus@convergence.de>.
+ *
+ * Modifications by:
+ * Billy Biggs <vektor@dumbterm.net>.
+ * Björn Englund <d4bjorn@dtek.chalmers.se>.
+ *
+ * dvdudf: parse and read the UDF volume information of a DVD Video
+ * Copyright (C) 1999 Christian Wolff for convergence integrated media
+ * GmbH The author can be reached at scarabaeus@convergence.de, the
+ * project's page is at http://linuxtv.org/dvd/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA. Or, point your browser to
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <inttypes.h>
+
+#include "dvd_reader.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Looks for a file on the UDF disc/imagefile and returns the block number
+ * where it begins, or 0 if it is not found. The filename should be an
+ * absolute pathname on the UDF filesystem, starting with '/'. For example,
+ * '/VIDEO_TS/VTS_01_1.IFO'. On success, filesize will be set to the size of
+ * the file in bytes.
+ */
+uint32_t UDFFindFile( dvd_reader_t *device, char *filename, uint32_t *size );
+
+void FreeUDFCache(void *cache);
+int UDFGetVolumeIdentifier(dvd_reader_t *device,
+ char *volid, unsigned int volid_size);
+int UDFGetVolumeSetIdentifier(dvd_reader_t *device,
+ uint8_t *volsetid, unsigned int volsetid_size);
+#ifdef __cplusplus
+};
+#endif
+#endif /* DVD_UDF_H_INCLUDED */
diff --git a/dvdread/dvdread_internal.h b/dvdread/dvdread_internal.h
new file mode 100644
index 0000000000..5c8a535cc0
--- /dev/null
+++ b/dvdread/dvdread_internal.h
@@ -0,0 +1,8 @@
+#ifndef DVDREAD_INTERNAL_H
+#define DVDREAD_INTERNAL_H
+
+
+#define CHECK_VALUE(arg)
+
+
+#endif /* DVDREAD_INTERNAL_H */
diff --git a/dvdread/ifo_print.c b/dvdread/ifo_print.c
new file mode 100644
index 0000000000..17820c77a4
--- /dev/null
+++ b/dvdread/ifo_print.c
@@ -0,0 +1,1152 @@
+/*
+ * Copyright (C) 2000, 2001, 2002, 2003
+ * Björn Englund <d4bjorn@dtek.chalmers.se>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
+ * detailed changelog at http://svn.mplayerhq.hu/mplayer/trunk/
+ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "ifo_types.h"
+#include "ifo_read.h"
+#include "ifo_print.h"
+#include "dvdread_internal.h"
+
+/* Put this in some other file / package? It's used in nav_print too. */
+static void ifoPrint_time(dvd_time_t *dtime) {
+ const char *rate;
+ CHECK_VALUE((dtime->hour>>4) < 0xa && (dtime->hour&0xf) < 0xa);
+ CHECK_VALUE((dtime->minute>>4) < 0x7 && (dtime->minute&0xf) < 0xa);
+ CHECK_VALUE((dtime->second>>4) < 0x7 && (dtime->second&0xf) < 0xa);
+ CHECK_VALUE((dtime->frame_u&0xf) < 0xa);
+
+ printf("%02x:%02x:%02x.%02x",
+ dtime->hour,
+ dtime->minute,
+ dtime->second,
+ dtime->frame_u & 0x3f);
+ switch((dtime->frame_u & 0xc0) >> 6) {
+ case 1:
+ rate = "25.00";
+ break;
+ case 3:
+ rate = "29.97";
+ break;
+ default:
+ if(dtime->hour == 0 && dtime->minute == 0
+ && dtime->second == 0 && dtime->frame_u == 0)
+ rate = "no";
+ else
+ rate = "(please send a bug report)";
+ break;
+ }
+ printf(" @ %s fps", rate);
+}
+
+/* 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(video_attr_t *attr) {
+
+ /* The following test is shorter but not correct ISO C,
+ memcmp(attr,my_friendly_zeros, sizeof(video_attr_t)) */
+ if(attr->mpeg_version == 0
+ && attr->video_format == 0
+ && attr->display_aspect_ratio == 0
+ && attr->permitted_df == 0
+ && attr->unknown1 == 0
+ && attr->line21_cc_1 == 0
+ && attr->line21_cc_2 == 0
+ && attr->bit_rate == 0
+ && attr->video_format == 0
+ && attr->letterboxed == 0
+ && attr->film_mode == 0) {
+ printf("-- Unspecified --");
+ return;
+ }
+
+ switch(attr->mpeg_version) {
+ case 0:
+ printf("mpeg1 ");
+ break;
+ case 1:
+ printf("mpeg2 ");
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ switch(attr->video_format) {
+ case 0:
+ printf("ntsc ");
+ break;
+ case 1:
+ printf("pal ");
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ switch(attr->display_aspect_ratio) {
+ case 0:
+ printf("4:3 ");
+ break;
+ case 3:
+ printf("16:9 ");
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ // Wide is allways allowed..!!!
+ switch(attr->permitted_df) {
+ case 0:
+ printf("pan&scan+letterboxed ");
+ break;
+ case 1:
+ printf("only pan&scan "); //??
+ break;
+ case 2:
+ printf("only letterboxed ");
+ break;
+ case 3:
+ // not specified
+ break;
+ default:
+ printf("(please send a bug report)");
+ }
+
+ printf("U%x ", attr->unknown1);
+ CHECK_VALUE(!attr->unknown1);
+
+ if(attr->line21_cc_1 || attr->line21_cc_2) {
+ printf("NTSC CC ");
+ if(attr->line21_cc_1)
+ printf("1 ");
+ if(attr->line21_cc_2)
+ printf("2 ");
+ }
+
+ switch(attr->bit_rate) {
+ case 0:
+ printf("Variable Bit Rate ");
+ break;
+ case 1:
+ printf("Constant Bit Rate ");
+ break;
+ default:
+ printf("(please send a bug report)");
+ }
+
+ {
+ int height = 480;
+ if(attr->video_format != 0)
+ height = 576;
+ switch(attr->picture_size) {
+ case 0:
+ printf("720x%d ", height);
+ break;
+ case 1:
+ printf("704x%d ", height);
+ break;
+ case 2:
+ printf("352x%d ", height);
+ break;
+ case 3:
+ printf("352x%d ", height/2);
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+ }
+
+ if(attr->letterboxed) {
+ printf("source letterboxed ");
+ }
+
+ if(attr->film_mode) {
+ printf("film");
+ } else {
+ printf("video"); //camera
+ }
+}
+
+static void ifoPrint_audio_attributes(audio_attr_t *attr) {
+
+ if(attr->audio_format == 0
+ && attr->multichannel_extension == 0
+ && attr->lang_type == 0
+ && attr->application_mode == 0
+ && attr->quantization == 0
+ && attr->sample_frequency == 0
+ && attr->channels == 0
+ && attr->lang_code == 0
+ && attr->lang_extension == 0
+ && attr->code_extension == 0
+ && attr->unknown3 == 0
+ && attr->unknown1 == 0) {
+ printf("-- Unspecified --");
+ return;
+ }
+
+ switch(attr->audio_format) {
+ case 0:
+ printf("ac3 ");
+ break;
+ case 1:
+ printf("(please send a bug report) ");
+ break;
+ case 2:
+ printf("mpeg1 ");
+ break;
+ case 3:
+ printf("mpeg2ext ");
+ break;
+ case 4:
+ printf("lpcm ");
+ break;
+ case 5:
+ printf("(please send a bug report) ");
+ break;
+ case 6:
+ printf("dts ");
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ if(attr->multichannel_extension)
+ printf("multichannel_extension ");
+
+ switch(attr->lang_type) {
+ case 0:
+ // not specified
+ CHECK_VALUE(attr->lang_code == 0 || attr->lang_code == 0xffff);
+ break;
+ case 1:
+ printf("%c%c (%c) ", attr->lang_code>>8, attr->lang_code & 0xff,
+ attr->lang_extension ? attr->lang_extension : ' ');
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ switch(attr->application_mode) {
+ case 0:
+ // not specified
+ break;
+ case 1:
+ printf("karaoke mode ");
+ break;
+ case 2:
+ printf("surround sound mode ");
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ switch(attr->quantization) {
+ case 0:
+ printf("16bit ");
+ break;
+ case 1:
+ printf("20bit ");
+ break;
+ case 2:
+ printf("24bit ");
+ break;
+ case 3:
+ printf("drc ");
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ switch(attr->sample_frequency) {
+ case 0:
+ printf("48kHz ");
+ break;
+ case 1:
+ printf("??kHz ");
+ break;
+ default:
+ printf("sample_frequency %i (please send a bug report) ",
+ attr->sample_frequency);
+ }
+
+ printf("%dCh ", attr->channels + 1);
+
+ switch(attr->code_extension) {
+ case 0:
+ printf("Not specified ");
+ break;
+ case 1: // Normal audio
+ printf("Normal Caption ");
+ break;
+ case 2: // visually imparied
+ printf("Audio for visually impaired ");
+ break;
+ case 3: // Directors 1
+ printf("Director's comments 1 ");
+ break;
+ case 4: // Directors 2
+ printf("Director's comments 2 ");
+ break;
+ //case 4: // Music score ?
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ printf("%d ", attr->unknown3);
+ if(attr->application_mode == 1) {
+ printf("ca=%d ", attr->app_info.karaoke.channel_assignment);
+ printf("%d ", attr->app_info.karaoke.version);
+ if(attr->app_info.karaoke.mc_intro)
+ printf("mc intro ");
+ printf("%s ", attr->app_info.karaoke.mode ? "duet" : "solo");
+ printf("%d ", attr->app_info.karaoke.unknown4);
+ }
+ if(attr->application_mode == 2) {
+ if(attr->app_info.surround.dolby_encoded) {
+ printf("dolby surround ");
+ }
+ printf("%d ", attr->app_info.surround.unknown5);
+ printf("%d ", attr->app_info.surround.unknown6);
+ }
+}
+
+static void ifoPrint_subp_attributes(subp_attr_t *attr) {
+
+ if(attr->type == 0
+ && attr->code_mode == 0
+ && attr->lang_code == 0
+ && attr->lang_extension == 0
+ && attr->zero1 == 0
+ && attr->zero2 == 0
+ && attr->code_extension == 0) {
+ printf("-- Unspecified --");
+ return;
+ }
+
+ switch(attr->code_mode) {
+ case 0:
+ printf("Coding Mode RLE ");
+ break;
+ case 1:
+ printf("Coding Mode Extended ");
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ if(attr->type == 1) {
+ if(isalpha((int)(attr->lang_code >> 8))
+ && isalpha((int)(attr->lang_code & 0xff))) {
+ printf("%c%c ", attr->lang_code >> 8, attr->lang_code & 0xff);
+ } else {
+ printf("%02x%02x ", attr->lang_code >> 8, attr->lang_code & 0xff);
+ }
+ } else {
+ printf("lang not specified ");
+ }
+
+ printf("%d ", attr->zero1);
+ printf("%d ", attr->zero2);
+ printf("%d ", attr->code_extension);
+
+ /* Is this correct? should it not be subp_code_ext here instead? */
+ switch(attr->lang_extension) {
+ case 0:
+ printf("Not specified ");
+ break;
+ case 1:
+ printf("Caption with normal size character ");
+ break;
+ case 2:
+ printf("Caption with bigger size character ");
+ break;
+ case 3:
+ printf("Caption for children ");
+ break;
+ case 4:
+ printf("reserved ");
+ break;
+ case 5:
+ printf("Closed Caption with normal size character ");
+ break;
+ case 6:
+ printf("Closed Caption with bigger size character ");
+ break;
+ case 7:
+ printf("Closed Caption for children ");
+ break;
+ case 8:
+ printf("reserved ");
+ break;
+ case 9:
+ printf("Forced Caption");
+ break;
+ case 10:
+ printf("reserved ");
+ break;
+ case 11:
+ printf("reserved ");
+ break;
+ case 12:
+ printf("reserved ");
+ break;
+ case 13:
+ printf("Director's comments with normal size character ");
+ break;
+ case 14:
+ printf("Director's comments with bigger size character ");
+ break;
+ case 15:
+ printf("Director's comments for children ");
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+}
+
+
+static void ifoPrint_USER_OPS(user_ops_t *user_ops) {
+ uint32_t uops;
+ unsigned char *ptr = (unsigned char *)user_ops;
+
+ uops = (*ptr++ << 24);
+ uops |= (*ptr++ << 16);
+ uops |= (*ptr++ << 8);
+ uops |= (*ptr++);
+
+ if(uops == 0) {
+ printf("None\n");
+ } else if(uops == 0x01ffffff) {
+ printf("All\n");
+ } else {
+ if(user_ops->title_or_time_play)
+ printf("Title or Time Play, ");
+ if(user_ops->chapter_search_or_play)
+ printf("Chapter Search or Play, ");
+ if(user_ops->title_play)
+ printf("Title Play, ");
+ if(user_ops->stop)
+ printf("Stop, ");
+ if(user_ops->go_up)
+ printf("Go Up, ");
+ if(user_ops->time_or_chapter_search)
+ printf("Time or Chapter Search, ");
+ if(user_ops->prev_or_top_pg_search)
+ printf("Prev or Top PG Search, ");
+ if(user_ops->next_pg_search)
+ printf("Next PG Search, ");
+ if(user_ops->forward_scan)
+ printf("Forward Scan, ");
+ if(user_ops->backward_scan)
+ printf("Backward Scan, ");
+ if(user_ops->title_menu_call)
+ printf("Title Menu Call, ");
+ if(user_ops->root_menu_call)
+ printf("Root Menu Call, ");
+ if(user_ops->subpic_menu_call)
+ printf("SubPic Menu Call, ");
+ if(user_ops->audio_menu_call)
+ printf("Audio Menu Call, ");
+ if(user_ops->angle_menu_call)
+ printf("Angle Menu Call, ");
+ if(user_ops->chapter_menu_call)
+ printf("Chapter Menu Call, ");
+ if(user_ops->resume)
+ printf("Resume, ");
+ if(user_ops->button_select_or_activate)
+ printf("Button Select or Activate, ");
+ if(user_ops->still_off)
+ printf("Still Off, ");
+ if(user_ops->pause_on)
+ printf("Pause On, ");
+ if(user_ops->audio_stream_change)
+ printf("Audio Stream Change, ");
+ if(user_ops->subpic_stream_change)
+ printf("SubPic Stream Change, ");
+ if(user_ops->angle_change)
+ printf("Angle Change, ");
+ if(user_ops->karaoke_audio_pres_mode_change)
+ printf("Karaoke Audio Pres Mode Change, ");
+ if(user_ops->video_pres_mode_change)
+ printf("Video Pres Mode Change, ");
+ printf("\n");
+ }
+}
+
+
+void ifoPrint_VMGI_MAT(vmgi_mat_t *vmgi_mat) {
+
+ printf("VMG Identifier: %.12s\n", vmgi_mat->vmg_identifier);
+ printf("Last Sector of VMG: %08x\n", vmgi_mat->vmg_last_sector);
+ printf("Last Sector of VMGI: %08x\n", vmgi_mat->vmgi_last_sector);
+ printf("Specification version number: %01x.%01x\n",
+ vmgi_mat->specification_version >> 4,
+ vmgi_mat->specification_version & 0xf);
+ /* Byte 2 of 'VMG Category' (00xx0000) is the Region Code */
+ printf("VMG Category: %08x\n", vmgi_mat->vmg_category);
+ printf("VMG Number of Volumes: %i\n", vmgi_mat->vmg_nr_of_volumes);
+ printf("VMG This Volume: %i\n", vmgi_mat->vmg_this_volume_nr);
+ printf("Disc side %i\n", vmgi_mat->disc_side);
+ printf("VMG Number of Title Sets %i\n", vmgi_mat->vmg_nr_of_title_sets);
+ printf("Provider ID: %.32s\n", vmgi_mat->provider_identifier);
+ printf("VMG POS Code: %08x", (uint32_t)(vmgi_mat->vmg_pos_code >> 32));
+ printf("%08x\n", (uint32_t)vmgi_mat->vmg_pos_code);
+ printf("End byte of VMGI_MAT: %08x\n", vmgi_mat->vmgi_last_byte);
+ printf("Start byte of First Play PGC FP PGC: %08x\n",
+ vmgi_mat->first_play_pgc);
+ printf("Start sector of VMGM_VOBS: %08x\n", vmgi_mat->vmgm_vobs);
+ printf("Start sector of TT_SRPT: %08x\n", vmgi_mat->tt_srpt);
+ printf("Start sector of VMGM_PGCI_UT: %08x\n", vmgi_mat->vmgm_pgci_ut);
+ printf("Start sector of PTL_MAIT: %08x\n", vmgi_mat->ptl_mait);
+ printf("Start sector of VTS_ATRT: %08x\n", vmgi_mat->vts_atrt);
+ printf("Start sector of TXTDT_MG: %08x\n", vmgi_mat->txtdt_mgi);
+ printf("Start sector of VMGM_C_ADT: %08x\n", vmgi_mat->vmgm_c_adt);
+ printf("Start sector of VMGM_VOBU_ADMAP: %08x\n",
+ vmgi_mat->vmgm_vobu_admap);
+ printf("Video attributes of VMGM_VOBS: ");
+ ifoPrint_video_attributes(&vmgi_mat->vmgm_video_attr);
+ printf("\n");
+ printf("VMGM Number of Audio attributes: %i\n",
+ vmgi_mat->nr_of_vmgm_audio_streams);
+ if(vmgi_mat->nr_of_vmgm_audio_streams > 0) {
+ printf("\tstream %i status: ", 1);
+ ifoPrint_audio_attributes(&vmgi_mat->vmgm_audio_attr);
+ printf("\n");
+ }
+ printf("VMGM Number of Sub-picture attributes: %i\n",
+ vmgi_mat->nr_of_vmgm_subp_streams);
+ if(vmgi_mat->nr_of_vmgm_subp_streams > 0) {
+ printf("\tstream %2i status: ", 1);
+ ifoPrint_subp_attributes(&vmgi_mat->vmgm_subp_attr);
+ printf("\n");
+ }
+}
+
+
+void ifoPrint_VTSI_MAT(vtsi_mat_t *vtsi_mat) {
+ int i;
+
+ printf("VTS Identifier: %.12s\n", vtsi_mat->vts_identifier);
+ printf("Last Sector of VTS: %08x\n", vtsi_mat->vts_last_sector);
+ printf("Last Sector of VTSI: %08x\n", vtsi_mat->vtsi_last_sector);
+ printf("Specification version number: %01x.%01x\n",
+ vtsi_mat->specification_version>>4,
+ vtsi_mat->specification_version&0xf);
+ printf("VTS Category: %08x\n", vtsi_mat->vts_category);
+ printf("End byte of VTSI_MAT: %08x\n", vtsi_mat->vtsi_last_byte);
+ printf("Start sector of VTSM_VOBS: %08x\n", vtsi_mat->vtsm_vobs);
+ printf("Start sector of VTSTT_VOBS: %08x\n", vtsi_mat->vtstt_vobs);
+ printf("Start sector of VTS_PTT_SRPT: %08x\n", vtsi_mat->vts_ptt_srpt);
+ printf("Start sector of VTS_PGCIT: %08x\n", vtsi_mat->vts_pgcit);
+ printf("Start sector of VTSM_PGCI_UT: %08x\n", vtsi_mat->vtsm_pgci_ut);
+ printf("Start sector of VTS_TMAPT: %08x\n", vtsi_mat->vts_tmapt);
+ printf("Start sector of VTSM_C_ADT: %08x\n", vtsi_mat->vtsm_c_adt);
+ printf("Start sector of VTSM_VOBU_ADMAP: %08x\n",vtsi_mat->vtsm_vobu_admap);
+ printf("Start sector of VTS_C_ADT: %08x\n", vtsi_mat->vts_c_adt);
+ printf("Start sector of VTS_VOBU_ADMAP: %08x\n", vtsi_mat->vts_vobu_admap);
+
+ printf("Video attributes of VTSM_VOBS: ");
+ ifoPrint_video_attributes(&vtsi_mat->vtsm_video_attr);
+ printf("\n");
+
+ printf("VTSM Number of Audio attributes: %i\n",
+ vtsi_mat->nr_of_vtsm_audio_streams);
+ if(vtsi_mat->nr_of_vtsm_audio_streams > 0) {
+ printf("\tstream %i status: ", 1);
+ ifoPrint_audio_attributes(&vtsi_mat->vtsm_audio_attr);
+ printf("\n");
+ }
+
+ printf("VTSM Number of Sub-picture attributes: %i\n",
+ vtsi_mat->nr_of_vtsm_subp_streams);
+ if(vtsi_mat->nr_of_vtsm_subp_streams > 0) {
+ printf("\tstream %2i status: ", 1);
+ ifoPrint_subp_attributes(&vtsi_mat->vtsm_subp_attr);
+ printf("\n");
+ }
+
+ printf("Video attributes of VTS_VOBS: ");
+ ifoPrint_video_attributes(&vtsi_mat->vts_video_attr);
+ printf("\n");
+
+ printf("VTS Number of Audio attributes: %i\n",
+ vtsi_mat->nr_of_vts_audio_streams);
+ for(i = 0; i < vtsi_mat->nr_of_vts_audio_streams; i++) {
+ printf("\tstream %i status: ", i);
+ ifoPrint_audio_attributes(&vtsi_mat->vts_audio_attr[i]);
+ printf("\n");
+ }
+
+ printf("VTS Number of Subpicture attributes: %i\n",
+ vtsi_mat->nr_of_vts_subp_streams);
+ for(i = 0; i < vtsi_mat->nr_of_vts_subp_streams; i++) {
+ printf("\tstream %2i status: ", i);
+ ifoPrint_subp_attributes(&vtsi_mat->vts_subp_attr[i]);
+ printf("\n");
+ }
+
+ /* FIXME: Add printing of MultiChannel Extension */
+}
+
+
+static void ifoPrint_PGC_COMMAND_TBL(pgc_command_tbl_t *cmd_tbl) {
+ int i;
+
+ if(cmd_tbl == NULL) {
+ printf("No Command table present\n");
+ return;
+ }
+
+ printf("Number of Pre commands: %i\n", cmd_tbl->nr_of_pre);
+ for(i = 0; i < cmd_tbl->nr_of_pre; i++) {
+ 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(&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(&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].present) {
+ printf("Audio stream %i control: %04x\n",
+ i, pgc->audio_control[i]);
+ }
+ }
+
+ for(i = 0; i < 32; i++) {
+ if(pgc->subp_control[i].present) {
+ printf("Subpicture stream %2i control: %08x\n",
+ i, pgc->subp_control[i]);
+ }
+ }
+
+ printf("Next PGC number: %i\n", pgc->next_pgc_nr);
+ printf("Prev PGC number: %i\n", pgc->prev_pgc_nr);
+ printf("GoUp PGC number: %i\n", pgc->goup_pgc_nr);
+ if(pgc->nr_of_programs != 0) {
+ printf("Still time: %i seconds (255=inf)\n", pgc->still_time);
+ if(pgc->pg_playback_mode == 0)
+ printf("PG Playback mode: Sequential\n");
+ else if(!(pgc->pg_playback_mode & 0x80))
+ printf("PG Playback mode: Random %i\n", pgc->pg_playback_mode);
+ else
+ printf("PG Playback mode: Shuffle %i\n", pgc->pg_playback_mode & 0x7f );
+ }
+
+ if(pgc->nr_of_programs != 0) {
+ for(i = 0; i < 16; i++) {
+ printf("Color %2i: %08x\n", i, pgc->palette[i]);
+ }
+ }
+
+ /* Memmory offsets to div. tables. */
+ ifoPrint_PGC_COMMAND_TBL(pgc->command_tbl);
+ ifoPrint_PGC_PROGRAM_MAP(pgc->program_map, pgc->nr_of_programs);
+ ifoPrint_CELL_PLAYBACK(pgc->cell_playback, pgc->nr_of_cells);
+ ifoPrint_CELL_POSITION(pgc->cell_position, pgc->nr_of_cells);
+}
+
+
+void ifoPrint_TT_SRPT(tt_srpt_t *tt_srpt) {
+ int i;
+
+ printf("Number of TitleTrack search pointers: %i\n",
+ tt_srpt->nr_of_srpts);
+ for(i=0;i<tt_srpt->nr_of_srpts;i++) {
+ printf("Title Track index %i\n", i + 1);
+ printf("\tTitle set number (VTS): %i",
+ tt_srpt->title[i].title_set_nr);
+ printf("\tVTS_TTN: %i\n", tt_srpt->title[i].vts_ttn);
+ printf("\tNumber of PTTs: %i\n", tt_srpt->title[i].nr_of_ptts);
+ printf("\tNumber of angles: %i\n",
+ tt_srpt->title[i].nr_of_angles);
+ printf("\tTitle playback type: %s%s%s%s%s%s%s\n",
+ tt_srpt->title[i].pb_ty.multi_or_random_pgc_title ?
+ " One Random PGC Title or Multi PGC Title" :
+ " One Sequential PGC Title",
+ tt_srpt->title[i].pb_ty.jlc_exists_in_cell_cmd ?
+ "" : ", No Link/Jump/Call exists in Cell command",
+ tt_srpt->title[i].pb_ty.jlc_exists_in_prepost_cmd ?
+ "" : ", No Link/Jump/Call exists in Pre- and/or Post-command",
+ tt_srpt->title[i].pb_ty.jlc_exists_in_button_cmd ?
+ "" : ", No Link/Jump/Call exists in Button command",
+ tt_srpt->title[i].pb_ty.jlc_exists_in_tt_dom ?
+ "" : ", No Link/Jump/Call exists in TT_DOM",
+ tt_srpt->title[i].pb_ty.chapter_search_or_play ?
+ ", UOP1 (TT_Play and PTT_Search) prohibited" : "",
+ tt_srpt->title[i].pb_ty.title_or_time_play ?
+ ", UOP0 (Time_Play and Time_Search) prohibited" : ""
+ );
+ printf("\tParental ID field: %04x\n",
+ tt_srpt->title[i].parental_id);
+ printf("\tTitle set starting sector %08x\n",
+ tt_srpt->title[i].title_set_sector);
+ }
+}
+
+
+void ifoPrint_VTS_PTT_SRPT(vts_ptt_srpt_t *vts_ptt_srpt) {
+ int i, j;
+ printf(" nr_of_srpts %i last byte %i\n",
+ vts_ptt_srpt->nr_of_srpts,
+ vts_ptt_srpt->last_byte);
+ for(i=0;i<vts_ptt_srpt->nr_of_srpts;i++) {
+ printf("\nVTS_PTT number %d has a offset %d relative to VTS_PTT_SRPT\n",
+ i + 1, vts_ptt_srpt->ttu_offset[i]);
+ for(j=0;j<vts_ptt_srpt->title[i].nr_of_ptts;j++) {
+ printf("VTS_PTT_SRPT - Title %3i part %3i: PGC: %3i PG: %3i\n",
+ i + 1, j + 1,
+ vts_ptt_srpt->title[i].ptt[j].pgcn,
+ vts_ptt_srpt->title[i].ptt[j].pgn );
+ }
+ }
+}
+
+
+void ifoPrint_PTL_MAIT(ptl_mait_t *ptl_mait) {
+ int i, level, vts;
+
+ printf("Number of Countries: %i\n", ptl_mait->nr_of_countries);
+ printf("Number of VTSs: %i\n", ptl_mait->nr_of_vtss);
+ printf("Last byte: %i\n", ptl_mait->last_byte);
+
+ for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+
+ printf("Start byte: %i\n", ptl_mait->countries[i].pf_ptl_mai_start_byte);
+ printf("Parental Masks for country: %c%c\n",
+ ptl_mait->countries[i].country_code >> 8,
+ ptl_mait->countries[i].country_code & 0xff);
+
+ for(vts = 0; vts <= ptl_mait->nr_of_vtss; vts++) {
+ if( vts == 0 ) {
+ printf("VMG ");
+ } else {
+ printf("VTS %2d ", vts);
+ }
+ for(level = 0; level < 8; level++) {
+ printf("%d: %04x ", level,
+ ptl_mait->countries[i].pf_ptl_mai[vts][level] );
+ }
+ printf("\n");
+ }
+ }
+}
+
+void ifoPrint_VTS_TMAPT(vts_tmapt_t *vts_tmapt) {
+ unsigned int timeunit;
+ int i, j;
+
+ printf("Number of VTS_TMAPS: %i\n", vts_tmapt->nr_of_tmaps);
+ printf("Last byte: %i\n", vts_tmapt->last_byte);
+
+ for(i = 0; i < vts_tmapt->nr_of_tmaps; i++) {
+ printf("TMAP %i\n", i + 1);
+ printf(" offset %d relative to VTS_TMAPTI\n", vts_tmapt->tmap_offset[i]);
+ printf(" Time unit (seconds): %i\n", vts_tmapt->tmap[i].tmu);
+ printf(" Number of entries: %i\n", vts_tmapt->tmap[i].nr_of_entries);
+ timeunit = vts_tmapt->tmap[i].tmu;
+ for(j = 0; j < vts_tmapt->tmap[i].nr_of_entries; j++) {
+ unsigned int ac_time = timeunit * (j + 1);
+ printf("Time: %2i:%02i:%02i VOBU Sector: 0x%08x %s\n",
+ ac_time / (60 * 60), (ac_time / 60) % 60, ac_time % 60,
+ vts_tmapt->tmap[i].map_ent[j] & 0x7fffffff,
+ (vts_tmapt->tmap[i].map_ent[j] >> 31) ? "discontinuity" : "");
+ }
+ }
+}
+
+void ifoPrint_C_ADT(c_adt_t *c_adt) {
+ int i, entries;
+
+ printf("Number of VOBs in this VOBS: %i\n", c_adt->nr_of_vobs);
+ //entries = c_adt->nr_of_vobs;
+ entries = (c_adt->last_byte + 1 - C_ADT_SIZE)/sizeof(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 (%c)\n",
+ pgci_ut->lu[i].lang_code >> 8,
+ pgci_ut->lu[i].lang_code & 0xff,
+ pgci_ut->lu[i].lang_extension ? pgci_ut->lu[i].lang_extension :' ');
+ printf("Menu Existence: %02x\n", pgci_ut->lu[i].exists);
+ ifoPrint_PGCIT(pgci_ut->lu[i].pgcit);
+ }
+}
+
+
+static void ifoPrint_VTS_ATTRIBUTES(vts_attributes_t *vts_attributes) {
+ int i;
+
+ printf("VTS_CAT Application type: %08x\n", vts_attributes->vts_cat);
+
+ printf("Video attributes of VTSM_VOBS: ");
+ ifoPrint_video_attributes(&vts_attributes->vtsm_vobs_attr);
+ printf("\n");
+ printf("Number of Audio streams: %i\n",
+ vts_attributes->nr_of_vtsm_audio_streams);
+ if(vts_attributes->nr_of_vtsm_audio_streams > 0) {
+ printf("\tstream %i attributes: ", 1);
+ ifoPrint_audio_attributes(&vts_attributes->vtsm_audio_attr);
+ printf("\n");
+ }
+ printf("Number of Subpicture streams: %i\n",
+ vts_attributes->nr_of_vtsm_subp_streams);
+ if(vts_attributes->nr_of_vtsm_subp_streams > 0) {
+ printf("\tstream %2i attributes: ", 1);
+ ifoPrint_subp_attributes(&vts_attributes->vtsm_subp_attr);
+ printf("\n");
+ }
+
+ printf("Video attributes of VTSTT_VOBS: ");
+ ifoPrint_video_attributes(&vts_attributes->vtstt_vobs_video_attr);
+ printf("\n");
+ printf("Number of Audio streams: %i\n",
+ vts_attributes->nr_of_vtstt_audio_streams);
+ for(i = 0; i < vts_attributes->nr_of_vtstt_audio_streams; i++) {
+ printf("\tstream %i attributes: ", i);
+ ifoPrint_audio_attributes(&vts_attributes->vtstt_audio_attr[i]);
+ printf("\n");
+ }
+
+ printf("Number of Subpicture streams: %i\n",
+ vts_attributes->nr_of_vtstt_subp_streams);
+ for(i = 0; i < vts_attributes->nr_of_vtstt_subp_streams; i++) {
+ printf("\tstream %2i attributes: ", i);
+ ifoPrint_subp_attributes(&vts_attributes->vtstt_subp_attr[i]);
+ printf("\n");
+ }
+}
+
+
+void ifoPrint_VTS_ATRT(vts_atrt_t *vts_atrt) {
+ int i;
+
+ printf("Number of Video Title Sets: %3i\n", vts_atrt->nr_of_vtss);
+ for(i = 0; i < vts_atrt->nr_of_vtss; i++) {
+ printf("\nVideo Title Set %i\n", i + 1);
+ printf(" offset %d relative to VMG_VTS_ATRT\n",
+ vts_atrt->vts_atrt_offsets[i]);
+ ifoPrint_VTS_ATTRIBUTES(&vts_atrt->vts[i]);
+ }
+}
+
+
+void ifoPrint(dvd_reader_t *dvd, int title) {
+ ifo_handle_t *ifohandle;
+
+ ifohandle = ifoOpen(dvd, title);
+ if(!ifohandle) {
+ 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("\nTime Search table\n");
+ printf( "-----------------\n");
+ if(ifohandle->vts_tmapt) {
+ ifoPrint_VTS_TMAPT(ifohandle->vts_tmapt);
+ } else {
+ printf("No Time Search table present\n");
+ }
+
+ printf("\nMenu Cell Adress table\n");
+ printf( "-----------------\n");
+ if(ifohandle->menu_c_adt) {
+ ifoPrint_C_ADT(ifohandle->menu_c_adt);
+ } else {
+ printf("No Cell Adress table present\n");
+ }
+
+ printf("\nVideo Title Set Menu VOBU address map\n");
+ printf( "-----------------\n");
+ if(ifohandle->menu_vobu_admap) {
+ ifoPrint_VOBU_ADMAP(ifohandle->menu_vobu_admap);
+ } else {
+ printf("No Menu VOBU address map present\n");
+ }
+
+ printf("\nCell Adress table\n");
+ printf( "-----------------\n");
+ ifoPrint_C_ADT(ifohandle->vts_c_adt);
+
+ printf("\nVideo Title Set VOBU address map\n");
+ printf( "-----------------\n");
+ ifoPrint_VOBU_ADMAP(ifohandle->vts_vobu_admap);
+ }
+
+ ifoClose(ifohandle);
+}
+
diff --git a/dvdread/ifo_print.h b/dvdread/ifo_print.h
new file mode 100644
index 0000000000..c6e24f6a74
--- /dev/null
+++ b/dvdread/ifo_print.h
@@ -0,0 +1,63 @@
+#ifndef IFO_PRINT_H_INCLUDED
+#define IFO_PRINT_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001 Björn Englund <d4bjorn@dtek.chalmers.se>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
+ * detailed changelog at http://svn.mplayerhq.hu/mplayer/trunk/
+ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ifo_types.h"
+#include "dvd_reader.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * This file provides example functions for printing information about the IFO
+ * file to stdout.
+ */
+
+/**
+ * Print the complete parsing information for the given file.
+ */
+
+/* ifoPrint(dvd, title); */
+void ifoPrint(dvd_reader_t *, int);
+
+void ifoPrint_VMGI_MAT(vmgi_mat_t *);
+void ifoPrint_VTSI_MAT(vtsi_mat_t *);
+
+void ifoPrint_PTL_MAIT(ptl_mait_t *);
+void ifoPrint_VTS_ATRT(vts_atrt_t *);
+void ifoPrint_TT_SRPT(tt_srpt_t *);
+void ifoPrint_VTS_PTT_SRPT(vts_ptt_srpt_t *);
+void ifoPrint_PGC(pgc_t *);
+void ifoPrint_PGCIT(pgcit_t *);
+void ifoPrint_PGCI_UT(pgci_ut_t *);
+void ifoPrint_VTS_TMAPT(vts_tmapt_t *);
+void ifoPrint_C_ADT(c_adt_t *);
+void ifoPrint_VOBU_ADMAP(vobu_admap_t *);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* IFO_PRINT_H_INCLUDED */
diff --git a/dvdread/ifo_read.c b/dvdread/ifo_read.c
new file mode 100644
index 0000000000..8ad7baeea1
--- /dev/null
+++ b/dvdread/ifo_read.c
@@ -0,0 +1,2022 @@
+/*
+ * Copyright (C) 2000, 2001, 2002, 2003
+ * Björn Englund <d4bjorn@dtek.chalmers.se>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
+ * detailed changelog at http://svn.mplayerhq.hu/mplayer/trunk/
+ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include "bswap.h"
+#include "ifo_types.h"
+#include "ifo_read.h"
+#include "dvd_reader.h"
+#include "dvdread_internal.h"
+
+#ifndef DVD_BLOCK_LEN
+#define DVD_BLOCK_LEN 2048
+#endif
+
+#ifndef NDEBUG
+#define CHECK_ZERO0(arg) \
+ if(arg != 0) { \
+ fprintf(stderr, "*** Zero check failed in %s:%i\n for %s = 0x%x\n", \
+ __FILE__, __LINE__, # arg, arg); \
+ }
+#define CHECK_ZERO(arg) \
+ if(memcmp(my_friendly_zeros, &arg, sizeof(arg))) { \
+ unsigned int i_CZ; \
+ fprintf(stderr, "*** Zero check failed in %s:%i\n for %s = 0x", \
+ __FILE__, __LINE__, # arg ); \
+ for(i_CZ = 0; i_CZ < sizeof(arg); i_CZ++) \
+ fprintf(stderr, "%02x", *((uint8_t *)&arg + i_CZ)); \
+ fprintf(stderr, "\n"); \
+ }
+static const uint8_t my_friendly_zeros[2048];
+#else
+#define CHECK_ZERO0(arg) (void)(arg)
+#define CHECK_ZERO(arg) (void)(arg)
+#endif
+
+
+/* Prototypes for internal functions */
+static int ifoRead_VMG(ifo_handle_t *ifofile);
+static int ifoRead_VTS(ifo_handle_t *ifofile);
+static int ifoRead_PGC(ifo_handle_t *ifofile, pgc_t *pgc, unsigned int offset);
+static int ifoRead_PGC_COMMAND_TBL(ifo_handle_t *ifofile,
+ pgc_command_tbl_t *cmd_tbl,
+ unsigned int offset);
+static int ifoRead_PGC_PROGRAM_MAP(ifo_handle_t *ifofile,
+ pgc_program_map_t *program_map,
+ unsigned int nr, unsigned int offset);
+static int ifoRead_CELL_PLAYBACK_TBL(ifo_handle_t *ifofile,
+ cell_playback_t *cell_playback,
+ unsigned int nr, unsigned int offset);
+static int ifoRead_CELL_POSITION_TBL(ifo_handle_t *ifofile,
+ cell_position_t *cell_position,
+ unsigned int nr, unsigned int offset);
+static int ifoRead_VTS_ATTRIBUTES(ifo_handle_t *ifofile,
+ vts_attributes_t *vts_attributes,
+ unsigned int offset);
+static int ifoRead_C_ADT_internal(ifo_handle_t *ifofile, c_adt_t *c_adt,
+ unsigned int sector);
+static int ifoRead_VOBU_ADMAP_internal(ifo_handle_t *ifofile,
+ vobu_admap_t *vobu_admap,
+ unsigned int sector);
+static int ifoRead_PGCIT_internal(ifo_handle_t *ifofile, pgcit_t *pgcit,
+ unsigned int offset);
+
+static void ifoFree_PGC(pgc_t *pgc);
+static void ifoFree_PGC_COMMAND_TBL(pgc_command_tbl_t *cmd_tbl);
+static void ifoFree_PGCIT_internal(pgcit_t *pgcit);
+
+
+static inline int DVDFileSeek_( dvd_file_t *dvd_file, uint32_t offset ) {
+ return (DVDFileSeek(dvd_file, (int)offset) == (int)offset);
+}
+
+
+ifo_handle_t *ifoOpen(dvd_reader_t *dvd, int title) {
+ ifo_handle_t *ifofile;
+
+ ifofile = malloc(sizeof(ifo_handle_t));
+ if(!ifofile)
+ return 0;
+
+ memset(ifofile, 0, sizeof(ifo_handle_t));
+
+ ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_FILE);
+ if(!ifofile->file) /* Should really catch any error and try to fallback */
+ ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_BACKUP_FILE);
+ if(!ifofile->file) {
+ if(title) {
+ fprintf(stderr, "libdvdread: Can't open file VTS_%02d_0.IFO.\n", title);
+ } 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_VTS_TMAPT(ifofile);
+ ifoRead_C_ADT(ifofile);
+ ifoRead_VOBU_ADMAP(ifofile);
+
+ if(!ifoRead_TITLE_C_ADT(ifofile) || !ifoRead_TITLE_VOBU_ADMAP(ifofile)) {
+ fprintf(stderr, "libdvdread: Invalid title IFO (VTS_%02d_0.IFO).\n",
+ title);
+ ifoClose(ifofile);
+ return 0;
+ }
+
+ return ifofile;
+ }
+
+ if(title) {
+ fprintf(stderr, "libdvdread: Invalid IFO for title %d (VTS_%02d_0.IFO).\n",
+ title, title);
+ } else {
+ fprintf(stderr, "libdvdread: Invalid IFO for VMGM (VIDEO_TS.IFO).\n");
+ }
+ ifoClose(ifofile);
+ return 0;
+}
+
+
+ifo_handle_t *ifoOpenVMGI(dvd_reader_t *dvd) {
+ ifo_handle_t *ifofile;
+
+ ifofile = 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) /* Should really catch any error and try to fallback */
+ ifofile->file = DVDOpenFile(dvd, 0, DVD_READ_INFO_BACKUP_FILE);
+ if(!ifofile->file) {
+ fprintf(stderr, "libdvdread: Can't open file VIDEO_TS.IFO.\n");
+ free(ifofile);
+ 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 = malloc(sizeof(ifo_handle_t));
+ if(!ifofile)
+ return 0;
+
+ memset(ifofile, 0, sizeof(ifo_handle_t));
+
+ if(title <= 0 || title > 99) {
+ fprintf(stderr, "libdvdread: ifoOpenVTSI invalid title (%d).\n", title);
+ free(ifofile);
+ return 0;
+ }
+
+ ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_FILE);
+ if(!ifofile->file) /* Should really catch any error and try to fallback */
+ ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_BACKUP_FILE);
+ if(!ifofile->file) {
+ fprintf(stderr, "libdvdread: Can't open file VTS_%02d_0.IFO.\n", title);
+ free(ifofile);
+ 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 = malloc(sizeof(vmgi_mat_t));
+ if(!vmgi_mat)
+ return 0;
+
+ ifofile->vmgi_mat = vmgi_mat;
+
+ if(!DVDFileSeek_(ifofile->file, 0)) {
+ free(ifofile->vmgi_mat);
+ ifofile->vmgi_mat = 0;
+ return 0;
+ }
+
+ if(!DVDReadBytes(ifofile->file, vmgi_mat, sizeof(vmgi_mat_t))) {
+ free(ifofile->vmgi_mat);
+ ifofile->vmgi_mat = 0;
+ return 0;
+ }
+
+ if(strncmp("DVDVIDEO-VMG", vmgi_mat->vmg_identifier, 12) != 0) {
+ free(ifofile->vmgi_mat);
+ ifofile->vmgi_mat = 0;
+ return 0;
+ }
+
+ B2N_32(vmgi_mat->vmg_last_sector);
+ B2N_32(vmgi_mat->vmgi_last_sector);
+ B2N_32(vmgi_mat->vmg_category);
+ B2N_16(vmgi_mat->vmg_nr_of_volumes);
+ B2N_16(vmgi_mat->vmg_this_volume_nr);
+ B2N_16(vmgi_mat->vmg_nr_of_title_sets);
+ B2N_64(vmgi_mat->vmg_pos_code);
+ B2N_32(vmgi_mat->vmgi_last_byte);
+ B2N_32(vmgi_mat->first_play_pgc);
+ B2N_32(vmgi_mat->vmgm_vobs);
+ B2N_32(vmgi_mat->tt_srpt);
+ B2N_32(vmgi_mat->vmgm_pgci_ut);
+ B2N_32(vmgi_mat->ptl_mait);
+ B2N_32(vmgi_mat->vts_atrt);
+ B2N_32(vmgi_mat->txtdt_mgi);
+ B2N_32(vmgi_mat->vmgm_c_adt);
+ B2N_32(vmgi_mat->vmgm_vobu_admap);
+ B2N_16(vmgi_mat->vmgm_audio_attr.lang_code);
+ B2N_16(vmgi_mat->vmgm_subp_attr.lang_code);
+
+
+ CHECK_ZERO(vmgi_mat->zero_1);
+ CHECK_ZERO(vmgi_mat->zero_2);
+ CHECK_ZERO(vmgi_mat->zero_3);
+ CHECK_ZERO(vmgi_mat->zero_4);
+ CHECK_ZERO(vmgi_mat->zero_5);
+ CHECK_ZERO(vmgi_mat->zero_6);
+ CHECK_ZERO(vmgi_mat->zero_7);
+ CHECK_ZERO(vmgi_mat->zero_8);
+ CHECK_ZERO(vmgi_mat->zero_9);
+ CHECK_ZERO(vmgi_mat->zero_10);
+ CHECK_VALUE(vmgi_mat->vmg_last_sector != 0);
+ CHECK_VALUE(vmgi_mat->vmgi_last_sector != 0);
+ CHECK_VALUE(vmgi_mat->vmgi_last_sector * 2 <= vmgi_mat->vmg_last_sector);
+ CHECK_VALUE(vmgi_mat->vmgi_last_sector * 2 <= vmgi_mat->vmg_last_sector);
+ CHECK_VALUE(vmgi_mat->vmg_nr_of_volumes != 0);
+ CHECK_VALUE(vmgi_mat->vmg_this_volume_nr != 0);
+ CHECK_VALUE(vmgi_mat->vmg_this_volume_nr <= vmgi_mat->vmg_nr_of_volumes);
+ CHECK_VALUE(vmgi_mat->disc_side == 1 || vmgi_mat->disc_side == 2);
+ CHECK_VALUE(vmgi_mat->vmg_nr_of_title_sets != 0);
+ CHECK_VALUE(vmgi_mat->vmgi_last_byte >= 341);
+ CHECK_VALUE(vmgi_mat->vmgi_last_byte / DVD_BLOCK_LEN <=
+ vmgi_mat->vmgi_last_sector);
+ /* It seems that first_play_pgc is optional. */
+ CHECK_VALUE(vmgi_mat->first_play_pgc < vmgi_mat->vmgi_last_byte);
+ CHECK_VALUE(vmgi_mat->vmgm_vobs == 0 ||
+ (vmgi_mat->vmgm_vobs > vmgi_mat->vmgi_last_sector &&
+ vmgi_mat->vmgm_vobs < vmgi_mat->vmg_last_sector));
+ CHECK_VALUE(vmgi_mat->tt_srpt <= vmgi_mat->vmgi_last_sector);
+ CHECK_VALUE(vmgi_mat->vmgm_pgci_ut <= vmgi_mat->vmgi_last_sector);
+ CHECK_VALUE(vmgi_mat->ptl_mait <= vmgi_mat->vmgi_last_sector);
+ CHECK_VALUE(vmgi_mat->vts_atrt <= vmgi_mat->vmgi_last_sector);
+ CHECK_VALUE(vmgi_mat->txtdt_mgi <= vmgi_mat->vmgi_last_sector);
+ CHECK_VALUE(vmgi_mat->vmgm_c_adt <= vmgi_mat->vmgi_last_sector);
+ CHECK_VALUE(vmgi_mat->vmgm_vobu_admap <= vmgi_mat->vmgi_last_sector);
+
+ CHECK_VALUE(vmgi_mat->nr_of_vmgm_audio_streams <= 1);
+ CHECK_VALUE(vmgi_mat->nr_of_vmgm_subp_streams <= 1);
+
+ return 1;
+}
+
+
+static int ifoRead_VTS(ifo_handle_t *ifofile) {
+ vtsi_mat_t *vtsi_mat;
+ int i;
+
+ vtsi_mat = malloc(sizeof(vtsi_mat_t));
+ if(!vtsi_mat)
+ return 0;
+
+ ifofile->vtsi_mat = vtsi_mat;
+
+ if(!DVDFileSeek_(ifofile->file, 0)) {
+ free(ifofile->vtsi_mat);
+ ifofile->vtsi_mat = 0;
+ return 0;
+ }
+
+ if(!(DVDReadBytes(ifofile->file, vtsi_mat, sizeof(vtsi_mat_t)))) {
+ free(ifofile->vtsi_mat);
+ ifofile->vtsi_mat = 0;
+ return 0;
+ }
+
+ if(strncmp("DVDVIDEO-VTS", vtsi_mat->vts_identifier, 12) != 0) {
+ free(ifofile->vtsi_mat);
+ ifofile->vtsi_mat = 0;
+ return 0;
+ }
+
+ B2N_32(vtsi_mat->vts_last_sector);
+ B2N_32(vtsi_mat->vtsi_last_sector);
+ B2N_32(vtsi_mat->vts_category);
+ B2N_32(vtsi_mat->vtsi_last_byte);
+ B2N_32(vtsi_mat->vtsm_vobs);
+ B2N_32(vtsi_mat->vtstt_vobs);
+ B2N_32(vtsi_mat->vts_ptt_srpt);
+ B2N_32(vtsi_mat->vts_pgcit);
+ B2N_32(vtsi_mat->vtsm_pgci_ut);
+ B2N_32(vtsi_mat->vts_tmapt);
+ B2N_32(vtsi_mat->vtsm_c_adt);
+ B2N_32(vtsi_mat->vtsm_vobu_admap);
+ B2N_32(vtsi_mat->vts_c_adt);
+ B2N_32(vtsi_mat->vts_vobu_admap);
+ B2N_16(vtsi_mat->vtsm_audio_attr.lang_code);
+ B2N_16(vtsi_mat->vtsm_subp_attr.lang_code);
+ for(i = 0; i < 8; i++)
+ B2N_16(vtsi_mat->vts_audio_attr[i].lang_code);
+ for(i = 0; i < 32; i++)
+ B2N_16(vtsi_mat->vts_subp_attr[i].lang_code);
+
+
+ CHECK_ZERO(vtsi_mat->zero_1);
+ CHECK_ZERO(vtsi_mat->zero_2);
+ CHECK_ZERO(vtsi_mat->zero_3);
+ CHECK_ZERO(vtsi_mat->zero_4);
+ CHECK_ZERO(vtsi_mat->zero_5);
+ CHECK_ZERO(vtsi_mat->zero_6);
+ CHECK_ZERO(vtsi_mat->zero_7);
+ CHECK_ZERO(vtsi_mat->zero_8);
+ CHECK_ZERO(vtsi_mat->zero_9);
+ CHECK_ZERO(vtsi_mat->zero_10);
+ CHECK_ZERO(vtsi_mat->zero_11);
+ CHECK_ZERO(vtsi_mat->zero_12);
+ CHECK_ZERO(vtsi_mat->zero_13);
+ CHECK_ZERO(vtsi_mat->zero_14);
+ CHECK_ZERO(vtsi_mat->zero_15);
+ CHECK_ZERO(vtsi_mat->zero_16);
+ CHECK_ZERO(vtsi_mat->zero_17);
+ CHECK_ZERO(vtsi_mat->zero_18);
+ CHECK_ZERO(vtsi_mat->zero_19);
+ CHECK_ZERO(vtsi_mat->zero_20);
+ CHECK_ZERO(vtsi_mat->zero_21);
+ CHECK_VALUE(vtsi_mat->vtsi_last_sector*2 <= vtsi_mat->vts_last_sector);
+ CHECK_VALUE(vtsi_mat->vtsi_last_byte/DVD_BLOCK_LEN <= vtsi_mat->vtsi_last_sector);
+ CHECK_VALUE(vtsi_mat->vtsm_vobs == 0 ||
+ (vtsi_mat->vtsm_vobs > vtsi_mat->vtsi_last_sector &&
+ vtsi_mat->vtsm_vobs < vtsi_mat->vts_last_sector));
+ CHECK_VALUE(vtsi_mat->vtstt_vobs == 0 ||
+ (vtsi_mat->vtstt_vobs > vtsi_mat->vtsi_last_sector &&
+ vtsi_mat->vtstt_vobs < vtsi_mat->vts_last_sector));
+ CHECK_VALUE(vtsi_mat->vts_ptt_srpt <= vtsi_mat->vtsi_last_sector);
+ CHECK_VALUE(vtsi_mat->vts_pgcit <= vtsi_mat->vtsi_last_sector);
+ CHECK_VALUE(vtsi_mat->vtsm_pgci_ut <= vtsi_mat->vtsi_last_sector);
+ CHECK_VALUE(vtsi_mat->vts_tmapt <= vtsi_mat->vtsi_last_sector);
+ CHECK_VALUE(vtsi_mat->vtsm_c_adt <= vtsi_mat->vtsi_last_sector);
+ CHECK_VALUE(vtsi_mat->vtsm_vobu_admap <= vtsi_mat->vtsi_last_sector);
+ CHECK_VALUE(vtsi_mat->vts_c_adt <= vtsi_mat->vtsi_last_sector);
+ CHECK_VALUE(vtsi_mat->vts_vobu_admap <= vtsi_mat->vtsi_last_sector);
+
+ CHECK_VALUE(vtsi_mat->nr_of_vtsm_audio_streams <= 1);
+ CHECK_VALUE(vtsi_mat->nr_of_vtsm_subp_streams <= 1);
+
+ CHECK_VALUE(vtsi_mat->nr_of_vts_audio_streams <= 8);
+ for(i = vtsi_mat->nr_of_vts_audio_streams; i < 8; i++)
+ CHECK_ZERO(vtsi_mat->vts_audio_attr[i]);
+
+ CHECK_VALUE(vtsi_mat->nr_of_vts_subp_streams <= 32);
+ for(i = vtsi_mat->nr_of_vts_subp_streams; i < 32; i++)
+ CHECK_ZERO(vtsi_mat->vts_subp_attr[i]);
+
+ for(i = 0; i < 8; i++) {
+ CHECK_ZERO0(vtsi_mat->vts_mu_audio_attr[i].zero1);
+ CHECK_ZERO0(vtsi_mat->vts_mu_audio_attr[i].zero2);
+ CHECK_ZERO0(vtsi_mat->vts_mu_audio_attr[i].zero3);
+ CHECK_ZERO0(vtsi_mat->vts_mu_audio_attr[i].zero4);
+ CHECK_ZERO0(vtsi_mat->vts_mu_audio_attr[i].zero5);
+ CHECK_ZERO(vtsi_mat->vts_mu_audio_attr[i].zero6);
+ }
+
+ return 1;
+}
+
+
+static int ifoRead_PGC_COMMAND_TBL(ifo_handle_t *ifofile,
+ pgc_command_tbl_t *cmd_tbl,
+ unsigned int offset) {
+
+ 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);
+
+ CHECK_VALUE(cmd_tbl->nr_of_pre + cmd_tbl->nr_of_post + cmd_tbl->nr_of_cell<= 255);
+
+ if(cmd_tbl->nr_of_pre != 0) {
+ unsigned int pre_cmds_size = cmd_tbl->nr_of_pre * COMMAND_DATA_SIZE;
+ cmd_tbl->pre_cmds = malloc(pre_cmds_size);
+ if(!cmd_tbl->pre_cmds)
+ return 0;
+
+ if(!(DVDReadBytes(ifofile->file, cmd_tbl->pre_cmds, pre_cmds_size))) {
+ free(cmd_tbl->pre_cmds);
+ return 0;
+ }
+ }
+
+ if(cmd_tbl->nr_of_post != 0) {
+ unsigned int post_cmds_size = cmd_tbl->nr_of_post * COMMAND_DATA_SIZE;
+ cmd_tbl->post_cmds = malloc(post_cmds_size);
+ if(!cmd_tbl->post_cmds) {
+ if(cmd_tbl->pre_cmds)
+ free(cmd_tbl->pre_cmds);
+ return 0;
+ }
+ if(!(DVDReadBytes(ifofile->file, cmd_tbl->post_cmds, post_cmds_size))) {
+ if(cmd_tbl->pre_cmds)
+ free(cmd_tbl->pre_cmds);
+ free(cmd_tbl->post_cmds);
+ return 0;
+ }
+ }
+
+ if(cmd_tbl->nr_of_cell != 0) {
+ unsigned int cell_cmds_size = cmd_tbl->nr_of_cell * COMMAND_DATA_SIZE;
+ cmd_tbl->cell_cmds = malloc(cell_cmds_size);
+ if(!cmd_tbl->cell_cmds) {
+ if(cmd_tbl->pre_cmds)
+ free(cmd_tbl->pre_cmds);
+ if(cmd_tbl->post_cmds)
+ free(cmd_tbl->post_cmds);
+ return 0;
+ }
+ if(!(DVDReadBytes(ifofile->file, cmd_tbl->cell_cmds, cell_cmds_size))) {
+ if(cmd_tbl->pre_cmds)
+ free(cmd_tbl->pre_cmds);
+ if(cmd_tbl->post_cmds)
+ free(cmd_tbl->post_cmds);
+ free(cmd_tbl->cell_cmds);
+ return 0;
+ }
+ }
+
+ /*
+ * Make a run over all the commands and see that we can interpret them all?
+ */
+ return 1;
+}
+
+
+static void ifoFree_PGC_COMMAND_TBL(pgc_command_tbl_t *cmd_tbl) {
+ if(cmd_tbl) {
+ if(cmd_tbl->nr_of_pre && cmd_tbl->pre_cmds)
+ free(cmd_tbl->pre_cmds);
+ if(cmd_tbl->nr_of_post && cmd_tbl->post_cmds)
+ free(cmd_tbl->post_cmds);
+ if(cmd_tbl->nr_of_cell && cmd_tbl->cell_cmds)
+ free(cmd_tbl->cell_cmds);
+ free(cmd_tbl);
+ }
+}
+
+static int ifoRead_PGC_PROGRAM_MAP(ifo_handle_t *ifofile,
+ pgc_program_map_t *program_map,
+ unsigned int nr, unsigned int offset) {
+ unsigned int size = nr * sizeof(pgc_program_map_t);
+
+ if(!DVDFileSeek_(ifofile->file, offset))
+ return 0;
+
+ if(!(DVDReadBytes(ifofile->file, program_map, size)))
+ return 0;
+
+ return 1;
+}
+
+static int ifoRead_CELL_PLAYBACK_TBL(ifo_handle_t *ifofile,
+ cell_playback_t *cell_playback,
+ unsigned int nr, unsigned int offset) {
+ unsigned int i;
+ unsigned int size = nr * sizeof(cell_playback_t);
+
+ if(!DVDFileSeek_(ifofile->file, offset))
+ return 0;
+
+ if(!(DVDReadBytes(ifofile->file, cell_playback, size)))
+ return 0;
+
+ for(i = 0; i < nr; i++) {
+ B2N_32(cell_playback[i].first_sector);
+ B2N_32(cell_playback[i].first_ilvu_end_sector);
+ B2N_32(cell_playback[i].last_vobu_start_sector);
+ B2N_32(cell_playback[i].last_sector);
+
+ /* Changed < to <= because this was false in the movie 'Pi'. */
+ CHECK_VALUE(cell_playback[i].last_vobu_start_sector <=
+ cell_playback[i].last_sector);
+ CHECK_VALUE(cell_playback[i].first_sector <=
+ cell_playback[i].last_vobu_start_sector);
+ }
+
+ return 1;
+}
+
+
+static int ifoRead_CELL_POSITION_TBL(ifo_handle_t *ifofile,
+ cell_position_t *cell_position,
+ unsigned int nr, unsigned int offset) {
+ unsigned int i;
+ unsigned int size = nr * sizeof(cell_position_t);
+
+ if(!DVDFileSeek_(ifofile->file, offset))
+ return 0;
+
+ if(!(DVDReadBytes(ifofile->file, cell_position, size)))
+ return 0;
+
+ for(i = 0; i < nr; i++) {
+ B2N_16(cell_position[i].vob_id_nr);
+ CHECK_ZERO(cell_position[i].zero_1);
+ }
+
+ return 1;
+}
+
+static int ifoRead_PGC(ifo_handle_t *ifofile, pgc_t *pgc, unsigned int offset) {
+ unsigned int i;
+
+ if(!DVDFileSeek_(ifofile->file, offset))
+ return 0;
+
+ if(!(DVDReadBytes(ifofile->file, pgc, PGC_SIZE)))
+ return 0;
+
+ B2N_16(pgc->next_pgc_nr);
+ B2N_16(pgc->prev_pgc_nr);
+ B2N_16(pgc->goup_pgc_nr);
+ B2N_16(pgc->command_tbl_offset);
+ B2N_16(pgc->program_map_offset);
+ B2N_16(pgc->cell_playback_offset);
+ B2N_16(pgc->cell_position_offset);
+
+ for(i = 0; i < 16; i++)
+ B2N_32(pgc->palette[i]);
+
+ CHECK_ZERO(pgc->zero_1);
+ CHECK_VALUE(pgc->nr_of_programs <= pgc->nr_of_cells);
+
+ /* verify time (look at print_time) */
+ for(i = 0; i < 8; i++)
+ if(!pgc->audio_control[i].present)
+ CHECK_ZERO(pgc->audio_control[i]);
+ for(i = 0; i < 32; i++)
+ if(!pgc->subp_control[i].present)
+ CHECK_ZERO(pgc->subp_control[i]);
+
+ /* Check that time is 0:0:0:0 also if nr_of_programs == 0 */
+ if(pgc->nr_of_programs == 0) {
+ CHECK_ZERO(pgc->still_time);
+ CHECK_ZERO(pgc->pg_playback_mode); // ??
+ CHECK_VALUE(pgc->program_map_offset == 0);
+ CHECK_VALUE(pgc->cell_playback_offset == 0);
+ CHECK_VALUE(pgc->cell_position_offset == 0);
+ } else {
+ CHECK_VALUE(pgc->program_map_offset != 0);
+ CHECK_VALUE(pgc->cell_playback_offset != 0);
+ CHECK_VALUE(pgc->cell_position_offset != 0);
+ }
+
+ if(pgc->command_tbl_offset != 0) {
+ pgc->command_tbl = malloc(sizeof(pgc_command_tbl_t));
+ if(!pgc->command_tbl)
+ return 0;
+
+ if(!ifoRead_PGC_COMMAND_TBL(ifofile, pgc->command_tbl,
+ offset + pgc->command_tbl_offset)) {
+ free(pgc->command_tbl);
+ return 0;
+ }
+ } else {
+ pgc->command_tbl = NULL;
+ }
+
+ if(pgc->program_map_offset != 0) {
+ pgc->program_map = malloc(pgc->nr_of_programs * sizeof(pgc_program_map_t));
+ if(!pgc->program_map) {
+ ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+ return 0;
+ }
+ if(!ifoRead_PGC_PROGRAM_MAP(ifofile, pgc->program_map,pgc->nr_of_programs,
+ offset + pgc->program_map_offset)) {
+ ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+ free(pgc->program_map);
+ return 0;
+ }
+ } else {
+ pgc->program_map = NULL;
+ }
+
+ if(pgc->cell_playback_offset != 0) {
+ pgc->cell_playback = malloc(pgc->nr_of_cells * sizeof(cell_playback_t));
+ if(!pgc->cell_playback) {
+ ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+ if(pgc->program_map)
+ free(pgc->program_map);
+ return 0;
+ }
+ if(!ifoRead_CELL_PLAYBACK_TBL(ifofile, pgc->cell_playback,
+ pgc->nr_of_cells,
+ offset + pgc->cell_playback_offset)) {
+ ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+ if(pgc->program_map)
+ free(pgc->program_map);
+ free(pgc->cell_playback);
+ return 0;
+ }
+ } else {
+ pgc->cell_playback = NULL;
+ }
+
+ if(pgc->cell_position_offset != 0) {
+ pgc->cell_position = malloc(pgc->nr_of_cells * sizeof(cell_position_t));
+ if(!pgc->cell_position) {
+ ifoFree_PGC(pgc);
+ return 0;
+ }
+ if(!ifoRead_CELL_POSITION_TBL(ifofile, pgc->cell_position,
+ pgc->nr_of_cells,
+ offset + pgc->cell_position_offset)) {
+ ifoFree_PGC(pgc);
+ return 0;
+ }
+ } else {
+ pgc->cell_position = NULL;
+ }
+
+ return 1;
+}
+
+int ifoRead_FP_PGC(ifo_handle_t *ifofile) {
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vmgi_mat)
+ return 0;
+
+ /* It seems that first_play_pgc is optional after all. */
+ ifofile->first_play_pgc = 0;
+ if(ifofile->vmgi_mat->first_play_pgc == 0)
+ return 1;
+
+ ifofile->first_play_pgc = malloc(sizeof(pgc_t));
+ if(!ifofile->first_play_pgc)
+ return 0;
+
+ if(!ifoRead_PGC(ifofile, ifofile->first_play_pgc,
+ ifofile->vmgi_mat->first_play_pgc)) {
+ free(ifofile->first_play_pgc);
+ ifofile->first_play_pgc = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+static void ifoFree_PGC(pgc_t *pgc) {
+ if(pgc) {
+ ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+ if(pgc->program_map)
+ free(pgc->program_map);
+ if(pgc->cell_playback)
+ free(pgc->cell_playback);
+ if(pgc->cell_position)
+ free(pgc->cell_position);
+ }
+}
+
+void ifoFree_FP_PGC(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ if(ifofile->first_play_pgc) {
+ ifoFree_PGC(ifofile->first_play_pgc);
+ free(ifofile->first_play_pgc);
+ ifofile->first_play_pgc = 0;
+ }
+}
+
+
+int ifoRead_TT_SRPT(ifo_handle_t *ifofile) {
+ tt_srpt_t *tt_srpt;
+ int i, info_length;
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vmgi_mat)
+ return 0;
+
+ if(ifofile->vmgi_mat->tt_srpt == 0) /* mandatory */
+ return 0;
+
+ if(!DVDFileSeek_(ifofile->file, ifofile->vmgi_mat->tt_srpt * DVD_BLOCK_LEN))
+ return 0;
+
+ tt_srpt = malloc(sizeof(tt_srpt_t));
+ if(!tt_srpt)
+ return 0;
+
+ ifofile->tt_srpt = tt_srpt;
+
+ if(!(DVDReadBytes(ifofile->file, tt_srpt, TT_SRPT_SIZE))) {
+ fprintf(stderr, "libdvdread: Unable to read read TT_SRPT.\n");
+ free(tt_srpt);
+ return 0;
+ }
+
+ B2N_16(tt_srpt->nr_of_srpts);
+ B2N_32(tt_srpt->last_byte);
+
+ info_length = tt_srpt->last_byte + 1 - TT_SRPT_SIZE;
+
+ tt_srpt->title = malloc(info_length);
+ if(!tt_srpt->title) {
+ free(tt_srpt);
+ ifofile->tt_srpt = 0;
+ return 0;
+ }
+ if(!(DVDReadBytes(ifofile->file, tt_srpt->title, info_length))) {
+ fprintf(stderr, "libdvdread: Unable to read read TT_SRPT.\n");
+ ifoFree_TT_SRPT(ifofile);
+ return 0;
+ }
+
+ for(i = 0; i < tt_srpt->nr_of_srpts; i++) {
+ B2N_16(tt_srpt->title[i].nr_of_ptts);
+ B2N_16(tt_srpt->title[i].parental_id);
+ B2N_32(tt_srpt->title[i].title_set_sector);
+ }
+
+
+ CHECK_ZERO(tt_srpt->zero_1);
+ CHECK_VALUE(tt_srpt->nr_of_srpts != 0);
+ CHECK_VALUE(tt_srpt->nr_of_srpts < 100); // ??
+ CHECK_VALUE((int)tt_srpt->nr_of_srpts * sizeof(title_info_t) <= info_length);
+
+ for(i = 0; i < tt_srpt->nr_of_srpts; i++) {
+ CHECK_VALUE(tt_srpt->title[i].pb_ty.zero_1 == 0);
+ CHECK_VALUE(tt_srpt->title[i].nr_of_angles != 0);
+ CHECK_VALUE(tt_srpt->title[i].nr_of_angles < 10);
+ //CHECK_VALUE(tt_srpt->title[i].nr_of_ptts != 0);
+ // XXX: this assertion breaks Ghostbusters:
+ CHECK_VALUE(tt_srpt->title[i].nr_of_ptts < 1000); // ??
+ CHECK_VALUE(tt_srpt->title[i].title_set_nr != 0);
+ CHECK_VALUE(tt_srpt->title[i].title_set_nr < 100); // ??
+ CHECK_VALUE(tt_srpt->title[i].vts_ttn != 0);
+ CHECK_VALUE(tt_srpt->title[i].vts_ttn < 100); // ??
+ //CHECK_VALUE(tt_srpt->title[i].title_set_sector != 0);
+ }
+
+ // Make this a function
+#if 0
+ if(memcmp((uint8_t *)tt_srpt->title +
+ tt_srpt->nr_of_srpts * sizeof(title_info_t),
+ my_friendly_zeros,
+ info_length - tt_srpt->nr_of_srpts * sizeof(title_info_t))) {
+ fprintf(stderr, "VMG_PTT_SRPT slack is != 0, ");
+ hexdump((uint8_t *)tt_srpt->title +
+ tt_srpt->nr_of_srpts * sizeof(title_info_t),
+ info_length - tt_srpt->nr_of_srpts * sizeof(title_info_t));
+ }
+#endif
+
+ return 1;
+}
+
+
+void ifoFree_TT_SRPT(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ if(ifofile->tt_srpt) {
+ free(ifofile->tt_srpt->title);
+ free(ifofile->tt_srpt);
+ ifofile->tt_srpt = 0;
+ }
+}
+
+
+int ifoRead_VTS_PTT_SRPT(ifo_handle_t *ifofile) {
+ vts_ptt_srpt_t *vts_ptt_srpt;
+ int info_length, i, j;
+ uint32_t *data;
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vtsi_mat)
+ return 0;
+
+ if(ifofile->vtsi_mat->vts_ptt_srpt == 0) /* mandatory */
+ return 0;
+
+ if(!DVDFileSeek_(ifofile->file,
+ ifofile->vtsi_mat->vts_ptt_srpt * DVD_BLOCK_LEN))
+ return 0;
+
+ vts_ptt_srpt = malloc(sizeof(vts_ptt_srpt_t));
+ if(!vts_ptt_srpt)
+ return 0;
+
+ ifofile->vts_ptt_srpt = vts_ptt_srpt;
+
+ if(!(DVDReadBytes(ifofile->file, vts_ptt_srpt, VTS_PTT_SRPT_SIZE))) {
+ fprintf(stderr, "libdvdread: Unable to read PTT search table.\n");
+ free(vts_ptt_srpt);
+ return 0;
+ }
+
+ B2N_16(vts_ptt_srpt->nr_of_srpts);
+ B2N_32(vts_ptt_srpt->last_byte);
+
+ CHECK_ZERO(vts_ptt_srpt->zero_1);
+ CHECK_VALUE(vts_ptt_srpt->nr_of_srpts != 0);
+ CHECK_VALUE(vts_ptt_srpt->nr_of_srpts < 100); // ??
+
+ info_length = vts_ptt_srpt->last_byte + 1 - VTS_PTT_SRPT_SIZE;
+
+ data = malloc(info_length);
+ if(!data) {
+ free(vts_ptt_srpt);
+ ifofile->vts_ptt_srpt = 0;
+ return 0;
+ }
+ if(!(DVDReadBytes(ifofile->file, data, info_length))) {
+ fprintf(stderr, "libdvdread: Unable to read PTT search table.\n");
+ free(vts_ptt_srpt);
+ free(data);
+ ifofile->vts_ptt_srpt = 0;
+ return 0;
+ }
+
+ for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+ B2N_32(data[i]);
+ /* assert(data[i] + sizeof(ptt_info_t) <= vts_ptt_srpt->last_byte + 1);
+ Magic Knight Rayearth Daybreak is mastered very strange and has
+ Titles with 0 PTTs. They all have a data[i] offsets beyond the end of
+ of the vts_ptt_srpt structure. */
+ CHECK_VALUE(data[i] + sizeof(ptt_info_t) <= vts_ptt_srpt->last_byte + 1 + 4);
+ }
+
+ vts_ptt_srpt->ttu_offset = data;
+
+ vts_ptt_srpt->title = malloc(vts_ptt_srpt->nr_of_srpts * sizeof(ttu_t));
+ if(!vts_ptt_srpt->title) {
+ free(vts_ptt_srpt);
+ free(data);
+ ifofile->vts_ptt_srpt = 0;
+ return 0;
+ }
+ for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+ int n;
+ if(i < vts_ptt_srpt->nr_of_srpts - 1)
+ n = (data[i+1] - data[i]);
+ else
+ n = (vts_ptt_srpt->last_byte + 1 - data[i]);
+ /* assert(n > 0 && (n % 4) == 0);
+ Magic Knight Rayearth Daybreak is mastered very strange and has
+ Titles with 0 PTTs. */
+ if(n < 0) n = 0;
+ CHECK_VALUE(n % 4 == 0);
+
+ vts_ptt_srpt->title[i].nr_of_ptts = n / 4;
+ vts_ptt_srpt->title[i].ptt = malloc(n * sizeof(ptt_info_t));
+ if(!vts_ptt_srpt->title[i].ptt) {
+ for(n = 0; n < i; n++)
+ free(vts_ptt_srpt->title[n].ptt);
+ free(vts_ptt_srpt);
+ free(data);
+ ifofile->vts_ptt_srpt = 0;
+ return 0;
+ }
+ for(j = 0; j < vts_ptt_srpt->title[i].nr_of_ptts; j++) {
+ /* The assert placed here because of Magic Knight Rayearth Daybreak */
+ CHECK_VALUE(data[i] + sizeof(ptt_info_t) <= vts_ptt_srpt->last_byte + 1);
+ vts_ptt_srpt->title[i].ptt[j].pgcn
+ = *(uint16_t*)(((char *)data) + data[i] + 4*j - VTS_PTT_SRPT_SIZE);
+ vts_ptt_srpt->title[i].ptt[j].pgn
+ = *(uint16_t*)(((char *)data) + data[i] + 4*j + 2 - VTS_PTT_SRPT_SIZE);
+ }
+ }
+
+ for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+ for(j = 0; j < vts_ptt_srpt->title[i].nr_of_ptts; j++) {
+ B2N_16(vts_ptt_srpt->title[i].ptt[j].pgcn);
+ B2N_16(vts_ptt_srpt->title[i].ptt[j].pgn);
+ }
+ }
+
+ for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+ CHECK_VALUE(vts_ptt_srpt->title[i].nr_of_ptts < 1000); // ??
+ for(j = 0; j < vts_ptt_srpt->title[i].nr_of_ptts; j++) {
+ CHECK_VALUE(vts_ptt_srpt->title[i].ptt[j].pgcn != 0 );
+ CHECK_VALUE(vts_ptt_srpt->title[i].ptt[j].pgcn < 1000); // ??
+ CHECK_VALUE(vts_ptt_srpt->title[i].ptt[j].pgn != 0);
+ CHECK_VALUE(vts_ptt_srpt->title[i].ptt[j].pgn < 100); // ??
+ }
+ }
+
+ return 1;
+}
+
+
+void ifoFree_VTS_PTT_SRPT(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ if(ifofile->vts_ptt_srpt) {
+ int i;
+ for(i = 0; i < ifofile->vts_ptt_srpt->nr_of_srpts; i++)
+ free(ifofile->vts_ptt_srpt->title[i].ptt);
+ free(ifofile->vts_ptt_srpt->ttu_offset);
+ free(ifofile->vts_ptt_srpt->title);
+ free(ifofile->vts_ptt_srpt);
+ ifofile->vts_ptt_srpt = 0;
+ }
+}
+
+
+int ifoRead_PTL_MAIT(ifo_handle_t *ifofile) {
+ ptl_mait_t *ptl_mait;
+ int info_length;
+ unsigned int i, j;
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vmgi_mat)
+ return 0;
+
+ if(ifofile->vmgi_mat->ptl_mait == 0)
+ return 1;
+
+ if(!DVDFileSeek_(ifofile->file, ifofile->vmgi_mat->ptl_mait * DVD_BLOCK_LEN))
+ return 0;
+
+ ptl_mait = malloc(sizeof(ptl_mait_t));
+ if(!ptl_mait)
+ return 0;
+
+ ifofile->ptl_mait = ptl_mait;
+
+ if(!(DVDReadBytes(ifofile->file, ptl_mait, PTL_MAIT_SIZE))) {
+ free(ptl_mait);
+ ifofile->ptl_mait = 0;
+ return 0;
+ }
+
+ B2N_16(ptl_mait->nr_of_countries);
+ B2N_16(ptl_mait->nr_of_vtss);
+ B2N_32(ptl_mait->last_byte);
+
+ CHECK_VALUE(ptl_mait->nr_of_countries != 0);
+ CHECK_VALUE(ptl_mait->nr_of_countries < 100); // ??
+ CHECK_VALUE(ptl_mait->nr_of_vtss != 0);
+ CHECK_VALUE(ptl_mait->nr_of_vtss < 100); // ??
+ CHECK_VALUE(ptl_mait->nr_of_countries * PTL_MAIT_COUNTRY_SIZE
+ <= ptl_mait->last_byte + 1 - PTL_MAIT_SIZE);
+
+ info_length = ptl_mait->nr_of_countries * sizeof(ptl_mait_country_t);
+ ptl_mait->countries = malloc(info_length);
+ if(!ptl_mait->countries) {
+ free(ptl_mait);
+ ifofile->ptl_mait = 0;
+ return 0;
+ }
+
+ for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+ if(!(DVDReadBytes(ifofile->file, &ptl_mait->countries[i], PTL_MAIT_COUNTRY_SIZE))) {
+ fprintf(stderr, "libdvdread: Unable to read PTL_MAIT.\n");
+ free(ptl_mait->countries);
+ free(ptl_mait);
+ ifofile->ptl_mait = 0;
+ return 0;
+ }
+ }
+
+ for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+ B2N_16(ptl_mait->countries[i].country_code);
+ B2N_16(ptl_mait->countries[i].pf_ptl_mai_start_byte);
+ }
+
+ for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+ CHECK_ZERO(ptl_mait->countries[i].zero_1);
+ CHECK_ZERO(ptl_mait->countries[i].zero_2);
+ CHECK_VALUE(ptl_mait->countries[i].pf_ptl_mai_start_byte
+ + 8*2 * (ptl_mait->nr_of_vtss + 1) <= ptl_mait->last_byte + 1);
+ }
+
+ for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+ uint16_t *pf_temp;
+
+ if(!DVDFileSeek_(ifofile->file,
+ ifofile->vmgi_mat->ptl_mait * DVD_BLOCK_LEN
+ + ptl_mait->countries[i].pf_ptl_mai_start_byte)) {
+ fprintf(stderr, "libdvdread: Unable to seak PTL_MAIT table.\n");
+ free(ptl_mait->countries);
+ free(ptl_mait);
+ return 0;
+ }
+ info_length = (ptl_mait->nr_of_vtss + 1) * sizeof(pf_level_t);
+ pf_temp = malloc(info_length);
+ if(!pf_temp) {
+ for(j = 0; j < i ; j++) {
+ free(ptl_mait->countries[j].pf_ptl_mai);
+ }
+ free(ptl_mait->countries);
+ free(ptl_mait);
+ return 0;
+ }
+ if(!(DVDReadBytes(ifofile->file, pf_temp, info_length))) {
+ fprintf(stderr, "libdvdread: Unable to read PTL_MAIT table.\n");
+ free(pf_temp);
+ for(j = 0; j < i ; j++) {
+ free(ptl_mait->countries[j].pf_ptl_mai);
+ }
+ free(ptl_mait->countries);
+ free(ptl_mait);
+ return 0;
+ }
+ for (j = 0; j < ((ptl_mait->nr_of_vtss + 1) * 8); j++) {
+ B2N_16(pf_temp[j]);
+ }
+ ptl_mait->countries[i].pf_ptl_mai = malloc(info_length);
+ if(!ptl_mait->countries[i].pf_ptl_mai) {
+ free(pf_temp);
+ for(j = 0; j < i ; j++) {
+ free(ptl_mait->countries[j].pf_ptl_mai);
+ }
+ free(ptl_mait->countries);
+ free(ptl_mait);
+ return 0;
+ }
+ { /* Transpose the array so we can use C indexing. */
+ int level, vts;
+ for(level = 0; level < 8; level++) {
+ for(vts = 0; vts <= ptl_mait->nr_of_vtss; vts++) {
+ ptl_mait->countries[i].pf_ptl_mai[vts][level] =
+ pf_temp[(7-level)*(ptl_mait->nr_of_vtss+1) + vts];
+ }
+ }
+ free(pf_temp);
+ }
+ }
+ return 1;
+}
+
+void ifoFree_PTL_MAIT(ifo_handle_t *ifofile) {
+ unsigned int i;
+
+ if(!ifofile)
+ return;
+
+ if(ifofile->ptl_mait) {
+ for(i = 0; i < ifofile->ptl_mait->nr_of_countries; i++) {
+ free(ifofile->ptl_mait->countries[i].pf_ptl_mai);
+ }
+ free(ifofile->ptl_mait->countries);
+ free(ifofile->ptl_mait);
+ ifofile->ptl_mait = 0;
+ }
+}
+
+int ifoRead_VTS_TMAPT(ifo_handle_t *ifofile) {
+ vts_tmapt_t *vts_tmapt;
+ uint32_t *vts_tmap_srp;
+ unsigned int offset;
+ int info_length;
+ unsigned int i, j;
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vtsi_mat)
+ return 0;
+
+ if(ifofile->vtsi_mat->vts_tmapt == 0) { /* optional(?) */
+ ifofile->vts_tmapt = NULL;
+ fprintf(stderr,"Please send bug report - no VTS_TMAPT ?? \n");
+ return 1;
+ }
+
+ offset = ifofile->vtsi_mat->vts_tmapt * DVD_BLOCK_LEN;
+
+ if(!DVDFileSeek_(ifofile->file, offset))
+ return 0;
+
+ vts_tmapt = malloc(sizeof(vts_tmapt_t));
+ if(!vts_tmapt)
+ return 0;
+
+ ifofile->vts_tmapt = vts_tmapt;
+
+ if(!(DVDReadBytes(ifofile->file, vts_tmapt, VTS_TMAPT_SIZE))) {
+ fprintf(stderr, "libdvdread: Unable to read VTS_TMAPT.\n");
+ free(vts_tmapt);
+ ifofile->vts_tmapt = NULL;
+ return 0;
+ }
+
+ B2N_16(vts_tmapt->nr_of_tmaps);
+ B2N_32(vts_tmapt->last_byte);
+
+ CHECK_ZERO(vts_tmapt->zero_1);
+
+ info_length = vts_tmapt->nr_of_tmaps * 4;
+
+ vts_tmap_srp = malloc(info_length);
+ if(!vts_tmap_srp) {
+ free(vts_tmapt);
+ ifofile->vts_tmapt = NULL;
+ return 0;
+ }
+
+ vts_tmapt->tmap_offset = vts_tmap_srp;
+
+ if(!(DVDReadBytes(ifofile->file, vts_tmap_srp, info_length))) {
+ fprintf(stderr, "libdvdread: Unable to read VTS_TMAPT.\n");
+ free(vts_tmap_srp);
+ free(vts_tmapt);
+ ifofile->vts_tmapt = NULL;
+ return 0;
+ }
+
+ for (i = 0; i < vts_tmapt->nr_of_tmaps; i++) {
+ B2N_32(vts_tmap_srp[i]);
+ }
+
+
+ info_length = vts_tmapt->nr_of_tmaps * sizeof(vts_tmap_t);
+
+ vts_tmapt->tmap = malloc(info_length);
+ if(!vts_tmapt->tmap) {
+ free(vts_tmap_srp);
+ free(vts_tmapt);
+ ifofile->vts_tmapt = NULL;
+ return 0;
+ }
+
+ memset(vts_tmapt->tmap, 0, info_length); /* So ifoFree_VTS_TMAPT works. */
+
+ for(i = 0; i < vts_tmapt->nr_of_tmaps; i++) {
+ if(!DVDFileSeek_(ifofile->file, offset + vts_tmap_srp[i])) {
+ ifoFree_VTS_TMAPT(ifofile);
+ return 0;
+ }
+
+ if(!(DVDReadBytes(ifofile->file, &vts_tmapt->tmap[i], VTS_TMAP_SIZE))) {
+ fprintf(stderr, "libdvdread: Unable to read VTS_TMAP.\n");
+ ifoFree_VTS_TMAPT(ifofile);
+ return 0;
+ }
+
+ B2N_16(vts_tmapt->tmap[i].nr_of_entries);
+ CHECK_ZERO(vts_tmapt->tmap[i].zero_1);
+
+ if(vts_tmapt->tmap[i].nr_of_entries == 0) { /* Early out if zero entries */
+ vts_tmapt->tmap[i].map_ent = NULL;
+ continue;
+ }
+
+ info_length = vts_tmapt->tmap[i].nr_of_entries * sizeof(map_ent_t);
+
+ vts_tmapt->tmap[i].map_ent = malloc(info_length);
+ if(!vts_tmapt->tmap[i].map_ent) {
+ ifoFree_VTS_TMAPT(ifofile);
+ return 0;
+ }
+
+ if(!(DVDReadBytes(ifofile->file, vts_tmapt->tmap[i].map_ent, info_length))) {
+ fprintf(stderr, "libdvdread: Unable to read VTS_TMAP_ENT.\n");
+ ifoFree_VTS_TMAPT(ifofile);
+ return 0;
+ }
+
+ for(j = 0; j < vts_tmapt->tmap[i].nr_of_entries; j++)
+ B2N_32(vts_tmapt->tmap[i].map_ent[j]);
+ }
+
+ return 1;
+}
+
+void ifoFree_VTS_TMAPT(ifo_handle_t *ifofile) {
+ unsigned int i;
+
+ if(!ifofile)
+ return;
+
+ if(ifofile->vts_tmapt) {
+ for(i = 0; i < ifofile->vts_tmapt->nr_of_tmaps; i++)
+ if(ifofile->vts_tmapt->tmap[i].map_ent)
+ free(ifofile->vts_tmapt->tmap[i].map_ent);
+ free(ifofile->vts_tmapt->tmap);
+ free(ifofile->vts_tmapt->tmap_offset);
+ free(ifofile->vts_tmapt);
+ ifofile->vts_tmapt = NULL;
+ }
+}
+
+
+int ifoRead_TITLE_C_ADT(ifo_handle_t *ifofile) {
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vtsi_mat)
+ return 0;
+
+ if(ifofile->vtsi_mat->vts_c_adt == 0) /* mandatory */
+ return 0;
+
+ ifofile->vts_c_adt = malloc(sizeof(c_adt_t));
+ if(!ifofile->vts_c_adt)
+ return 0;
+
+ if(!ifoRead_C_ADT_internal(ifofile, ifofile->vts_c_adt,
+ ifofile->vtsi_mat->vts_c_adt)) {
+ free(ifofile->vts_c_adt);
+ ifofile->vts_c_adt = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+int ifoRead_C_ADT(ifo_handle_t *ifofile) {
+ unsigned int sector;
+
+ if(!ifofile)
+ return 0;
+
+ if(ifofile->vmgi_mat) {
+ if(ifofile->vmgi_mat->vmgm_c_adt == 0)
+ return 1;
+ sector = ifofile->vmgi_mat->vmgm_c_adt;
+ } else if(ifofile->vtsi_mat) {
+ if(ifofile->vtsi_mat->vtsm_c_adt == 0)
+ return 1;
+ sector = ifofile->vtsi_mat->vtsm_c_adt;
+ } else {
+ return 0;
+ }
+
+ ifofile->menu_c_adt = malloc(sizeof(c_adt_t));
+ if(!ifofile->menu_c_adt)
+ return 0;
+
+ if(!ifoRead_C_ADT_internal(ifofile, ifofile->menu_c_adt, sector)) {
+ free(ifofile->menu_c_adt);
+ ifofile->menu_c_adt = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+static int ifoRead_C_ADT_internal(ifo_handle_t *ifofile,
+ c_adt_t *c_adt, unsigned int sector) {
+ int i, info_length;
+
+ if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN))
+ return 0;
+
+ if(!(DVDReadBytes(ifofile->file, c_adt, C_ADT_SIZE)))
+ return 0;
+
+ B2N_16(c_adt->nr_of_vobs);
+ B2N_32(c_adt->last_byte);
+
+ info_length = c_adt->last_byte + 1 - C_ADT_SIZE;
+
+ CHECK_ZERO(c_adt->zero_1);
+ /* assert(c_adt->nr_of_vobs > 0);
+ Magic Knight Rayearth Daybreak is mastered very strange and has
+ Titles with a VOBS that has no cells. */
+ CHECK_VALUE(info_length % sizeof(cell_adr_t) == 0);
+
+ /* assert(info_length / sizeof(cell_adr_t) >= c_adt->nr_of_vobs);
+ Enemy of the State region 2 (de) has Titles where nr_of_vobs field
+ is to high, they high ones are never referenced though. */
+ if(info_length / sizeof(cell_adr_t) < c_adt->nr_of_vobs) {
+ fprintf(stderr, "libdvdread: *C_ADT nr_of_vobs > avaiable info entries\n");
+ c_adt->nr_of_vobs = info_length / sizeof(cell_adr_t);
+ }
+
+ c_adt->cell_adr_table = malloc(info_length);
+ if(!c_adt->cell_adr_table)
+ return 0;
+
+ if(info_length &&
+ !(DVDReadBytes(ifofile->file, c_adt->cell_adr_table, info_length))) {
+ free(c_adt->cell_adr_table);
+ return 0;
+ }
+
+ for(i = 0; i < info_length/sizeof(cell_adr_t); i++) {
+ B2N_16(c_adt->cell_adr_table[i].vob_id);
+ B2N_32(c_adt->cell_adr_table[i].start_sector);
+ B2N_32(c_adt->cell_adr_table[i].last_sector);
+
+ CHECK_ZERO(c_adt->cell_adr_table[i].zero_1);
+ CHECK_VALUE(c_adt->cell_adr_table[i].vob_id > 0);
+ CHECK_VALUE(c_adt->cell_adr_table[i].vob_id <= c_adt->nr_of_vobs);
+ CHECK_VALUE(c_adt->cell_adr_table[i].cell_id > 0);
+ CHECK_VALUE(c_adt->cell_adr_table[i].start_sector <
+ c_adt->cell_adr_table[i].last_sector);
+ }
+
+ return 1;
+}
+
+
+static void ifoFree_C_ADT_internal(c_adt_t *c_adt) {
+ if(c_adt) {
+ free(c_adt->cell_adr_table);
+ free(c_adt);
+ }
+}
+
+void ifoFree_C_ADT(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ ifoFree_C_ADT_internal(ifofile->menu_c_adt);
+ ifofile->menu_c_adt = 0;
+}
+
+void ifoFree_TITLE_C_ADT(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ ifoFree_C_ADT_internal(ifofile->vts_c_adt);
+ ifofile->vts_c_adt = 0;
+}
+
+int ifoRead_TITLE_VOBU_ADMAP(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vtsi_mat)
+ return 0;
+
+ if(ifofile->vtsi_mat->vts_vobu_admap == 0) /* mandatory */
+ return 0;
+
+ ifofile->vts_vobu_admap = malloc(sizeof(vobu_admap_t));
+ if(!ifofile->vts_vobu_admap)
+ return 0;
+
+ if(!ifoRead_VOBU_ADMAP_internal(ifofile, ifofile->vts_vobu_admap,
+ ifofile->vtsi_mat->vts_vobu_admap)) {
+ free(ifofile->vts_vobu_admap);
+ ifofile->vts_vobu_admap = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+int ifoRead_VOBU_ADMAP(ifo_handle_t *ifofile) {
+ unsigned int sector;
+
+ if(!ifofile)
+ return 0;
+
+ if(ifofile->vmgi_mat) {
+ if(ifofile->vmgi_mat->vmgm_vobu_admap == 0)
+ return 1;
+ sector = ifofile->vmgi_mat->vmgm_vobu_admap;
+ } else if(ifofile->vtsi_mat) {
+ if(ifofile->vtsi_mat->vtsm_vobu_admap == 0)
+ return 1;
+ sector = ifofile->vtsi_mat->vtsm_vobu_admap;
+ } else {
+ return 0;
+ }
+
+ ifofile->menu_vobu_admap = malloc(sizeof(vobu_admap_t));
+ if(!ifofile->menu_vobu_admap)
+ return 0;
+
+ if(!ifoRead_VOBU_ADMAP_internal(ifofile, ifofile->menu_vobu_admap, sector)) {
+ free(ifofile->menu_vobu_admap);
+ ifofile->menu_vobu_admap = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+static int ifoRead_VOBU_ADMAP_internal(ifo_handle_t *ifofile,
+ vobu_admap_t *vobu_admap,
+ unsigned int sector) {
+ unsigned int i;
+ int info_length;
+
+ if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN))
+ return 0;
+
+ if(!(DVDReadBytes(ifofile->file, vobu_admap, VOBU_ADMAP_SIZE)))
+ return 0;
+
+ B2N_32(vobu_admap->last_byte);
+
+ info_length = vobu_admap->last_byte + 1 - VOBU_ADMAP_SIZE;
+ /* assert(info_length > 0);
+ Magic Knight Rayearth Daybreak is mastered very strange and has
+ Titles with a VOBS that has no VOBUs. */
+ CHECK_VALUE(info_length % sizeof(uint32_t) == 0);
+
+ vobu_admap->vobu_start_sectors = malloc(info_length);
+ if(!vobu_admap->vobu_start_sectors) {
+ return 0;
+ }
+ if(info_length &&
+ !(DVDReadBytes(ifofile->file,
+ vobu_admap->vobu_start_sectors, info_length))) {
+ free(vobu_admap->vobu_start_sectors);
+ return 0;
+ }
+
+ for(i = 0; i < info_length/sizeof(uint32_t); i++)
+ B2N_32(vobu_admap->vobu_start_sectors[i]);
+
+ return 1;
+}
+
+
+static void ifoFree_VOBU_ADMAP_internal(vobu_admap_t *vobu_admap) {
+ if(vobu_admap) {
+ free(vobu_admap->vobu_start_sectors);
+ free(vobu_admap);
+ }
+}
+
+void ifoFree_VOBU_ADMAP(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ ifoFree_VOBU_ADMAP_internal(ifofile->menu_vobu_admap);
+ ifofile->menu_vobu_admap = 0;
+}
+
+void ifoFree_TITLE_VOBU_ADMAP(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ ifoFree_VOBU_ADMAP_internal(ifofile->vts_vobu_admap);
+ ifofile->vts_vobu_admap = 0;
+}
+
+int ifoRead_PGCIT(ifo_handle_t *ifofile) {
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vtsi_mat)
+ return 0;
+
+ if(ifofile->vtsi_mat->vts_pgcit == 0) /* mandatory */
+ return 0;
+
+ ifofile->vts_pgcit = malloc(sizeof(pgcit_t));
+ if(!ifofile->vts_pgcit)
+ return 0;
+
+ if(!ifoRead_PGCIT_internal(ifofile, ifofile->vts_pgcit,
+ ifofile->vtsi_mat->vts_pgcit * DVD_BLOCK_LEN)) {
+ free(ifofile->vts_pgcit);
+ ifofile->vts_pgcit = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+static int ifoRead_PGCIT_internal(ifo_handle_t *ifofile, pgcit_t *pgcit,
+ unsigned int offset) {
+ int i, info_length;
+ uint8_t *data, *ptr;
+
+ if(!DVDFileSeek_(ifofile->file, offset))
+ return 0;
+
+ if(!(DVDReadBytes(ifofile->file, pgcit, PGCIT_SIZE)))
+ return 0;
+
+ B2N_16(pgcit->nr_of_pgci_srp);
+ B2N_32(pgcit->last_byte);
+
+ CHECK_ZERO(pgcit->zero_1);
+ /* assert(pgcit->nr_of_pgci_srp != 0);
+ Magic Knight Rayearth Daybreak is mastered very strange and has
+ Titles with 0 PTTs. */
+ CHECK_VALUE(pgcit->nr_of_pgci_srp < 10000); // ?? seen max of 1338
+
+ info_length = pgcit->nr_of_pgci_srp * PGCI_SRP_SIZE;
+ data = malloc(info_length);
+ if(!data)
+ return 0;
+
+ if(info_length && !(DVDReadBytes(ifofile->file, data, info_length))) {
+ free(data);
+ return 0;
+ }
+
+ pgcit->pgci_srp = malloc(pgcit->nr_of_pgci_srp * sizeof(pgci_srp_t));
+ if(!pgcit->pgci_srp) {
+ free(data);
+ return 0;
+ }
+ ptr = data;
+ for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+ memcpy(&pgcit->pgci_srp[i], ptr, PGCI_LU_SIZE);
+ ptr += PGCI_LU_SIZE;
+ B2N_16(pgcit->pgci_srp[i].ptl_id_mask);
+ B2N_32(pgcit->pgci_srp[i].pgc_start_byte);
+ CHECK_VALUE(pgcit->pgci_srp[i].unknown1 == 0);
+ }
+ free(data);
+
+ for(i = 0; i < pgcit->nr_of_pgci_srp; i++)
+ CHECK_VALUE(pgcit->pgci_srp[i].pgc_start_byte + PGC_SIZE <= pgcit->last_byte+1);
+
+ for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+ pgcit->pgci_srp[i].pgc = malloc(sizeof(pgc_t));
+ if(!pgcit->pgci_srp[i].pgc) {
+ int j;
+ for(j = 0; j < i; j++) {
+ ifoFree_PGC(pgcit->pgci_srp[j].pgc);
+ free(pgcit->pgci_srp[j].pgc);
+ }
+ 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 = malloc(sizeof(pgci_ut_t));
+ if(!ifofile->pgci_ut)
+ return 0;
+
+ if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN)) {
+ free(ifofile->pgci_ut);
+ ifofile->pgci_ut = 0;
+ return 0;
+ }
+
+ if(!(DVDReadBytes(ifofile->file, ifofile->pgci_ut, PGCI_UT_SIZE))) {
+ free(ifofile->pgci_ut);
+ ifofile->pgci_ut = 0;
+ return 0;
+ }
+
+ pgci_ut = ifofile->pgci_ut;
+
+ B2N_16(pgci_ut->nr_of_lus);
+ B2N_32(pgci_ut->last_byte);
+
+ CHECK_ZERO(pgci_ut->zero_1);
+ CHECK_VALUE(pgci_ut->nr_of_lus != 0);
+ CHECK_VALUE(pgci_ut->nr_of_lus < 100); // ?? 3-4 ?
+ CHECK_VALUE((uint32_t)pgci_ut->nr_of_lus * PGCI_LU_SIZE < pgci_ut->last_byte);
+
+ info_length = pgci_ut->nr_of_lus * PGCI_LU_SIZE;
+ data = malloc(info_length);
+ if(!data) {
+ free(pgci_ut);
+ ifofile->pgci_ut = 0;
+ return 0;
+ }
+ if(!(DVDReadBytes(ifofile->file, data, info_length))) {
+ free(data);
+ free(pgci_ut);
+ ifofile->pgci_ut = 0;
+ return 0;
+ }
+
+ pgci_ut->lu = malloc(pgci_ut->nr_of_lus * sizeof(pgci_lu_t));
+ if(!pgci_ut->lu) {
+ free(data);
+ free(pgci_ut);
+ ifofile->pgci_ut = 0;
+ return 0;
+ }
+ ptr = data;
+ for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+ memcpy(&pgci_ut->lu[i], ptr, PGCI_LU_SIZE);
+ ptr += PGCI_LU_SIZE;
+ B2N_16(pgci_ut->lu[i].lang_code);
+ B2N_32(pgci_ut->lu[i].lang_start_byte);
+ }
+ free(data);
+
+ for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+ // Maybe this is only defined for v1.1 and later titles?
+ /* If the bits in 'lu[i].exists' are enumerated abcd efgh then:
+ VTS_x_yy.IFO VIDEO_TS.IFO
+ a == 0x83 "Root" 0x82 "Title"
+ b == 0x84 "Subpicture"
+ c == 0x85 "Audio"
+ d == 0x86 "Angle"
+ e == 0x87 "PTT"
+ */
+ CHECK_VALUE((pgci_ut->lu[i].exists & 0x07) == 0);
+ }
+
+ for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+ pgci_ut->lu[i].pgcit = malloc(sizeof(pgcit_t));
+ if(!pgci_ut->lu[i].pgcit) {
+ unsigned int j;
+ for(j = 0; j < i; j++) {
+ ifoFree_PGCIT_internal(pgci_ut->lu[j].pgcit);
+ free(pgci_ut->lu[j].pgcit);
+ }
+ free(pgci_ut->lu);
+ free(pgci_ut);
+ ifofile->pgci_ut = 0;
+ return 0;
+ }
+ if(!ifoRead_PGCIT_internal(ifofile, pgci_ut->lu[i].pgcit,
+ sector * DVD_BLOCK_LEN
+ + pgci_ut->lu[i].lang_start_byte)) {
+ unsigned int j;
+ for(j = 0; j < i; j++) {
+ ifoFree_PGCIT_internal(pgci_ut->lu[j].pgcit);
+ free(pgci_ut->lu[j].pgcit);
+ }
+ free(pgci_ut->lu[i].pgcit);
+ free(pgci_ut->lu);
+ free(pgci_ut);
+ ifofile->pgci_ut = 0;
+ return 0;
+ }
+ // FIXME: Iterate and verify that all menus that should exists accordingly
+ // to pgci_ut->lu[i].exists really do?
+ }
+
+ return 1;
+}
+
+
+void ifoFree_PGCI_UT(ifo_handle_t *ifofile) {
+ unsigned int i;
+
+ if(!ifofile)
+ return;
+
+ if(ifofile->pgci_ut) {
+ for(i = 0; i < ifofile->pgci_ut->nr_of_lus; i++) {
+ ifoFree_PGCIT_internal(ifofile->pgci_ut->lu[i].pgcit);
+ free(ifofile->pgci_ut->lu[i].pgcit);
+ }
+ free(ifofile->pgci_ut->lu);
+ free(ifofile->pgci_ut);
+ ifofile->pgci_ut = 0;
+ }
+}
+
+static int ifoRead_VTS_ATTRIBUTES(ifo_handle_t *ifofile,
+ vts_attributes_t *vts_attributes,
+ unsigned int offset) {
+ unsigned int i;
+
+ if(!DVDFileSeek_(ifofile->file, offset))
+ return 0;
+
+ if(!(DVDReadBytes(ifofile->file, vts_attributes, sizeof(vts_attributes_t))))
+ return 0;
+
+ B2N_32(vts_attributes->last_byte);
+ B2N_32(vts_attributes->vts_cat);
+ B2N_16(vts_attributes->vtsm_audio_attr.lang_code);
+ B2N_16(vts_attributes->vtsm_subp_attr.lang_code);
+ for(i = 0; i < 8; i++)
+ B2N_16(vts_attributes->vtstt_audio_attr[i].lang_code);
+ for(i = 0; i < 32; i++)
+ B2N_16(vts_attributes->vtstt_subp_attr[i].lang_code);
+
+ CHECK_ZERO(vts_attributes->zero_1);
+ CHECK_ZERO(vts_attributes->zero_2);
+ CHECK_ZERO(vts_attributes->zero_3);
+ CHECK_ZERO(vts_attributes->zero_4);
+ CHECK_ZERO(vts_attributes->zero_5);
+ CHECK_ZERO(vts_attributes->zero_6);
+ CHECK_ZERO(vts_attributes->zero_7);
+ CHECK_VALUE(vts_attributes->nr_of_vtsm_audio_streams <= 1);
+ CHECK_VALUE(vts_attributes->nr_of_vtsm_subp_streams <= 1);
+ CHECK_VALUE(vts_attributes->nr_of_vtstt_audio_streams <= 8);
+ for(i = vts_attributes->nr_of_vtstt_audio_streams; i < 8; i++)
+ CHECK_ZERO(vts_attributes->vtstt_audio_attr[i]);
+ CHECK_VALUE(vts_attributes->nr_of_vtstt_subp_streams <= 32);
+ {
+ unsigned int nr_coded;
+ CHECK_VALUE(vts_attributes->last_byte + 1 >= VTS_ATTRIBUTES_MIN_SIZE);
+ nr_coded = (vts_attributes->last_byte + 1 - VTS_ATTRIBUTES_MIN_SIZE)/6;
+ // This is often nr_coded = 70, how do you know how many there really are?
+ if(nr_coded > 32) { // We haven't read more from disk/file anyway
+ nr_coded = 32;
+ }
+ CHECK_VALUE(vts_attributes->nr_of_vtstt_subp_streams <= nr_coded);
+ for(i = vts_attributes->nr_of_vtstt_subp_streams; i < nr_coded; i++)
+ CHECK_ZERO(vts_attributes->vtstt_subp_attr[i]);
+ }
+
+ return 1;
+}
+
+
+
+int ifoRead_VTS_ATRT(ifo_handle_t *ifofile) {
+ vts_atrt_t *vts_atrt;
+ unsigned int i, info_length, sector;
+ uint32_t *data;
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vmgi_mat)
+ return 0;
+
+ if(ifofile->vmgi_mat->vts_atrt == 0) /* mandatory */
+ return 0;
+
+ sector = ifofile->vmgi_mat->vts_atrt;
+ if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN))
+ return 0;
+
+ vts_atrt = malloc(sizeof(vts_atrt_t));
+ if(!vts_atrt)
+ return 0;
+
+ ifofile->vts_atrt = vts_atrt;
+
+ if(!(DVDReadBytes(ifofile->file, vts_atrt, VTS_ATRT_SIZE))) {
+ free(vts_atrt);
+ ifofile->vts_atrt = 0;
+ return 0;
+ }
+
+ B2N_16(vts_atrt->nr_of_vtss);
+ B2N_32(vts_atrt->last_byte);
+
+ CHECK_ZERO(vts_atrt->zero_1);
+ CHECK_VALUE(vts_atrt->nr_of_vtss != 0);
+ CHECK_VALUE(vts_atrt->nr_of_vtss < 100); //??
+ CHECK_VALUE((uint32_t)vts_atrt->nr_of_vtss * (4 + VTS_ATTRIBUTES_MIN_SIZE) +
+ VTS_ATRT_SIZE < vts_atrt->last_byte + 1);
+
+ info_length = vts_atrt->nr_of_vtss * sizeof(uint32_t);
+ data = malloc(info_length);
+ if(!data) {
+ free(vts_atrt);
+ ifofile->vts_atrt = 0;
+ return 0;
+ }
+
+ vts_atrt->vts_atrt_offsets = data;
+
+ if(!(DVDReadBytes(ifofile->file, data, info_length))) {
+ free(data);
+ free(vts_atrt);
+ ifofile->vts_atrt = 0;
+ return 0;
+ }
+
+ for(i = 0; i < vts_atrt->nr_of_vtss; i++) {
+ B2N_32(data[i]);
+ CHECK_VALUE(data[i] + VTS_ATTRIBUTES_MIN_SIZE < vts_atrt->last_byte + 1);
+ }
+
+ info_length = vts_atrt->nr_of_vtss * sizeof(vts_attributes_t);
+ vts_atrt->vts = malloc(info_length);
+ if(!vts_atrt->vts) {
+ free(data);
+ free(vts_atrt);
+ ifofile->vts_atrt = 0;
+ return 0;
+ }
+ for(i = 0; i < vts_atrt->nr_of_vtss; i++) {
+ unsigned int offset = data[i];
+ if(!ifoRead_VTS_ATTRIBUTES(ifofile, &(vts_atrt->vts[i]),
+ (sector * DVD_BLOCK_LEN) + offset)) {
+ free(data);
+ free(vts_atrt);
+ ifofile->vts_atrt = 0;
+ return 0;
+ }
+
+ // This assert cant be in ifoRead_VTS_ATTRIBUTES
+ CHECK_VALUE(offset + vts_atrt->vts[i].last_byte <= vts_atrt->last_byte + 1);
+ // Is this check correct?
+ }
+
+ return 1;
+}
+
+
+void ifoFree_VTS_ATRT(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ if(ifofile->vts_atrt) {
+ free(ifofile->vts_atrt->vts);
+ free(ifofile->vts_atrt->vts_atrt_offsets);
+ free(ifofile->vts_atrt);
+ ifofile->vts_atrt = 0;
+ }
+}
+
+
+int ifoRead_TXTDT_MGI(ifo_handle_t *ifofile) {
+ txtdt_mgi_t *txtdt_mgi;
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vmgi_mat)
+ return 0;
+
+ /* Return successfully if there is nothing to read. */
+ if(ifofile->vmgi_mat->txtdt_mgi == 0)
+ return 1;
+
+ if(!DVDFileSeek_(ifofile->file,
+ ifofile->vmgi_mat->txtdt_mgi * DVD_BLOCK_LEN))
+ return 0;
+
+ txtdt_mgi = malloc(sizeof(txtdt_mgi_t));
+ if(!txtdt_mgi) {
+ return 0;
+ }
+ ifofile->txtdt_mgi = txtdt_mgi;
+
+ if(!(DVDReadBytes(ifofile->file, txtdt_mgi, TXTDT_MGI_SIZE))) {
+ 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/dvdread/ifo_read.h b/dvdread/ifo_read.h
new file mode 100644
index 0000000000..d64f6e712c
--- /dev/null
+++ b/dvdread/ifo_read.h
@@ -0,0 +1,231 @@
+#ifndef IFO_READ_H_INCLUDED
+#define IFO_READ_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001, 2002 Björn Englund <d4bjorn@dtek.chalmers.se>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
+ * detailed changelog at http://svn.mplayerhq.hu/mplayer/trunk/
+ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ifo_types.h"
+#include "dvd_reader.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * handle = ifoOpen(dvd, title);
+ *
+ * Opens an IFO and reads in all the data for the IFO file corresponding to the
+ * given title. If title 0 is given, the video manager IFO file is read.
+ * Returns a handle to a completely parsed structure.
+ */
+ifo_handle_t *ifoOpen(dvd_reader_t *, int );
+
+/**
+ * handle = ifoOpenVMGI(dvd);
+ *
+ * Opens an IFO and reads in _only_ the vmgi_mat data. This call can be used
+ * together with the calls below to read in each segment of the IFO file on
+ * demand.
+ */
+ifo_handle_t *ifoOpenVMGI(dvd_reader_t *);
+
+/**
+ * handle = ifoOpenVTSI(dvd, title);
+ *
+ * Opens an IFO and reads in _only_ the vtsi_mat data. This call can be used
+ * together with the calls below to read in each segment of the IFO file on
+ * demand.
+ */
+ifo_handle_t *ifoOpenVTSI(dvd_reader_t *, int);
+
+/**
+ * ifoClose(ifofile);
+ * Cleans up the IFO information. This will free all data allocated for the
+ * substructures.
+ */
+void ifoClose(ifo_handle_t *);
+
+/**
+ * The following functions are for reading only part of the VMGI/VTSI files.
+ * Returns 1 if the data was successfully read and 0 on error.
+ */
+
+/**
+ * okay = ifoRead_PLT_MAIT(ifofile);
+ *
+ * Read in the Parental Management Information table, filling the
+ * ifofile->ptl_mait structure and its substructures. This data is only
+ * located in the video manager information file. This fills the
+ * ifofile->ptl_mait structure and all its substructures.
+ */
+int ifoRead_PTL_MAIT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_VTS_ATRT(ifofile);
+ *
+ * Read in the attribute table for the main menu vob, filling the
+ * ifofile->vts_atrt structure and its substructures. Only located in the
+ * video manager information file. This fills in the ifofile->vts_atrt
+ * structure and all its substructures.
+ */
+int ifoRead_VTS_ATRT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_TT_SRPT(ifofile);
+ *
+ * Reads the title info for the main menu, filling the ifofile->tt_srpt
+ * structure and its substructures. This data is only located in the video
+ * manager information file. This structure is mandatory in the IFO file.
+ */
+int ifoRead_TT_SRPT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_VTS_PTT_SRPT(ifofile);
+ *
+ * Reads in the part of title search pointer table, filling the
+ * ifofile->vts_ptt_srpt structure and its substructures. This data is only
+ * located in the video title set information file. This structure is
+ * mandatory, and must be included in the VTSI file.
+ */
+int ifoRead_VTS_PTT_SRPT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_FP_PGC(ifofile);
+ *
+ * Reads in the first play program chain data, filling the
+ * ifofile->first_play_pgc structure. This data is only located in the video
+ * manager information file (VMGI). This structure is optional.
+ */
+int ifoRead_FP_PGC(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_PGCIT(ifofile);
+ *
+ * Reads in the program chain information table for the video title set. Fills
+ * in the ifofile->vts_pgcit structure and its substructures, which includes
+ * the data for each program chain in the set. This data is only located in
+ * the video title set information file. This structure is mandatory, and must
+ * be included in the VTSI file.
+ */
+int ifoRead_PGCIT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_PGCI_UT(ifofile);
+ *
+ * Reads in the menu PGCI unit table for the menu VOB. For the video manager,
+ * this corresponds to the VIDEO_TS.VOB file, and for each title set, this
+ * corresponds to the VTS_XX_0.VOB file. This data is located in both the
+ * video manager and video title set information files. For VMGI files, this
+ * fills the ifofile->vmgi_pgci_ut structure and all its substructures. For
+ * VTSI files, this fills the ifofile->vtsm_pgci_ut structure.
+ */
+int ifoRead_PGCI_UT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_VTS_TMAPT(ifofile);
+ *
+ * Reads in the VTS Time Map Table, this data is only located in the video
+ * title set information file. This fills the ifofile->vts_tmapt structure
+ * and all its substructures. When pressent enables VOBU level time-based
+ * seeking for One_Sequential_PGC_Titles.
+ */
+int ifoRead_VTS_TMAPT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_C_ADT(ifofile);
+ *
+ * Reads in the cell address table for the menu VOB. For the video manager,
+ * this corresponds to the VIDEO_TS.VOB file, and for each title set, this
+ * corresponds to the VTS_XX_0.VOB file. This data is located in both the
+ * video manager and video title set information files. For VMGI files, this
+ * fills the ifofile->vmgm_c_adt structure and all its substructures. For VTSI
+ * files, this fills the ifofile->vtsm_c_adt structure.
+ */
+int ifoRead_C_ADT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_TITLE_C_ADT(ifofile);
+ *
+ * Reads in the cell address table for the video title set corresponding to
+ * this IFO file. This data is only located in the video title set information
+ * file. This structure is mandatory, and must be included in the VTSI file.
+ * This call fills the ifofile->vts_c_adt structure and its substructures.
+ */
+int ifoRead_TITLE_C_ADT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_VOBU_ADMAP(ifofile);
+ *
+ * Reads in the VOBU address map for the menu VOB. For the video manager, this
+ * corresponds to the VIDEO_TS.VOB file, and for each title set, this
+ * corresponds to the VTS_XX_0.VOB file. This data is located in both the
+ * video manager and video title set information files. For VMGI files, this
+ * fills the ifofile->vmgm_vobu_admap structure and all its substructures. For
+ * VTSI files, this fills the ifofile->vtsm_vobu_admap structure.
+ */
+int ifoRead_VOBU_ADMAP(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_TITLE_VOBU_ADMAP(ifofile);
+ *
+ * Reads in the VOBU address map for the associated video title set. This data
+ * is only located in the video title set information file. This structure is
+ * mandatory, and must be included in the VTSI file. Fills the
+ * ifofile->vts_vobu_admap structure and its substructures.
+ */
+int ifoRead_TITLE_VOBU_ADMAP(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_TXTDT_MGI(ifofile);
+ *
+ * Reads in the text data strings for the DVD. Fills the ifofile->txtdt_mgi
+ * structure and all its substructures. This data is only located in the video
+ * manager information file. This structure is mandatory, and must be included
+ * in the VMGI file.
+ */
+int ifoRead_TXTDT_MGI(ifo_handle_t *);
+
+/**
+ * The following functions are used for freeing parsed sections of the
+ * ifo_handle_t structure and the allocated substructures. The free calls
+ * below are safe: they will not mind if you attempt to free part of an IFO
+ * file which was not read in or which does not exist.
+ */
+void ifoFree_PTL_MAIT(ifo_handle_t *);
+void ifoFree_VTS_ATRT(ifo_handle_t *);
+void ifoFree_TT_SRPT(ifo_handle_t *);
+void ifoFree_VTS_PTT_SRPT(ifo_handle_t *);
+void ifoFree_FP_PGC(ifo_handle_t *);
+void ifoFree_PGCIT(ifo_handle_t *);
+void ifoFree_PGCI_UT(ifo_handle_t *);
+void ifoFree_VTS_TMAPT(ifo_handle_t *);
+void ifoFree_C_ADT(ifo_handle_t *);
+void ifoFree_TITLE_C_ADT(ifo_handle_t *);
+void ifoFree_VOBU_ADMAP(ifo_handle_t *);
+void ifoFree_TITLE_VOBU_ADMAP(ifo_handle_t *);
+void ifoFree_TXTDT_MGI(ifo_handle_t *);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* IFO_READ_H_INCLUDED */
diff --git a/dvdread/ifo_types.h b/dvdread/ifo_types.h
new file mode 100644
index 0000000000..4eb6144e1c
--- /dev/null
+++ b/dvdread/ifo_types.h
@@ -0,0 +1,937 @@
+#ifndef IFO_TYPES_H_INCLUDED
+#define IFO_TYPES_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001 Björn Englund <d4bjorn@dtek.chalmers.se>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
+ * detailed changelog at http://svn.mplayerhq.hu/mplayer/trunk/
+ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <inttypes.h>
+#include "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 : 1;
+ unsigned int bit_rate : 1;
+
+ unsigned int picture_size : 2;
+ unsigned int letterboxed : 1;
+ unsigned int film_mode : 1;
+#else
+ unsigned int permitted_df : 2;
+ unsigned int display_aspect_ratio : 2;
+ unsigned int video_format : 2;
+ unsigned int mpeg_version : 2;
+
+ unsigned int film_mode : 1;
+ unsigned int letterboxed : 1;
+ unsigned int picture_size : 2;
+
+ unsigned int bit_rate : 1;
+ unsigned int unknown1 : 1;
+ unsigned int line21_cc_2 : 1;
+ unsigned int line21_cc_1 : 1;
+#endif
+} ATTRIBUTE_PACKED video_attr_t;
+
+/**
+ * Audio Attributes.
+ */
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+ unsigned int audio_format : 3;
+ unsigned int multichannel_extension : 1;
+ unsigned int lang_type : 2;
+ unsigned int application_mode : 2;
+
+ unsigned int quantization : 2;
+ unsigned int sample_frequency : 2;
+ unsigned int unknown1 : 1;
+ unsigned int channels : 3;
+#else
+ unsigned int application_mode : 2;
+ unsigned int lang_type : 2;
+ unsigned int multichannel_extension : 1;
+ unsigned int audio_format : 3;
+
+ unsigned int channels : 3;
+ unsigned int unknown1 : 1;
+ unsigned int sample_frequency : 2;
+ unsigned int quantization : 2;
+#endif
+ uint16_t lang_code;
+ uint8_t lang_extension;
+ uint8_t code_extension;
+ uint8_t unknown3;
+ union {
+ struct ATTRIBUTE_PACKED {
+#ifdef WORDS_BIGENDIAN
+ unsigned int unknown4 : 1;
+ unsigned int channel_assignment : 3;
+ unsigned int version : 2;
+ unsigned int mc_intro : 1; /* probably 0: true, 1:false */
+ unsigned int mode : 1; /* Karaoke mode 0: solo 1: duet */
+#else
+ unsigned int mode : 1;
+ unsigned int mc_intro : 1;
+ unsigned int version : 2;
+ unsigned int channel_assignment : 3;
+ unsigned int unknown4 : 1;
+#endif
+ } karaoke;
+ struct ATTRIBUTE_PACKED {
+#ifdef WORDS_BIGENDIAN
+ unsigned int unknown5 : 4;
+ unsigned int dolby_encoded : 1; /* suitable for surround decoding */
+ unsigned int unknown6 : 3;
+#else
+ unsigned int unknown6 : 3;
+ unsigned int dolby_encoded : 1;
+ unsigned int unknown5 : 4;
+#endif
+ } surround;
+ } app_info;
+} ATTRIBUTE_PACKED audio_attr_t;
+
+
+/**
+ * MultiChannel Extension
+ */
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+ unsigned int zero1 : 7;
+ unsigned int ach0_gme : 1;
+
+ unsigned int zero2 : 7;
+ unsigned int ach1_gme : 1;
+
+ unsigned int zero3 : 4;
+ unsigned int ach2_gv1e : 1;
+ unsigned int ach2_gv2e : 1;
+ unsigned int ach2_gm1e : 1;
+ unsigned int ach2_gm2e : 1;
+
+ unsigned int zero4 : 4;
+ unsigned int ach3_gv1e : 1;
+ unsigned int ach3_gv2e : 1;
+ unsigned int ach3_gmAe : 1;
+ unsigned int ach3_se2e : 1;
+
+ unsigned int zero5 : 4;
+ unsigned int ach4_gv1e : 1;
+ unsigned int ach4_gv2e : 1;
+ unsigned int ach4_gmBe : 1;
+ unsigned int ach4_seBe : 1;
+#else
+ unsigned int ach0_gme : 1;
+ unsigned int zero1 : 7;
+
+ unsigned int ach1_gme : 1;
+ unsigned int zero2 : 7;
+
+ unsigned int ach2_gm2e : 1;
+ unsigned int ach2_gm1e : 1;
+ unsigned int ach2_gv2e : 1;
+ unsigned int ach2_gv1e : 1;
+ unsigned int zero3 : 4;
+
+ unsigned int ach3_se2e : 1;
+ unsigned int ach3_gmAe : 1;
+ unsigned int ach3_gv2e : 1;
+ unsigned int ach3_gv1e : 1;
+ unsigned int zero4 : 4;
+
+ unsigned int ach4_seBe : 1;
+ unsigned int ach4_gmBe : 1;
+ unsigned int ach4_gv2e : 1;
+ unsigned int ach4_gv1e : 1;
+ unsigned int zero5 : 4;
+#endif
+ uint8_t zero6[19];
+} ATTRIBUTE_PACKED multichannel_ext_t;
+
+
+/**
+ * Subpicture Attributes.
+ */
+typedef struct {
+ /*
+ * type: 0 not specified
+ * 1 language
+ * 2 other
+ * coding mode: 0 run length
+ * 1 extended
+ * 2 other
+ * language: indicates language if type == 1
+ * lang extension: if type == 1 contains the lang extension
+ */
+#ifdef WORDS_BIGENDIAN
+ unsigned int code_mode : 3;
+ unsigned int zero1 : 3;
+ unsigned int type : 2;
+#else
+ unsigned int type : 2;
+ unsigned int zero1 : 3;
+ unsigned int code_mode : 3;
+#endif
+ uint8_t zero2;
+ uint16_t lang_code;
+ uint8_t lang_extension;
+ uint8_t code_extension;
+} ATTRIBUTE_PACKED subp_attr_t;
+
+
+
+/**
+ * PGC Command Table.
+ */
+typedef struct {
+ uint16_t nr_of_pre;
+ uint16_t nr_of_post;
+ uint16_t nr_of_cell;
+ uint16_t 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 playback_mode : 1; /**< When set, enter StillMode after each VOBU */
+ unsigned int restricted : 1; /**< ?? drop out of fastforward? */
+ unsigned int unknown2 : 6;
+#else
+ unsigned int seamless_angle : 1;
+ unsigned int stc_discontinuity: 1;
+ unsigned int interleaved : 1;
+ unsigned int seamless_play : 1;
+ unsigned int block_type : 2;
+ unsigned int block_mode : 2;
+
+ unsigned int unknown2 : 6;
+ unsigned int restricted : 1;
+ unsigned int playback_mode : 1;
+#endif
+ uint8_t still_time;
+ uint8_t cell_cmd_nr;
+ dvd_time_t playback_time;
+ uint32_t first_sector;
+ uint32_t first_ilvu_end_sector;
+ uint32_t last_vobu_start_sector;
+ uint32_t last_sector;
+} ATTRIBUTE_PACKED cell_playback_t;
+
+#define BLOCK_TYPE_NONE 0x0
+#define BLOCK_TYPE_ANGLE_BLOCK 0x1
+
+#define BLOCK_MODE_NOT_IN_BLOCK 0x0
+#define BLOCK_MODE_FIRST_CELL 0x1
+#define BLOCK_MODE_IN_BLOCK 0x2
+#define BLOCK_MODE_LAST_CELL 0x3
+
+/**
+ * Cell Position Information.
+ */
+typedef struct {
+ uint16_t vob_id_nr;
+ uint8_t zero_1;
+ uint8_t cell_nr;
+} ATTRIBUTE_PACKED cell_position_t;
+
+/**
+ * User Operations.
+ */
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+ unsigned int zero : 7; /* 25-31 */
+ unsigned int video_pres_mode_change : 1; /* 24 */
+
+ unsigned int karaoke_audio_pres_mode_change : 1; /* 23 */
+ unsigned int angle_change : 1;
+ unsigned int subpic_stream_change : 1;
+ unsigned int audio_stream_change : 1;
+ unsigned int pause_on : 1;
+ unsigned int still_off : 1;
+ unsigned int button_select_or_activate : 1;
+ unsigned int resume : 1; /* 16 */
+
+ unsigned int chapter_menu_call : 1; /* 15 */
+ unsigned int angle_menu_call : 1;
+ unsigned int audio_menu_call : 1;
+ unsigned int subpic_menu_call : 1;
+ unsigned int root_menu_call : 1;
+ unsigned int title_menu_call : 1;
+ unsigned int backward_scan : 1;
+ unsigned int forward_scan : 1; /* 8 */
+
+ unsigned int next_pg_search : 1; /* 7 */
+ unsigned int prev_or_top_pg_search : 1;
+ unsigned int time_or_chapter_search : 1;
+ unsigned int go_up : 1;
+ unsigned int stop : 1;
+ unsigned int title_play : 1;
+ unsigned int chapter_search_or_play : 1;
+ unsigned int title_or_time_play : 1; /* 0 */
+#else
+ unsigned int video_pres_mode_change : 1; /* 24 */
+ unsigned int zero : 7; /* 25-31 */
+
+ unsigned int resume : 1; /* 16 */
+ unsigned int button_select_or_activate : 1;
+ unsigned int still_off : 1;
+ unsigned int pause_on : 1;
+ unsigned int audio_stream_change : 1;
+ unsigned int subpic_stream_change : 1;
+ unsigned int angle_change : 1;
+ unsigned int karaoke_audio_pres_mode_change : 1; /* 23 */
+
+ unsigned int forward_scan : 1; /* 8 */
+ unsigned int backward_scan : 1;
+ unsigned int title_menu_call : 1;
+ unsigned int root_menu_call : 1;
+ unsigned int subpic_menu_call : 1;
+ unsigned int audio_menu_call : 1;
+ unsigned int angle_menu_call : 1;
+ unsigned int chapter_menu_call : 1; /* 15 */
+
+ unsigned int title_or_time_play : 1; /* 0 */
+ unsigned int chapter_search_or_play : 1;
+ unsigned int title_play : 1;
+ unsigned int stop : 1;
+ unsigned int go_up : 1;
+ unsigned int time_or_chapter_search : 1;
+ unsigned int prev_or_top_pg_search : 1;
+ unsigned int next_pg_search : 1; /* 7 */
+#endif
+} ATTRIBUTE_PACKED user_ops_t;
+
+/**
+ * Subpicture stream mapping for a subtitle
+ */
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+ unsigned int present : 1;
+ unsigned int zero1 : 2;
+ unsigned int s_4p3 : 5; /* stream for 4:3 on any display */
+
+ unsigned int zero2 : 3;
+ unsigned int s_wide : 5; /* stream for 16:9 on widescreen display */
+
+ unsigned int zero3 : 3;
+ unsigned int s_lbox : 5; /* stream for 16:9 on letterboxed 4:3 display */
+
+ unsigned int zero4 : 3;
+ unsigned int s_panscan : 5; /* stream for 16:9 with pan&scan data on 4:3 display */
+#else
+ unsigned int s_4p3 : 5; /* stream for 4:3 on any display */
+ unsigned int zero1 : 2;
+ unsigned int present : 1;
+
+ unsigned int s_wide : 5; /* stream for 16:9 on widescreen display */
+ unsigned int zero2 : 3;
+
+ unsigned int s_lbox : 5; /* stream for 16:9 on letterboxed 4:3 display */
+ unsigned int zero3 : 3;
+
+ unsigned int s_panscan : 5; /* stream for 16:9 with pan&scan data on 4:3 display */
+ unsigned int zero4 : 3;
+#endif
+} ATTRIBUTE_PACKED subp_mapping_t;
+
+/**
+ * Audio stream mapping for a soundtrack
+ */
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+ unsigned int present : 1;
+ unsigned int zero1 : 4;
+ unsigned int s_audio : 3;
+#else
+ unsigned int s_audio : 3;
+ unsigned int zero1 : 4;
+ unsigned int present : 1;
+#endif
+ uint8_t zero2;
+} ATTRIBUTE_PACKED audio_mapping_t;
+
+/**
+ * Program Chain Information.
+ */
+typedef struct {
+ uint16_t zero_1;
+ uint8_t nr_of_programs;
+ uint8_t nr_of_cells;
+ dvd_time_t playback_time;
+ user_ops_t prohibited_ops;
+ audio_mapping_t audio_control[8];
+ subp_mapping_t subp_control[32];
+ uint16_t next_pgc_nr;
+ uint16_t prev_pgc_nr;
+ uint16_t goup_pgc_nr;
+ 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 lang_extension;
+ 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; /* No explicit size given. */
+} 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;
+ unsigned int chapter_search_or_play : 1;
+ unsigned int jlc_exists_in_tt_dom : 1;
+ unsigned int jlc_exists_in_button_cmd : 1;
+ unsigned int jlc_exists_in_prepost_cmd : 1;
+ unsigned int jlc_exists_in_cell_cmd : 1;
+ unsigned int multi_or_random_pgc_title : 1;
+ unsigned int zero_1 : 1;
+#endif
+} ATTRIBUTE_PACKED playback_type_t;
+
+/**
+ * Title Information.
+ */
+typedef struct {
+ playback_type_t pb_ty;
+ uint8_t nr_of_angles;
+ uint16_t nr_of_ptts;
+ uint16_t parental_id;
+ uint8_t title_set_nr;
+ uint8_t vts_ttn;
+ uint32_t title_set_sector;
+} ATTRIBUTE_PACKED title_info_t;
+
+/**
+ * PartOfTitle Search Pointer Table.
+ */
+typedef struct {
+ uint16_t nr_of_srpts;
+ uint16_t zero_1;
+ uint32_t last_byte;
+ title_info_t *title;
+} ATTRIBUTE_PACKED tt_srpt_t;
+#define TT_SRPT_SIZE 8
+
+
+/**
+ * Parental Management Information Unit Table.
+ * Level 1 (US: G), ..., 7 (US: NC-17), 8
+ */
+typedef uint16_t pf_level_t[8];
+
+/**
+ * Parental Management Information Unit Table.
+ */
+typedef struct {
+ uint16_t country_code;
+ uint16_t zero_1;
+ uint16_t pf_ptl_mai_start_byte;
+ uint16_t zero_2;
+ pf_level_t *pf_ptl_mai; /* table of (nr_of_vtss + 1), video_ts is first */
+} ATTRIBUTE_PACKED ptl_mait_country_t;
+#define PTL_MAIT_COUNTRY_SIZE 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;
+ uint32_t *vts_atrt_offsets; /* offsets table for each vts_attributes */
+} 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 */
+ uint32_t vtsm_c_adt; /* sector */
+ uint32_t vtsm_vobu_admap; /* sector */
+ uint32_t vts_c_adt; /* sector */
+ uint32_t vts_vobu_admap; /* sector */
+ uint8_t zero_13[24];
+
+ video_attr_t vtsm_video_attr;
+ uint8_t zero_14;
+ uint8_t nr_of_vtsm_audio_streams; /* should be 0 or 1 */
+ audio_attr_t vtsm_audio_attr;
+ audio_attr_t zero_15[7];
+ uint8_t zero_16[17];
+ uint8_t nr_of_vtsm_subp_streams; /* should be 0 or 1 */
+ subp_attr_t vtsm_subp_attr;
+ subp_attr_t zero_17[27];
+ uint8_t zero_18[2];
+
+ video_attr_t vts_video_attr;
+ uint8_t zero_19;
+ uint8_t nr_of_vts_audio_streams;
+ audio_attr_t vts_audio_attr[8];
+ uint8_t zero_20[17];
+ uint8_t nr_of_vts_subp_streams;
+ subp_attr_t vts_subp_attr[32];
+ uint16_t zero_21;
+ multichannel_ext_t vts_mu_audio_attr[8];
+ /* XXX: how much 'padding' here, if any? */
+} ATTRIBUTE_PACKED vtsi_mat_t;
+
+/**
+ * PartOfTitle Unit Information.
+ */
+typedef struct {
+ uint16_t pgcn;
+ uint16_t pgn;
+} ATTRIBUTE_PACKED ptt_info_t;
+
+/**
+ * PartOfTitle Information.
+ */
+typedef struct {
+ uint16_t nr_of_ptts;
+ ptt_info_t *ptt;
+} ATTRIBUTE_PACKED ttu_t;
+
+/**
+ * PartOfTitle Search Pointer Table.
+ */
+typedef struct {
+ uint16_t nr_of_srpts;
+ uint16_t zero_1;
+ uint32_t last_byte;
+ ttu_t *title;
+ uint32_t *ttu_offset; /* offset table for each ttu */
+} ATTRIBUTE_PACKED vts_ptt_srpt_t;
+#define VTS_PTT_SRPT_SIZE 8
+
+
+/**
+ * Time Map Entry.
+ */
+/* Should this be bit field at all or just the uint32_t? */
+typedef uint32_t map_ent_t;
+
+/**
+ * Time Map.
+ */
+typedef struct {
+ uint8_t tmu; /* Time unit, in seconds */
+ uint8_t zero_1;
+ uint16_t nr_of_entries;
+ map_ent_t *map_ent;
+} ATTRIBUTE_PACKED vts_tmap_t;
+#define VTS_TMAP_SIZE 4
+
+/**
+ * Time Map Table.
+ */
+typedef struct {
+ uint16_t nr_of_tmaps;
+ uint16_t zero_1;
+ uint32_t last_byte;
+ vts_tmap_t *tmap;
+ uint32_t *tmap_offset; /* offset table for each tmap */
+} ATTRIBUTE_PACKED vts_tmapt_t;
+#define VTS_TMAPT_SIZE 8
+
+
+#if PRAGMA_PACK
+#pragma pack()
+#endif
+
+
+/**
+ * The following structure defines an IFO file. The structure is divided into
+ * two parts, the VMGI, or Video Manager Information, which is read from the
+ * VIDEO_TS.[IFO,BUP] file, and the VTSI, or Video Title Set Information, which
+ * is read in from the VTS_XX_0.[IFO,BUP] files.
+ */
+typedef struct {
+ dvd_file_t *file;
+
+ /* VMGI */
+ vmgi_mat_t *vmgi_mat;
+ tt_srpt_t *tt_srpt;
+ pgc_t *first_play_pgc;
+ ptl_mait_t *ptl_mait;
+ vts_atrt_t *vts_atrt;
+ txtdt_mgi_t *txtdt_mgi;
+
+ /* Common */
+ pgci_ut_t *pgci_ut;
+ c_adt_t *menu_c_adt;
+ vobu_admap_t *menu_vobu_admap;
+
+ /* VTSI */
+ vtsi_mat_t *vtsi_mat;
+ vts_ptt_srpt_t *vts_ptt_srpt;
+ pgcit_t *vts_pgcit;
+ vts_tmapt_t *vts_tmapt;
+ c_adt_t *vts_c_adt;
+ vobu_admap_t *vts_vobu_admap;
+} ifo_handle_t;
+
+#endif /* IFO_TYPES_H_INCLUDED */
diff --git a/dvdread/libdvdread_changes.diff b/dvdread/libdvdread_changes.diff
new file mode 100644
index 0000000000..ab3ab92b73
--- /dev/null
+++ b/dvdread/libdvdread_changes.diff
@@ -0,0 +1,778 @@
+diff -Naur dvdread.orig/bswap.h dvdread/bswap.h
+--- dvdread.orig/bswap.h 2005-06-23 00:18:54.000000000 +0200
++++ dvdread/bswap.h 2005-06-23 00:19:10.000000000 +0200
+@@ -59,13 +63,57 @@
+ #define B2N_32(x) x = be32toh(x)
+ #define B2N_64(x) x = be64toh(x)
+
++#elif defined(__DragonFly__)
++#include <sys/endian.h>
++#define B2N_16(x) x = be16toh(x)
++#define B2N_32(x) x = be32toh(x)
++#define B2N_64(x) x = be64toh(x)
++
++#elif defined(ARCH_X86)
++inline static unsigned short bswap_16(unsigned short x)
++{
++ __asm("xchgb %b0,%h0" :
++ "=q" (x) :
++ "0" (x));
++ return x;
++}
++#define B2N_16(x) x = bswap_16(x)
++
++inline static unsigned int bswap_32(unsigned int x)
++{
++ __asm(
++#if __CPU__ > 386
++ "bswap %0":
++ "=r" (x) :
++#else
++ "xchgb %b0,%h0\n"
++ " rorl $16,%0\n"
++ " xchgb %b0,%h0":
++ "=q" (x) :
++#endif
++ "0" (x));
++ return x;
++}
++#define B2N_32(x) x = bswap_32(x)
++
++inline static unsigned long long int bswap_64(unsigned long long int x)
++{
++ register union { __extension__ uint64_t __ll;
++ uint32_t __l[2]; } __x;
++ asm("xchgl %0,%1":
++ "=r"(__x.__l[0]),"=r"(__x.__l[1]):
++ "0"(bswap_32((unsigned long)x)),"1"(bswap_32((unsigned long)(x>>32))));
++ return __x.__ll;
++}
++#define B2N_64(x) x = bswap_64(x)
++
+ /* This is a slow but portable implementation, it has multiple evaluation
+ * problems so beware.
+ * Old FreeBSD's and Solaris don't have <byteswap.h> or any other such
+ * functionality!
+ */
+
+-#elif defined(__FreeBSD__) || defined(__sun) || defined(__bsdi__)
++#elif defined(__FreeBSD__) || defined(__sun) || defined(__bsdi__) || defined(__CYGWIN__)
+ #define B2N_16(x) \
+ x = ((((x) & 0xff00) >> 8) | \
+ (((x) & 0x00ff) << 8))
+diff -Naur dvdread.orig/.cvsignore dvdread/.cvsignore
+diff -Naur dvdread.orig/dvd_input.c dvdread/dvd_input.c
+--- dvdread.orig/dvd_input.c 2005-06-23 00:18:54.000000000 +0200
++++ dvdread/dvd_input.c 2005-06-23 00:30:23.000000000 +0200
+@@ -37,7 +41,7 @@
+
+ #ifdef HAVE_DVDCSS_DVDCSS_H
+ /* linking to libdvdcss */
+-#include <dvdcss/dvdcss.h>
++#include "dvdcss/dvdcss.h"
+ #define DVDcss_open(a) dvdcss_open((char*)(a))
+ #define DVDcss_close dvdcss_close
+ #define DVDcss_seek dvdcss_seek
+@@ -74,7 +78,7 @@
+ dvd_input_t dev;
+
+ /* Allocate the handle structure */
+- dev = (dvd_input_t) malloc(sizeof(dvd_input_t));
++ dev = (dvd_input_t) malloc(sizeof(struct dvd_input_s));
+ if(dev == NULL) {
+ fprintf(stderr, "libdvdread: Could not allocate memory.\n");
+ return NULL;
+@@ -154,7 +158,7 @@
+ dvd_input_t dev;
+
+ /* Allocate the library structure */
+- dev = (dvd_input_t) malloc(sizeof(dvd_input_t));
++ dev = (dvd_input_t) malloc(sizeof(struct dvd_input_s));
+ if(dev == NULL) {
+ fprintf(stderr, "libdvdread: Could not allocate memory.\n");
+ return NULL;
+@@ -320,8 +324,10 @@
+ fprintf(stderr, "DVDCSS_METHOD %s\n", psz_method);
+ fprintf(stderr, "DVDCSS_VERBOSE %s\n", psz_verbose);
+ */
++ /*
+ fprintf(stderr, "libdvdread: Using libdvdcss version %s for DVD access\n",
+ *dvdcss_version);
++ */
+
+ /* libdvdcss wrapper functions */
+ dvdinput_open = css_open;
+diff -Naur dvdread.orig/dvd_input.h dvdread/dvd_input.h
+diff -Naur dvdread.orig/dvd_reader.c dvdread/dvd_reader.c
+--- dvdread.orig/dvd_reader.c 2005-06-23 00:18:54.000000000 +0200
++++ dvdread/dvd_reader.c 2005-06-23 00:19:10.000000000 +0200
+@@ -32,24 +36,36 @@
+ #include <limits.h>
+ #include <dirent.h>
+
+-#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__)|| defined(__DARWIN__)
++#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__) || defined(__DARWIN__) || defined(__DragonFly__)
+ #define SYS_BSD 1
+ #endif
+
+ #if defined(__sun)
+ #include <sys/mnttab.h>
++#elif defined(hpux)
++#include </usr/conf/h/mnttab.h>
+ #elif defined(SYS_BSD)
+ #include <fstab.h>
+-#elif defined(__linux__)
++#elif defined(__linux__) || defined(__CYGWIN__)
+ #include <mntent.h>
+ #endif
+
++#if defined(__MINGW32__) && (__MINGW32_MAJOR_VERSION <= 3) && (__MINGW32_MINOR_VERSION < 10)
++#include <sys/timeb.h>
++static void gettimeofday(struct timeval* t,void* timezone){
++ struct timeb timebuffer;
++ ftime( &timebuffer );
++ t->tv_sec=timebuffer.time;
++ t->tv_usec=1000*timebuffer.millitm;
++}
++#endif
++
+ #include "dvd_udf.h"
+ #include "dvd_input.h"
+ #include "dvd_reader.h"
+ #include "md5.h"
+
+-#define DEFAULT_UDF_CACHE_LEVEL 1
++#define DEFAULT_UDF_CACHE_LEVEL 0
+
+ struct dvd_reader_s {
+ /* Basic information. */
+@@ -278,11 +301,16 @@
+ Darwin /dev/rdisk0, it needs to be the raw device
+ BSD/OS /dev/sr0c (if not mounted) or /dev/rsr0c ('c' any letter will do) */
+ static char *bsd_block2char( const char *path )
++#if defined(__FreeBSD__)
++{
++ return (char *) strdup( path );
++}
++#else
+ {
+ char *new_path;
+
+ /* If it doesn't start with "/dev/" or does start with "/dev/r" exit */
+- if( !strncmp( path, "/dev/", 5 ) || strncmp( path, "/dev/r", 6 ) )
++ if( strncmp( path, "/dev/", 5 ) || !strncmp( path, "/dev/r", 6 ) )
+ return (char *) strdup( path );
+
+ /* Replace "/dev/" with "/dev/r" */
+@@ -292,6 +320,7 @@
+
+ return new_path;
+ }
++#endif /* __FreeBSD__ */
+ #endif
+
+ dvd_reader_t *DVDOpen( const char *path )
+@@ -303,6 +332,16 @@
+ if( path == NULL )
+ return 0;
+
++#ifdef WIN32
++ /* Stat doesn't work on devices under mingwin/cygwin. */
++ if( path[0] && path[1] == ':' && path[2] == '\0' )
++ {
++ /* Don't try to stat the file */
++ fileinfo.st_mode = S_IFBLK;
++ }
++ else
++#endif
++ {
+ ret = stat( path, &fileinfo );
+ if( ret < 0 ) {
+ /* If we can't stat the file, give up */
+@@ -310,6 +349,7 @@
+ perror("");
+ return 0;
+ }
++ }
+
+ /* Try to open libdvdcss or fall back to standard functions */
+ have_css = dvdinput_setup();
+@@ -335,7 +375,7 @@
+ char *path_copy;
+ #if defined(SYS_BSD)
+ struct fstab* fe;
+-#elif defined(__sun) || defined(__linux__)
++#elif defined(__sun) || defined(__linux__) || defined(__CYGWIN__)
+ FILE *mntfile;
+ #endif
+
+@@ -350,7 +390,9 @@
+ if( cdir >= 0 ) {
+ chdir( path_copy );
+ new_path = getcwd( NULL, PATH_MAX );
++#ifndef __MINGW32__
+ fchdir( cdir );
++#endif
+ close( cdir );
+ if( new_path ) {
+ free( path_copy );
+@@ -406,7 +448,7 @@
+ }
+ fclose( mntfile );
+ }
+-#elif defined(__linux__)
++#elif defined(__linux__) || defined(__CYGWIN__)
+ mntfile = fopen( MOUNTED, "r" );
+ if( mntfile ) {
+ struct mntent *me;
+@@ -425,6 +467,9 @@
+ }
+ fclose( mntfile );
+ }
++#elif defined(__MINGW32__)
++ dev_name = strdup(path);
++ auth_drive = DVDOpenImageFile( path, have_css );
+ #endif
+ if( !dev_name ) {
+ fprintf( stderr, "libdvdread: Couldn't find device name.\n" );
+@@ -615,8 +660,8 @@
+ }
+
+ if( dvd->css_state == 1 /* Need key init */ ) {
+- initAllCSSKeys( dvd );
+- dvd->css_state = 2;
++// initAllCSSKeys( dvd );
++// dvd->css_state = 2;
+ }
+ /*
+ if( dvdinput_title( dvd_file->dvd->dev, (int)start ) < 0 ) {
+@@ -915,7 +960,7 @@
+ return (ssize_t)ret;
+ }
+
+-int32_t DVDFileSeek( dvd_file_t *dvd_file, int32_t offset )
++int DVDFileSeek( dvd_file_t *dvd_file, int offset )
+ {
+ /* Check arguments. */
+ if( dvd_file == NULL || offset < 0 )
+diff -Naur dvdread.orig/dvdread_internal.h dvdread/dvdread_internal.h
+--- dvdread.orig/dvdread_internal.h 2005-06-23 00:18:54.000000000 +0200
++++ dvdread/dvdread_internal.h 2005-06-23 00:19:10.000000000 +0200
+@@ -2,11 +2,7 @@
+ #define DVDREAD_INTERNAL_H
+
+
+-#define CHECK_VALUE(arg) \
+- if(!(arg)) { \
+- fprintf(stderr, "\n*** libdvdread: CHECK_VALUE failed in %s:%i ***" \
+- "\n*** for %s ***\n\n", \
+- __FILE__, __LINE__, # arg ); \
+- }
++#define CHECK_VALUE(arg)
++
+
+ #endif /* DVDREAD_INTERNAL_H */
+diff -Naur dvdread.orig/dvd_udf.c dvdread/dvd_udf.c
+--- dvdread.orig/dvd_udf.c 2005-06-23 00:18:54.000000000 +0200
++++ dvdread/dvd_udf.c 2005-06-23 00:19:10.000000000 +0200
+@@ -33,7 +37,9 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
++#ifndef __MINGW32__
+ #include <sys/ioctl.h>
++#endif
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <unistd.h>
+diff -Naur dvdread.orig/ifo_print.c dvdread/ifo_print.c
+--- dvdread.orig/ifo_print.c 30 Jun 2005 22:48:26 -0000 1.4
++++ dvdread/ifo_print.c 3 Oct 2005 14:29:01 -0000 1.5
+@@ -761,14 +761,14 @@
+ ifoPrint_USER_OPS(&pgc->prohibited_ops);
+
+ for(i = 0; i < 8; i++) {
+- if(pgc->audio_control[i] & 0x8000) { /* The 'is present' bit */
++ if(pgc->audio_control[i].present) {
+ printf("Audio stream %i control: %04x\n",
+ i, pgc->audio_control[i]);
+ }
+ }
+
+ for(i = 0; i < 32; i++) {
+- if(pgc->subp_control[i] & 0x80000000) { /* The 'is present' bit */
++ if(pgc->subp_control[i].present) {
+ printf("Subpicture stream %2i control: %08x\n",
+ i, pgc->subp_control[i]);
+ }
+
+diff -Naur dvdread.orig/ifo_print.h dvdread/ifo_print.h
+--- dvdread.orig/ifo_print.h 2002-08-15 22:13:21.000000000 +0200
++++ dvdread/ifo_print.h 2005-12-29 13:13:21.000000000 +0100
+@@ -20,8 +24,8 @@
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+-#include <dvdread/ifo_types.h>
+-#include <dvdread/dvd_reader.h>
++#include "ifo_types.h"
++#include "dvd_reader.h"
+
+ #ifdef __cplusplus
+ extern "C" {
+diff -Naur dvdread.orig/ifo_read.c dvdread/ifo_read.c
+--- dvdread.orig/ifo_read.c 30 Jun 2005 22:48:26 -0000 1.4
++++ dvdread/ifo_read.c 3 Oct 2005 14:29:01 -0000 1.5
+@@ -97,7 +101,7 @@
+ ifo_handle_t *ifoOpen(dvd_reader_t *dvd, int title) {
+ ifo_handle_t *ifofile;
+
+- ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
++ ifofile = malloc(sizeof(ifo_handle_t));
+ if(!ifofile)
+ return 0;
+
+@@ -182,7 +186,7 @@
+ ifo_handle_t *ifoOpenVMGI(dvd_reader_t *dvd) {
+ ifo_handle_t *ifofile;
+
+- ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
++ ifofile = malloc(sizeof(ifo_handle_t));
+ if(!ifofile)
+ return 0;
+
+@@ -209,7 +213,7 @@
+ ifo_handle_t *ifoOpenVTSI(dvd_reader_t *dvd, int title) {
+ ifo_handle_t *ifofile;
+
+- ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
++ ifofile = malloc(sizeof(ifo_handle_t));
+ if(!ifofile)
+ return 0;
+
+@@ -274,7 +278,7 @@
+ static int ifoRead_VMG(ifo_handle_t *ifofile) {
+ vmgi_mat_t *vmgi_mat;
+
+- vmgi_mat = (vmgi_mat_t *)malloc(sizeof(vmgi_mat_t));
++ vmgi_mat = malloc(sizeof(vmgi_mat_t));
+ if(!vmgi_mat)
+ return 0;
+
+@@ -365,7 +369,7 @@
+ vtsi_mat_t *vtsi_mat;
+ int i;
+
+- vtsi_mat = (vtsi_mat_t *)malloc(sizeof(vtsi_mat_t));
++ vtsi_mat = malloc(sizeof(vtsi_mat_t));
+ if(!vtsi_mat)
+ return 0;
+
+@@ -493,7 +497,7 @@
+
+ if(cmd_tbl->nr_of_pre != 0) {
+ unsigned int pre_cmds_size = cmd_tbl->nr_of_pre * COMMAND_DATA_SIZE;
+- cmd_tbl->pre_cmds = (vm_cmd_t *)malloc(pre_cmds_size);
++ cmd_tbl->pre_cmds = malloc(pre_cmds_size);
+ if(!cmd_tbl->pre_cmds)
+ return 0;
+
+@@ -505,7 +509,7 @@
+
+ if(cmd_tbl->nr_of_post != 0) {
+ unsigned int post_cmds_size = cmd_tbl->nr_of_post * COMMAND_DATA_SIZE;
+- cmd_tbl->post_cmds = (vm_cmd_t *)malloc(post_cmds_size);
++ cmd_tbl->post_cmds = malloc(post_cmds_size);
+ if(!cmd_tbl->post_cmds) {
+ if(cmd_tbl->pre_cmds)
+ free(cmd_tbl->pre_cmds);
+@@ -521,7 +525,7 @@
+
+ if(cmd_tbl->nr_of_cell != 0) {
+ unsigned int cell_cmds_size = cmd_tbl->nr_of_cell * COMMAND_DATA_SIZE;
+- cmd_tbl->cell_cmds = (vm_cmd_t *)malloc(cell_cmds_size);
++ cmd_tbl->cell_cmds = malloc(cell_cmds_size);
+ if(!cmd_tbl->cell_cmds) {
+ if(cmd_tbl->pre_cmds)
+ free(cmd_tbl->pre_cmds);
+@@ -638,10 +642,6 @@
+ B2N_16(pgc->cell_playback_offset);
+ B2N_16(pgc->cell_position_offset);
+
+- for(i = 0; i < 8; i++)
+- B2N_16(pgc->audio_control[i]);
+- for(i = 0; i < 32; i++)
+- B2N_32(pgc->subp_control[i]);
+ for(i = 0; i < 16; i++)
+ B2N_32(pgc->palette[i]);
+
+@@ -650,10 +650,10 @@
+
+ /* verify time (look at print_time) */
+ for(i = 0; i < 8; i++)
+- if(!pgc->audio_control[i] & 0x8000) /* The 'is present' bit */
++ if(!pgc->audio_control[i].present)
+ CHECK_ZERO(pgc->audio_control[i]);
+ for(i = 0; i < 32; i++)
+- if(!pgc->subp_control[i] & 0x80000000) /* The 'is present' bit */
++ if(!pgc->subp_control[i].present)
+ CHECK_ZERO(pgc->subp_control[i]);
+
+ /* Check that time is 0:0:0:0 also if nr_of_programs == 0 */
+@@ -752,7 +752,7 @@
+ if(ifofile->vmgi_mat->first_play_pgc == 0)
+ return 1;
+
+- ifofile->first_play_pgc = (pgc_t *)malloc(sizeof(pgc_t));
++ ifofile->first_play_pgc = malloc(sizeof(pgc_t));
+ if(!ifofile->first_play_pgc)
+ return 0;
+
+@@ -806,7 +806,7 @@
+ if(!DVDFileSeek_(ifofile->file, ifofile->vmgi_mat->tt_srpt * DVD_BLOCK_LEN))
+ return 0;
+
+- tt_srpt = (tt_srpt_t *)malloc(sizeof(tt_srpt_t));
++ tt_srpt = malloc(sizeof(tt_srpt_t));
+ if(!tt_srpt)
+ return 0;
+
+@@ -823,7 +823,7 @@
+
+ info_length = tt_srpt->last_byte + 1 - TT_SRPT_SIZE;
+
+- tt_srpt->title = (title_info_t *)malloc(info_length);
++ tt_srpt->title = malloc(info_length);
+ if(!tt_srpt->title) {
+ free(tt_srpt);
+ ifofile->tt_srpt = 0;
+@@ -908,7 +908,7 @@
+ ifofile->vtsi_mat->vts_ptt_srpt * DVD_BLOCK_LEN))
+ return 0;
+
+- vts_ptt_srpt = (vts_ptt_srpt_t *)malloc(sizeof(vts_ptt_srpt_t));
++ vts_ptt_srpt = malloc(sizeof(vts_ptt_srpt_t));
+ if(!vts_ptt_srpt)
+ return 0;
+
+@@ -929,7 +929,7 @@
+
+ info_length = vts_ptt_srpt->last_byte + 1 - VTS_PTT_SRPT_SIZE;
+
+- data = (uint32_t *)malloc(info_length);
++ data = malloc(info_length);
+ if(!data) {
+ free(vts_ptt_srpt);
+ ifofile->vts_ptt_srpt = 0;
+@@ -1047,7 +1047,7 @@
+ if(!DVDFileSeek_(ifofile->file, ifofile->vmgi_mat->ptl_mait * DVD_BLOCK_LEN))
+ return 0;
+
+- ptl_mait = (ptl_mait_t *)malloc(sizeof(ptl_mait_t));
++ ptl_mait = malloc(sizeof(ptl_mait_t));
+ if(!ptl_mait)
+ return 0;
+
+@@ -1071,7 +1071,7 @@
+ <= ptl_mait->last_byte + 1 - PTL_MAIT_SIZE);
+
+ info_length = ptl_mait->nr_of_countries * sizeof(ptl_mait_country_t);
+- ptl_mait->countries = (ptl_mait_country_t *)malloc(info_length);
++ ptl_mait->countries = malloc(info_length);
+ if(!ptl_mait->countries) {
+ free(ptl_mait);
+ ifofile->ptl_mait = 0;
+@@ -1112,7 +1112,7 @@
+ return 0;
+ }
+ info_length = (ptl_mait->nr_of_vtss + 1) * sizeof(pf_level_t);
+- pf_temp = (uint16_t *)malloc(info_length);
++ pf_temp = malloc(info_length);
+ if(!pf_temp) {
+ for(j = 0; j < i ; j++) {
+ free(ptl_mait->countries[j].pf_ptl_mai);
+@@ -1134,7 +1134,7 @@
+ for (j = 0; j < ((ptl_mait->nr_of_vtss + 1) * 8); j++) {
+ B2N_16(pf_temp[j]);
+ }
+- ptl_mait->countries[i].pf_ptl_mai = (pf_level_t *)malloc(info_length);
++ ptl_mait->countries[i].pf_ptl_mai = malloc(info_length);
+ if(!ptl_mait->countries[i].pf_ptl_mai) {
+ free(pf_temp);
+ for(j = 0; j < i ; j++) {
+@@ -1198,7 +1198,7 @@
+ if(!DVDFileSeek_(ifofile->file, offset))
+ return 0;
+
+- vts_tmapt = (vts_tmapt_t *)malloc(sizeof(vts_tmapt_t));
++ vts_tmapt = malloc(sizeof(vts_tmapt_t));
+ if(!vts_tmapt)
+ return 0;
+
+@@ -1218,7 +1218,7 @@
+
+ info_length = vts_tmapt->nr_of_tmaps * 4;
+
+- vts_tmap_srp = (uint32_t *)malloc(info_length);
++ vts_tmap_srp = malloc(info_length);
+ if(!vts_tmap_srp) {
+ free(vts_tmapt);
+ ifofile->vts_tmapt = NULL;
+@@ -1242,7 +1242,7 @@
+
+ info_length = vts_tmapt->nr_of_tmaps * sizeof(vts_tmap_t);
+
+- vts_tmapt->tmap = (vts_tmap_t *)malloc(info_length);
++ vts_tmapt->tmap = malloc(info_length);
+ if(!vts_tmapt->tmap) {
+ free(vts_tmap_srp);
+ free(vts_tmapt);
+@@ -1274,7 +1274,7 @@
+
+ info_length = vts_tmapt->tmap[i].nr_of_entries * sizeof(map_ent_t);
+
+- vts_tmapt->tmap[i].map_ent = (map_ent_t *)malloc(info_length);
++ vts_tmapt->tmap[i].map_ent = malloc(info_length);
+ if(!vts_tmapt->tmap[i].map_ent) {
+ ifoFree_VTS_TMAPT(ifofile);
+ return 0;
+@@ -1322,7 +1322,7 @@
+ if(ifofile->vtsi_mat->vts_c_adt == 0) /* mandatory */
+ return 0;
+
+- ifofile->vts_c_adt = (c_adt_t *)malloc(sizeof(c_adt_t));
++ ifofile->vts_c_adt = malloc(sizeof(c_adt_t));
+ if(!ifofile->vts_c_adt)
+ return 0;
+
+@@ -1354,7 +1354,7 @@
+ return 0;
+ }
+
+- ifofile->menu_c_adt = (c_adt_t *)malloc(sizeof(c_adt_t));
++ ifofile->menu_c_adt = malloc(sizeof(c_adt_t));
+ if(!ifofile->menu_c_adt)
+ return 0;
+
+@@ -1396,7 +1396,7 @@
+ c_adt->nr_of_vobs = info_length / sizeof(cell_adr_t);
+ }
+
+- c_adt->cell_adr_table = (cell_adr_t *)malloc(info_length);
++ c_adt->cell_adr_table = malloc(info_length);
+ if(!c_adt->cell_adr_table)
+ return 0;
+
+@@ -1456,7 +1456,7 @@
+ if(ifofile->vtsi_mat->vts_vobu_admap == 0) /* mandatory */
+ return 0;
+
+- ifofile->vts_vobu_admap = (vobu_admap_t *)malloc(sizeof(vobu_admap_t));
++ ifofile->vts_vobu_admap = malloc(sizeof(vobu_admap_t));
+ if(!ifofile->vts_vobu_admap)
+ return 0;
+
+@@ -1488,7 +1488,7 @@
+ return 0;
+ }
+
+- ifofile->menu_vobu_admap = (vobu_admap_t *)malloc(sizeof(vobu_admap_t));
++ ifofile->menu_vobu_admap = malloc(sizeof(vobu_admap_t));
+ if(!ifofile->menu_vobu_admap)
+ return 0;
+
+@@ -1521,7 +1521,7 @@
+ Titles with a VOBS that has no VOBUs. */
+ CHECK_VALUE(info_length % sizeof(uint32_t) == 0);
+
+- vobu_admap->vobu_start_sectors = (uint32_t *)malloc(info_length);
++ vobu_admap->vobu_start_sectors = malloc(info_length);
+ if(!vobu_admap->vobu_start_sectors) {
+ return 0;
+ }
+@@ -1573,7 +1573,7 @@
+ if(ifofile->vtsi_mat->vts_pgcit == 0) /* mandatory */
+ return 0;
+
+- ifofile->vts_pgcit = (pgcit_t *)malloc(sizeof(pgcit_t));
++ ifofile->vts_pgcit = malloc(sizeof(pgcit_t));
+ if(!ifofile->vts_pgcit)
+ return 0;
+
+@@ -1703,7 +1703,7 @@
+ return 0;
+ }
+
+- ifofile->pgci_ut = (pgci_ut_t *)malloc(sizeof(pgci_ut_t));
++ ifofile->pgci_ut = malloc(sizeof(pgci_ut_t));
+ if(!ifofile->pgci_ut)
+ return 0;
+
+@@ -1893,7 +1893,7 @@
+ if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN))
+ return 0;
+
+- vts_atrt = (vts_atrt_t *)malloc(sizeof(vts_atrt_t));
++ vts_atrt = malloc(sizeof(vts_atrt_t));
+ if(!vts_atrt)
+ return 0;
+
+@@ -1915,7 +1915,7 @@
+ VTS_ATRT_SIZE < vts_atrt->last_byte + 1);
+
+ info_length = vts_atrt->nr_of_vtss * sizeof(uint32_t);
+- data = (uint32_t *)malloc(info_length);
++ data = malloc(info_length);
+ if(!data) {
+ free(vts_atrt);
+ ifofile->vts_atrt = 0;
+@@ -1937,7 +1937,7 @@
+ }
+
+ info_length = vts_atrt->nr_of_vtss * sizeof(vts_attributes_t);
+- vts_atrt->vts = (vts_attributes_t *)malloc(info_length);
++ vts_atrt->vts = malloc(info_length);
+ if(!vts_atrt->vts) {
+ free(data);
+ free(vts_atrt);
+@@ -1993,7 +1993,7 @@
+ ifofile->vmgi_mat->txtdt_mgi * DVD_BLOCK_LEN))
+ return 0;
+
+- txtdt_mgi = (txtdt_mgi_t *)malloc(sizeof(txtdt_mgi_t));
++ txtdt_mgi = malloc(sizeof(txtdt_mgi_t));
+ if(!txtdt_mgi) {
+ return 0;
+ }
+
+diff -Naur dvdread.orig/ifo_read.h dvdread/ifo_read.h
+--- dvdread.orig/ifo_read.h 2005-06-23 00:18:54.000000000 +0200
++++ dvdread/ifo_read.h 2005-06-23 00:19:10.000000000 +0200
+@@ -20,8 +24,8 @@
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+-#include <dvdread/ifo_types.h>
+-#include <dvdread/dvd_reader.h>
++#include "ifo_types.h"
++#include "dvd_reader.h"
+
+ #ifdef __cplusplus
+ extern "C" {
+diff -Naur dvdread.orig/ifo_types.h dvdread/ifo_types.h
+--- dvdread.orig/ifo_types.h 2005-06-23 00:18:54.000000000 +0200
++++ dvdread/ifo_types.h 2005-06-23 00:19:10.000000000 +0200
+@@ -21,7 +25,7 @@
+ */
+
+ #include <inttypes.h>
+-#include <dvdread/dvd_reader.h>
++#include "dvd_reader.h"
+
+
+ #undef ATTRIBUTE_PACKED
+@@ -394,6 +398,55 @@
+ } ATTRIBUTE_PACKED user_ops_t;
+
+ /**
++ * Subpicture stream mapping for a subtitle
++ */
++typedef struct {
++#ifdef WORDS_BIGENDIAN
++ unsigned int present : 1;
++ unsigned int zero1 : 2;
++ unsigned int s_4p3 : 5; /* stream for 4:3 on any display */
++
++ unsigned int zero2 : 3;
++ unsigned int s_wide : 5; /* stream for 16:9 on widescreen display */
++
++ unsigned int zero3 : 3;
++ unsigned int s_lbox : 5; /* stream for 16:9 on letterboxed 4:3 display */
++
++ unsigned int zero4 : 3;
++ unsigned int s_panscan : 5; /* stream for 16:9 with pan&scan data on 4:3 display */
++#else
++ unsigned int s_4p3 : 5; /* stream for 4:3 on any display */
++ unsigned int zero1 : 2;
++ unsigned int present : 1;
++
++ unsigned int s_wide : 5; /* stream for 16:9 on widescreen display */
++ unsigned int zero2 : 3;
++
++ unsigned int s_lbox : 5; /* stream for 16:9 on letterboxed 4:3 display */
++ unsigned int zero3 : 3;
++
++ unsigned int s_panscan : 5; /* stream for 16:9 with pan&scan data on 4:3 display */
++ unsigned int zero4 : 3;
++#endif
++} ATTRIBUTE_PACKED subp_mapping_t;
++
++/**
++ * Audio stream mapping for a soundtrack
++ */
++typedef struct {
++#ifdef WORDS_BIGENDIAN
++ unsigned int present : 1;
++ unsigned int zero1 : 4;
++ unsigned int s_audio : 3;
++#else
++ unsigned int s_audio : 3;
++ unsigned int zero1 : 4;
++ unsigned int present : 1;
++#endif
++ uint8_t zero2;
++} ATTRIBUTE_PACKED audio_mapping_t;
++
++/**
+ * Program Chain Information.
+ */
+ typedef struct {
+@@ -402,8 +455,8 @@
+ uint8_t nr_of_cells;
+ dvd_time_t playback_time;
+ user_ops_t prohibited_ops;
+- uint16_t audio_control[8]; /* New type? */
+- uint32_t subp_control[32]; /* New type? */
++ audio_mapping_t audio_control[8];
++ subp_mapping_t subp_control[32];
+ uint16_t next_pgc_nr;
+ uint16_t prev_pgc_nr;
+ uint16_t goup_pgc_nr;
+diff -Naur dvdread.orig/nav_print.c dvdread/nav_print.c
+diff -Naur dvdread.orig/nav_print.h dvdread/nav_print.h
+--- dvdread.orig/nav_print.h 2005-06-23 00:18:54.000000000 +0200
++++ dvdread/nav_print.h 2005-06-23 00:19:10.000000000 +0200
+@@ -20,7 +24,7 @@
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+-#include <dvdread/nav_types.h>
++#include "nav_types.h"
+
+ /**
+ * Pretty printing of the NAV packets, PCI and DSI structs.
+diff -Naur dvdread.orig/nav_read.c dvdread/nav_read.c
+diff -Naur dvdread.orig/nav_read.h dvdread/nav_read.h
+--- dvdread.orig/nav_read.h 2005-06-23 00:18:54.000000000 +0200
++++ dvdread/nav_read.h 2005-06-23 00:19:10.000000000 +0200
+@@ -19,7 +23,7 @@
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+-#include <dvdread/nav_types.h>
++#include "nav_types.h"
+
+ /**
+ * Parsing of NAV data, PCI and DSI parts.
+diff -Naur dvdread.orig/nav_types.h dvdread/nav_types.h
+--- dvdread.orig/nav_types.h 2005-06-23 00:18:54.000000000 +0200
++++ dvdread/nav_types.h 2005-06-23 00:19:10.000000000 +0200
+@@ -30,7 +34,7 @@
+ */
+
+ #include <inttypes.h>
+-#include <dvdread/ifo_types.h> /* only dvd_time_t, vm_cmd_t and user_ops_t */
++#include "ifo_types.h" /* only dvd_time_t, vm_cmd_t and user_ops_t */
+
+
+ #undef ATTRIBUTE_PACKED
diff --git a/dvdread/md5.c b/dvdread/md5.c
new file mode 100644
index 0000000000..d4fd7d5e20
--- /dev/null
+++ b/dvdread/md5.c
@@ -0,0 +1,417 @@
+/* md5.c - Functions to compute MD5 message digest of files or memory blocks
+ according to the definition of MD5 in RFC 1321 from April 1992.
+ Copyright (C) 1995, 1996, 2001 Free Software Foundation, Inc.
+ NOTE: The canonical source of this file is maintained with the GNU C
+ Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+
+#if STDC_HEADERS || defined _LIBC
+# include <stdlib.h>
+# include <string.h>
+#else
+# ifndef HAVE_MEMCPY
+# define memcpy(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#include "md5.h"
+//#include "unlocked-io.h"
+
+#ifdef _LIBC
+# include <endian.h>
+# if __BYTE_ORDER == __BIG_ENDIAN
+# define WORDS_BIGENDIAN 1
+# endif
+#endif
+
+#ifdef WORDS_BIGENDIAN
+# define SWAP(n) \
+ (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+#else
+# define SWAP(n) (n)
+#endif
+
+
+/* This array contains the bytes used to pad the buffer to the next
+ 64-byte boundary. (RFC 1321, 3.1: Step 1) */
+static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ };
+
+
+/* Initialize structure containing state of computation.
+ (RFC 1321, 3.3: Step 3) */
+void
+md5_init_ctx (ctx)
+ struct md5_ctx *ctx;
+{
+ ctx->A = 0x67452301;
+ ctx->B = 0xefcdab89;
+ ctx->C = 0x98badcfe;
+ ctx->D = 0x10325476;
+
+ ctx->total[0] = ctx->total[1] = 0;
+ ctx->buflen = 0;
+}
+
+/* Put result from CTX in first 16 bytes following RESBUF. The result
+ must be in little endian byte order.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+void *
+md5_read_ctx (ctx, resbuf)
+ const struct md5_ctx *ctx;
+ void *resbuf;
+{
+ ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A);
+ ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B);
+ ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C);
+ ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D);
+
+ return resbuf;
+}
+
+/* Process the remaining bytes in the internal buffer and the usual
+ prolog according to the standard and write the result to RESBUF.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+void *
+md5_finish_ctx (ctx, resbuf)
+ struct md5_ctx *ctx;
+ void *resbuf;
+{
+ /* Take yet unprocessed bytes into account. */
+ md5_uint32 bytes = ctx->buflen;
+ size_t pad;
+
+ /* Now count remaining bytes. */
+ ctx->total[0] += bytes;
+ if (ctx->total[0] < bytes)
+ ++ctx->total[1];
+
+ pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
+ memcpy (&ctx->buffer[bytes], fillbuf, pad);
+
+ /* Put the 64-bit file length in *bits* at the end of the buffer. */
+ *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3);
+ *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) |
+ (ctx->total[0] >> 29));
+
+ /* Process last bytes. */
+ md5_process_block (ctx->buffer, bytes + pad + 8, ctx);
+
+ return md5_read_ctx (ctx, resbuf);
+}
+
+/* Compute MD5 message digest for bytes read from STREAM. The
+ resulting message digest number will be written into the 16 bytes
+ beginning at RESBLOCK. */
+int
+md5_stream (stream, resblock)
+ FILE *stream;
+ void *resblock;
+{
+ /* Important: BLOCKSIZE must be a multiple of 64. */
+#define BLOCKSIZE 4096
+ struct md5_ctx ctx;
+ char buffer[BLOCKSIZE + 72];
+ size_t sum;
+
+ /* Initialize the computation context. */
+ md5_init_ctx (&ctx);
+
+ /* Iterate over full file contents. */
+ while (1)
+ {
+ /* We read the file in blocks of BLOCKSIZE bytes. One call of the
+ computation function processes the whole buffer so that with the
+ next round of the loop another block can be read. */
+ size_t n;
+ sum = 0;
+
+ /* Read block. Take care for partial reads. */
+ do
+ {
+ n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);
+
+ sum += n;
+ }
+ while (sum < BLOCKSIZE && n != 0);
+ if (n == 0 && ferror (stream))
+ return 1;
+
+ /* If end of file is reached, end the loop. */
+ if (n == 0)
+ break;
+
+ /* Process buffer with BLOCKSIZE bytes. Note that
+ BLOCKSIZE % 64 == 0
+ */
+ md5_process_block (buffer, BLOCKSIZE, &ctx);
+ }
+
+ /* Add the last bytes if necessary. */
+ if (sum > 0)
+ md5_process_bytes (buffer, sum, &ctx);
+
+ /* Construct result in desired memory. */
+ md5_finish_ctx (&ctx, resblock);
+ return 0;
+}
+
+/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The
+ result is always in little endian byte order, so that a byte-wise
+ output yields to the wanted ASCII representation of the message
+ digest. */
+void *
+md5_buffer (buffer, len, resblock)
+ const char *buffer;
+ size_t len;
+ void *resblock;
+{
+ struct md5_ctx ctx;
+
+ /* Initialize the computation context. */
+ md5_init_ctx (&ctx);
+
+ /* Process whole buffer but last len % 64 bytes. */
+ md5_process_bytes (buffer, len, &ctx);
+
+ /* Put result in desired memory area. */
+ return md5_finish_ctx (&ctx, resblock);
+}
+
+
+void
+md5_process_bytes (buffer, len, ctx)
+ const void *buffer;
+ size_t len;
+ struct md5_ctx *ctx;
+{
+ /* When we already have some bits in our internal buffer concatenate
+ both inputs first. */
+ if (ctx->buflen != 0)
+ {
+ size_t left_over = ctx->buflen;
+ size_t add = 128 - left_over > len ? len : 128 - left_over;
+
+ memcpy (&ctx->buffer[left_over], buffer, add);
+ ctx->buflen += add;
+
+ if (left_over + add > 64)
+ {
+ md5_process_block (ctx->buffer, (left_over + add) & ~63, ctx);
+ /* The regions in the following copy operation cannot overlap. */
+ memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
+ (left_over + add) & 63);
+ ctx->buflen = (left_over + add) & 63;
+ }
+
+ buffer = (const char *) buffer + add;
+ len -= add;
+ }
+
+ /* Process available complete blocks. */
+ if (len > 64)
+ {
+ md5_process_block (buffer, len & ~63, ctx);
+ buffer = (const char *) buffer + (len & ~63);
+ len &= 63;
+ }
+
+ /* Move remaining bytes in internal buffer. */
+ if (len > 0)
+ {
+ memcpy (ctx->buffer, buffer, len);
+ ctx->buflen = len;
+ }
+}
+
+
+/* These are the four functions used in the four steps of the MD5 algorithm
+ and defined in the RFC 1321. The first function is a little bit optimized
+ (as found in Colin Plumbs public domain implementation). */
+/* #define FF(b, c, d) ((b & c) | (~b & d)) */
+#define FF(b, c, d) (d ^ (b & (c ^ d)))
+#define FG(b, c, d) FF (d, b, c)
+#define FH(b, c, d) (b ^ c ^ d)
+#define FI(b, c, d) (c ^ (b | ~d))
+
+/* Process LEN bytes of BUFFER, accumulating context into CTX.
+ It is assumed that LEN % 64 == 0. */
+
+void
+md5_process_block (buffer, len, ctx)
+ const void *buffer;
+ size_t len;
+ struct md5_ctx *ctx;
+{
+ md5_uint32 correct_words[16];
+ const md5_uint32 *words = buffer;
+ size_t nwords = len / sizeof (md5_uint32);
+ const md5_uint32 *endp = words + nwords;
+ md5_uint32 A = ctx->A;
+ md5_uint32 B = ctx->B;
+ md5_uint32 C = ctx->C;
+ md5_uint32 D = ctx->D;
+
+ /* First increment the byte count. RFC 1321 specifies the possible
+ length of the file up to 2^64 bits. Here we only compute the
+ number of bytes. Do a double word increment. */
+ ctx->total[0] += len;
+ if (ctx->total[0] < len)
+ ++ctx->total[1];
+
+ /* Process all bytes in the buffer with 64 bytes in each round of
+ the loop. */
+ while (words < endp)
+ {
+ md5_uint32 *cwp = correct_words;
+ md5_uint32 A_save = A;
+ md5_uint32 B_save = B;
+ md5_uint32 C_save = C;
+ md5_uint32 D_save = D;
+
+ /* First round: using the given function, the context and a constant
+ the next context is computed. Because the algorithms processing
+ unit is a 32-bit word and it is determined to work on words in
+ little endian byte order we perhaps have to change the byte order
+ before the computation. To reduce the work for the next steps
+ we store the swapped words in the array CORRECT_WORDS. */
+
+#define OP(a, b, c, d, s, T) \
+ do \
+ { \
+ a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \
+ ++words; \
+ a = rol (a, s); \
+ a += b; \
+ } \
+ while (0)
+
+ /* Before we start, one word to the strange constants.
+ They are defined in RFC 1321 as
+
+ T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64, or
+ perl -e 'foreach(1..64){printf "0x%08x\n", int (4294967296 * abs (sin $_))}'
+ */
+
+ /* Round 1. */
+ OP (A, B, C, D, 7, 0xd76aa478);
+ OP (D, A, B, C, 12, 0xe8c7b756);
+ OP (C, D, A, B, 17, 0x242070db);
+ OP (B, C, D, A, 22, 0xc1bdceee);
+ OP (A, B, C, D, 7, 0xf57c0faf);
+ OP (D, A, B, C, 12, 0x4787c62a);
+ OP (C, D, A, B, 17, 0xa8304613);
+ OP (B, C, D, A, 22, 0xfd469501);
+ OP (A, B, C, D, 7, 0x698098d8);
+ OP (D, A, B, C, 12, 0x8b44f7af);
+ OP (C, D, A, B, 17, 0xffff5bb1);
+ OP (B, C, D, A, 22, 0x895cd7be);
+ OP (A, B, C, D, 7, 0x6b901122);
+ OP (D, A, B, C, 12, 0xfd987193);
+ OP (C, D, A, B, 17, 0xa679438e);
+ OP (B, C, D, A, 22, 0x49b40821);
+
+ /* For the second to fourth round we have the possibly swapped words
+ in CORRECT_WORDS. Redefine the macro to take an additional first
+ argument specifying the function to use. */
+#undef OP
+#define OP(f, a, b, c, d, k, s, T) \
+ do \
+ { \
+ a += f (b, c, d) + correct_words[k] + T; \
+ a = rol (a, s); \
+ a += b; \
+ } \
+ while (0)
+
+ /* Round 2. */
+ OP (FG, A, B, C, D, 1, 5, 0xf61e2562);
+ OP (FG, D, A, B, C, 6, 9, 0xc040b340);
+ OP (FG, C, D, A, B, 11, 14, 0x265e5a51);
+ OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
+ OP (FG, A, B, C, D, 5, 5, 0xd62f105d);
+ OP (FG, D, A, B, C, 10, 9, 0x02441453);
+ OP (FG, C, D, A, B, 15, 14, 0xd8a1e681);
+ OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
+ OP (FG, A, B, C, D, 9, 5, 0x21e1cde6);
+ OP (FG, D, A, B, C, 14, 9, 0xc33707d6);
+ OP (FG, C, D, A, B, 3, 14, 0xf4d50d87);
+ OP (FG, B, C, D, A, 8, 20, 0x455a14ed);
+ OP (FG, A, B, C, D, 13, 5, 0xa9e3e905);
+ OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8);
+ OP (FG, C, D, A, B, 7, 14, 0x676f02d9);
+ OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
+
+ /* Round 3. */
+ OP (FH, A, B, C, D, 5, 4, 0xfffa3942);
+ OP (FH, D, A, B, C, 8, 11, 0x8771f681);
+ OP (FH, C, D, A, B, 11, 16, 0x6d9d6122);
+ OP (FH, B, C, D, A, 14, 23, 0xfde5380c);
+ OP (FH, A, B, C, D, 1, 4, 0xa4beea44);
+ OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9);
+ OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60);
+ OP (FH, B, C, D, A, 10, 23, 0xbebfbc70);
+ OP (FH, A, B, C, D, 13, 4, 0x289b7ec6);
+ OP (FH, D, A, B, C, 0, 11, 0xeaa127fa);
+ OP (FH, C, D, A, B, 3, 16, 0xd4ef3085);
+ OP (FH, B, C, D, A, 6, 23, 0x04881d05);
+ OP (FH, A, B, C, D, 9, 4, 0xd9d4d039);
+ OP (FH, D, A, B, C, 12, 11, 0xe6db99e5);
+ OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8);
+ OP (FH, B, C, D, A, 2, 23, 0xc4ac5665);
+
+ /* Round 4. */
+ OP (FI, A, B, C, D, 0, 6, 0xf4292244);
+ OP (FI, D, A, B, C, 7, 10, 0x432aff97);
+ OP (FI, C, D, A, B, 14, 15, 0xab9423a7);
+ OP (FI, B, C, D, A, 5, 21, 0xfc93a039);
+ OP (FI, A, B, C, D, 12, 6, 0x655b59c3);
+ OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92);
+ OP (FI, C, D, A, B, 10, 15, 0xffeff47d);
+ OP (FI, B, C, D, A, 1, 21, 0x85845dd1);
+ OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f);
+ OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
+ OP (FI, C, D, A, B, 6, 15, 0xa3014314);
+ OP (FI, B, C, D, A, 13, 21, 0x4e0811a1);
+ OP (FI, A, B, C, D, 4, 6, 0xf7537e82);
+ OP (FI, D, A, B, C, 11, 10, 0xbd3af235);
+ OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
+ OP (FI, B, C, D, A, 9, 21, 0xeb86d391);
+
+ /* Add the starting values of the context. */
+ A += A_save;
+ B += B_save;
+ C += C_save;
+ D += D_save;
+ }
+
+ /* Put checksum in context given as argument. */
+ ctx->A = A;
+ ctx->B = B;
+ ctx->C = C;
+ ctx->D = D;
+}
diff --git a/dvdread/md5.h b/dvdread/md5.h
new file mode 100644
index 0000000000..3b7dea1699
--- /dev/null
+++ b/dvdread/md5.h
@@ -0,0 +1,161 @@
+/* md5.h - Declaration of functions and data types used for MD5 sum
+ computing library functions.
+ Copyright (C) 1995, 1996, 1999 Free Software Foundation, Inc.
+ NOTE: The canonical source of this file is maintained with the GNU C
+ Library. Bugs can be reported to bug-glibc@prep.ai.mit.edu.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef _MD5_H
+#define _MD5_H 1
+
+#include <stdio.h>
+
+#if defined HAVE_LIMITS_H || _LIBC
+# include <limits.h>
+#endif
+
+/* The following contortions are an attempt to use the C preprocessor
+ to determine an unsigned integral type that is 32 bits wide. An
+ alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but
+ doing that would require that the configure script compile and *run*
+ the resulting executable. Locally running cross-compiled executables
+ is usually not possible. */
+
+#ifdef _LIBC
+# include <sys/types.h>
+typedef u_int32_t md5_uint32;
+#else
+# if defined __STDC__ && __STDC__
+# define UINT_MAX_32_BITS 4294967295U
+# else
+# define UINT_MAX_32_BITS 0xFFFFFFFF
+# endif
+
+/* If UINT_MAX isn't defined, assume it's a 32-bit type.
+ This should be valid for all systems GNU cares about because
+ that doesn't include 16-bit systems, and only modern systems
+ (that certainly have <limits.h>) have 64+-bit integral types. */
+
+# ifndef UINT_MAX
+# define UINT_MAX UINT_MAX_32_BITS
+# endif
+
+# if UINT_MAX == UINT_MAX_32_BITS
+ typedef unsigned int md5_uint32;
+# else
+# if USHRT_MAX == UINT_MAX_32_BITS
+ typedef unsigned short md5_uint32;
+# else
+# if ULONG_MAX == UINT_MAX_32_BITS
+ typedef unsigned long md5_uint32;
+# else
+ /* The following line is intended to evoke an error.
+ Using #error is not portable enough. */
+ "Cannot determine unsigned 32-bit data type."
+# endif
+# endif
+# endif
+#endif
+
+#undef __P
+#if defined (__STDC__) && __STDC__
+#define __P(x) x
+#else
+#define __P(x) ()
+#endif
+
+/* Structure to save state of computation between the single steps. */
+struct md5_ctx
+{
+ md5_uint32 A;
+ md5_uint32 B;
+ md5_uint32 C;
+ md5_uint32 D;
+
+ md5_uint32 total[2];
+ md5_uint32 buflen;
+ char buffer[128];
+};
+
+/*
+ * The following three functions are build up the low level used in
+ * the functions `md5_stream' and `md5_buffer'.
+ */
+
+/* Initialize structure containing state of computation.
+ (RFC 1321, 3.3: Step 3) */
+extern void md5_init_ctx __P ((struct md5_ctx *ctx));
+
+/* Starting with the result of former calls of this function (or the
+ initialization function update the context for the next LEN bytes
+ starting at BUFFER.
+ It is necessary that LEN is a multiple of 64!!! */
+extern void md5_process_block __P ((const void *buffer, size_t len,
+ struct md5_ctx *ctx));
+
+/* Starting with the result of former calls of this function (or the
+ initialization function update the context for the next LEN bytes
+ starting at BUFFER.
+ It is NOT required that LEN is a multiple of 64. */
+extern void md5_process_bytes __P ((const void *buffer, size_t len,
+ struct md5_ctx *ctx));
+
+/* Process the remaining bytes in the buffer and put result from CTX
+ in first 16 bytes following RESBUF. The result is always in little
+ endian byte order, so that a byte-wise output yields to the wanted
+ ASCII representation of the message digest.
+
+ IMPORTANT: On some systems it is required that RESBUF be correctly
+ aligned for a 32 bits value. */
+extern void *md5_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf));
+
+
+/* Put result from CTX in first 16 bytes following RESBUF. The result is
+ always in little endian byte order, so that a byte-wise output yields
+ to the wanted ASCII representation of the message digest.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32 bits value. */
+extern void *md5_read_ctx __P ((const struct md5_ctx *ctx, void *resbuf));
+
+
+/* Compute MD5 message digest for bytes read from STREAM. The
+ resulting message digest number will be written into the 16 bytes
+ beginning at RESBLOCK. */
+extern int md5_stream __P ((FILE *stream, void *resblock));
+
+/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The
+ result is always in little endian byte order, so that a byte-wise
+ output yields to the wanted ASCII representation of the message
+ digest. */
+extern void *md5_buffer __P ((const char *buffer, size_t len, void *resblock));
+
+/* The following is from gnupg-1.0.2's cipher/bithelp.h. */
+/* Rotate a 32 bit integer by n bytes */
+#if defined __GNUC__ && defined __i386__
+static inline md5_uint32
+rol(md5_uint32 x, int n)
+{
+ __asm__("roll %%cl,%0"
+ :"=r" (x)
+ :"0" (x),"c" (n));
+ return x;
+}
+#else
+# define rol(x,n) ( ((x) << (n)) | ((x) >> (32-(n))) )
+#endif
+
+#endif
diff --git a/dvdread/nav_print.c b/dvdread/nav_print.c
new file mode 100644
index 0000000000..6df3dc8310
--- /dev/null
+++ b/dvdread/nav_print.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2000, 2001, 2002, 2003 Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * Much of the contents in this file is based on VOBDUMP.
+ *
+ * VOBDUMP: a program for examining DVD .VOB filse
+ *
+ * Copyright 1998, 1999 Eric Smith <eric@brouhaha.com>
+ *
+ * VOBDUMP is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. Note that I am not
+ * granting permission to redistribute or modify VOBDUMP under the
+ * terms of any later version of the General Public License.
+ *
+ * This program is distributed in the hope that it will be useful (or
+ * at least amusing), but WITHOUT ANY WARRANTY; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <inttypes.h>
+
+#include "nav_types.h"
+#include "nav_print.h"
+#include "dvdread_internal.h"
+
+static void print_time(dvd_time_t *dtime) {
+ const char *rate;
+ CHECK_VALUE((dtime->hour>>4) < 0xa && (dtime->hour&0xf) < 0xa);
+ CHECK_VALUE((dtime->minute>>4) < 0x7 && (dtime->minute&0xf) < 0xa);
+ CHECK_VALUE((dtime->second>>4) < 0x7 && (dtime->second&0xf) < 0xa);
+ CHECK_VALUE((dtime->frame_u&0xf) < 0xa);
+
+ printf("%02x:%02x:%02x.%02x",
+ dtime->hour,
+ dtime->minute,
+ dtime->second,
+ dtime->frame_u & 0x3f);
+ switch((dtime->frame_u & 0xc0) >> 6) {
+ case 1:
+ rate = "25.00";
+ break;
+ case 3:
+ rate = "29.97";
+ break;
+ default:
+ rate = "(please send a bug report)";
+ break;
+ }
+ printf(" @ %s fps", rate);
+}
+
+
+static void navPrint_PCI_GI(pci_gi_t *pci_gi) {
+ int i;
+
+ printf("pci_gi:\n");
+ printf("nv_pck_lbn 0x%08x\n", pci_gi->nv_pck_lbn);
+ printf("vobu_cat 0x%04x\n", pci_gi->vobu_cat);
+ printf("vobu_uop_ctl 0x%08x\n", *(uint32_t*)&pci_gi->vobu_uop_ctl);
+ printf("vobu_s_ptm 0x%08x\n", pci_gi->vobu_s_ptm);
+ printf("vobu_e_ptm 0x%08x\n", pci_gi->vobu_e_ptm);
+ printf("vobu_se_e_ptm 0x%08x\n", pci_gi->vobu_se_e_ptm);
+ printf("e_eltm ");
+ print_time(&pci_gi->e_eltm);
+ printf("\n");
+
+ printf("vobu_isrc \"");
+ for(i = 0; i < 32; i++) {
+ char c = pci_gi->vobu_isrc[i];
+ if((c >= ' ') && (c <= '~'))
+ printf("%c", c);
+ else
+ printf(".");
+ }
+ printf("\"\n");
+}
+
+static void navPrint_NSML_AGLI(nsml_agli_t *nsml_agli) {
+ int i, j = 0;
+
+ for(i = 0; i < 9; i++)
+ j |= nsml_agli->nsml_agl_dsta[i];
+ if(j == 0)
+ return;
+
+ printf("nsml_agli:\n");
+ for(i = 0; i < 9; i++)
+ if(nsml_agli->nsml_agl_dsta[i])
+ printf("nsml_agl_c%d_dsta 0x%08x\n", i + 1,
+ nsml_agli->nsml_agl_dsta[i]);
+}
+
+static void navPrint_HL_GI(hl_gi_t *hl_gi, int *btngr_ns, int *btn_ns) {
+
+ if((hl_gi->hli_ss & 0x03) == 0)
+ return;
+
+ printf("hl_gi:\n");
+ printf("hli_ss 0x%01x\n", hl_gi->hli_ss & 0x03);
+ printf("hli_s_ptm 0x%08x\n", hl_gi->hli_s_ptm);
+ printf("hli_e_ptm 0x%08x\n", hl_gi->hli_e_ptm);
+ printf("btn_se_e_ptm 0x%08x\n", hl_gi->btn_se_e_ptm);
+
+ *btngr_ns = hl_gi->btngr_ns;
+ printf("btngr_ns %d\n", hl_gi->btngr_ns);
+ printf("btngr%d_dsp_ty 0x%02x\n", 1, hl_gi->btngr1_dsp_ty);
+ printf("btngr%d_dsp_ty 0x%02x\n", 2, hl_gi->btngr2_dsp_ty);
+ printf("btngr%d_dsp_ty 0x%02x\n", 3, hl_gi->btngr3_dsp_ty);
+
+ printf("btn_ofn %d\n", hl_gi->btn_ofn);
+ *btn_ns = hl_gi->btn_ns;
+ printf("btn_ns %d\n", hl_gi->btn_ns);
+ printf("nsl_btn_ns %d\n", hl_gi->nsl_btn_ns);
+ printf("fosl_btnn %d\n", hl_gi->fosl_btnn);
+ printf("foac_btnn %d\n", hl_gi->foac_btnn);
+}
+
+static void navPrint_BTN_COLIT(btn_colit_t *btn_colit) {
+ int i, j;
+
+ j = 0;
+ for(i = 0; i < 6; i++)
+ j |= btn_colit->btn_coli[i/2][i&1];
+ if(j == 0)
+ return;
+
+ printf("btn_colit:\n");
+ for(i = 0; i < 3; i++)
+ for(j = 0; j < 2; j++)
+ printf("btn_cqoli %d %s_coli: %08x\n",
+ i, (j == 0) ? "sl" : "ac",
+ btn_colit->btn_coli[i][j]);
+}
+
+static void navPrint_BTNIT(btni_t *btni_table, int btngr_ns, int btn_ns) {
+ int i, j;
+
+ printf("btnit:\n");
+ printf("btngr_ns: %i\n", btngr_ns);
+ printf("btn_ns: %i\n", btn_ns);
+
+ if(btngr_ns == 0)
+ return;
+
+ for(i = 0; i < btngr_ns; i++) {
+ for(j = 0; j < (36 / btngr_ns); j++) {
+ if(j < btn_ns) {
+ btni_t *btni = &btni_table[(36 / btngr_ns) * i + j];
+
+ printf("group %d btni %d: ", i+1, j+1);
+ printf("btn_coln %d, auto_action_mode %d\n",
+ btni->btn_coln, btni->auto_action_mode);
+ printf("coords (%d, %d) .. (%d, %d)\n",
+ btni->x_start, btni->y_start, btni->x_end, btni->y_end);
+
+ printf("up %d, ", btni->up);
+ printf("down %d, ", btni->down);
+ printf("left %d, ", btni->left);
+ printf("right %d\n", btni->right);
+
+ // 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/dvdread/nav_print.h b/dvdread/nav_print.h
new file mode 100644
index 0000000000..3a3354277f
--- /dev/null
+++ b/dvdread/nav_print.h
@@ -0,0 +1,54 @@
+#ifndef NAV_PRINT_H_INCLUDED
+#define NAV_PRINT_H_INCLUDED
+
+/*
+ * Copyright (C) 2001, 2002 Billy Biggs <vektor@dumbterm.net>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
+ * detailed changelog at http://svn.mplayerhq.hu/mplayer/trunk/
+ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "nav_types.h"
+
+/**
+ * Pretty printing of the NAV packets, PCI and DSI structs.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Prints information contained in the PCI to stdout.
+ *
+ * @param pci Pointer to the PCI data structure to be printed.
+ */
+void navPrint_PCI(pci_t *);
+
+/**
+ * Prints information contained in the DSI to stdout.
+ *
+ * @param dsi Pointer to the DSI data structure to be printed.
+ */
+void navPrint_DSI(dsi_t *);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* NAV_PRINT_H_INCLUDED */
diff --git a/dvdread/nav_read.c b/dvdread/nav_read.c
new file mode 100644
index 0000000000..1a907f9dfe
--- /dev/null
+++ b/dvdread/nav_read.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2000, 2001, 2002, 2003 Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "bswap.h"
+#include "nav_types.h"
+#include "nav_read.h"
+#include "dvdread_internal.h"
+
+void navRead_PCI(pci_t *pci, unsigned char *buffer) {
+ int i, j;
+
+ CHECK_VALUE(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]);
+
+ /* NOTE: I've had to change the structure from the disk layout to get
+ * the packing to work with Sun's Forte C compiler. */
+
+ /* pci hli btni */
+ for(i = 0; i < 36; i++) {
+ char tmp[sizeof(pci->hli.btnit[i])], swap;
+ memcpy(tmp, &(pci->hli.btnit[i]), sizeof(pci->hli.btnit[i]));
+ /* Byte 4 to 7 are 'rotated' was: ABCD EFGH IJ is: ABCG DEFH IJ */
+ swap = tmp[6];
+ tmp[6] = tmp[5];
+ tmp[5] = tmp[4];
+ tmp[4] = tmp[3];
+ tmp[3] = swap;
+
+ /* Then there are the two B2N_24(..) calls */
+#ifndef WORDS_BIGENDIAN
+ swap = tmp[0];
+ tmp[0] = tmp[2];
+ tmp[2] = swap;
+
+ swap = tmp[4];
+ tmp[4] = tmp[6];
+ tmp[6] = swap;
+#endif
+ memcpy(&(pci->hli.btnit[i]), tmp, sizeof(pci->hli.btnit[i]));
+ }
+
+
+#ifndef NDEBUG
+ /* Asserts */
+
+ /* pci pci gi */
+ CHECK_VALUE(pci->pci_gi.zero1 == 0);
+
+ /* pci hli hli_gi */
+ CHECK_VALUE(pci->hli.hl_gi.zero1 == 0);
+ CHECK_VALUE(pci->hli.hl_gi.zero2 == 0);
+ CHECK_VALUE(pci->hli.hl_gi.zero3 == 0);
+ CHECK_VALUE(pci->hli.hl_gi.zero4 == 0);
+ CHECK_VALUE(pci->hli.hl_gi.zero5 == 0);
+
+ /* Are there buttons defined here? */
+ if((pci->hli.hl_gi.hli_ss & 0x03) != 0) {
+ CHECK_VALUE(pci->hli.hl_gi.btn_ns != 0);
+ CHECK_VALUE(pci->hli.hl_gi.btngr_ns != 0);
+ } else {
+ CHECK_VALUE((pci->hli.hl_gi.btn_ns != 0 && pci->hli.hl_gi.btngr_ns != 0)
+ || (pci->hli.hl_gi.btn_ns == 0 && pci->hli.hl_gi.btngr_ns == 0));
+ }
+
+ /* pci hli btnit */
+ for(i = 0; i < pci->hli.hl_gi.btngr_ns; i++) {
+ for(j = 0; j < (36 / pci->hli.hl_gi.btngr_ns); j++) {
+ int n = (36 / pci->hli.hl_gi.btngr_ns) * i + j;
+ CHECK_VALUE(pci->hli.btnit[n].zero1 == 0);
+ CHECK_VALUE(pci->hli.btnit[n].zero2 == 0);
+ CHECK_VALUE(pci->hli.btnit[n].zero3 == 0);
+ CHECK_VALUE(pci->hli.btnit[n].zero4 == 0);
+ CHECK_VALUE(pci->hli.btnit[n].zero5 == 0);
+ CHECK_VALUE(pci->hli.btnit[n].zero6 == 0);
+
+ if (j < pci->hli.hl_gi.btn_ns) {
+ CHECK_VALUE(pci->hli.btnit[n].x_start <= pci->hli.btnit[n].x_end);
+ CHECK_VALUE(pci->hli.btnit[n].y_start <= pci->hli.btnit[n].y_end);
+ CHECK_VALUE(pci->hli.btnit[n].up <= pci->hli.hl_gi.btn_ns);
+ CHECK_VALUE(pci->hli.btnit[n].down <= pci->hli.hl_gi.btn_ns);
+ CHECK_VALUE(pci->hli.btnit[n].left <= pci->hli.hl_gi.btn_ns);
+ CHECK_VALUE(pci->hli.btnit[n].right <= pci->hli.hl_gi.btn_ns);
+ //vmcmd_verify(pci->hli.btnit[n].cmd);
+ } else {
+ int k;
+ CHECK_VALUE(pci->hli.btnit[n].btn_coln == 0);
+ CHECK_VALUE(pci->hli.btnit[n].auto_action_mode == 0);
+ CHECK_VALUE(pci->hli.btnit[n].x_start == 0);
+ CHECK_VALUE(pci->hli.btnit[n].y_start == 0);
+ CHECK_VALUE(pci->hli.btnit[n].x_end == 0);
+ CHECK_VALUE(pci->hli.btnit[n].y_end == 0);
+ CHECK_VALUE(pci->hli.btnit[n].up == 0);
+ CHECK_VALUE(pci->hli.btnit[n].down == 0);
+ CHECK_VALUE(pci->hli.btnit[n].left == 0);
+ CHECK_VALUE(pci->hli.btnit[n].right == 0);
+ for (k = 0; k < 8; k++)
+ CHECK_VALUE(pci->hli.btnit[n].cmd.bytes[k] == 0); //CHECK_ZERO?
+ }
+ }
+ }
+#endif /* !NDEBUG */
+}
+
+void navRead_DSI(dsi_t *dsi, unsigned char *buffer) {
+ int i;
+
+ CHECK_VALUE(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 */
+ CHECK_VALUE(dsi->dsi_gi.zero1 == 0);
+}
+
diff --git a/dvdread/nav_read.h b/dvdread/nav_read.h
new file mode 100644
index 0000000000..88c46d93bc
--- /dev/null
+++ b/dvdread/nav_read.h
@@ -0,0 +1,55 @@
+#ifndef NAV_READ_H_INCLUDED
+#define NAV_READ_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001, 2002 Håkan Hjort <d95hjort@dtek.chalmers.se>.
+ *
+ * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
+ * detailed changelog at http://svn.mplayerhq.hu/mplayer/trunk/
+ * $Id$
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "nav_types.h"
+
+/**
+ * Parsing of NAV data, PCI and DSI parts.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Reads the PCI packet data pointed to into th pci struct.
+ *
+ * @param pci Pointer to the PCI data structure to be filled in.
+ * @param bufffer Pointer to the buffer of the on disc PCI data.
+ */
+void navRead_PCI(pci_t *, unsigned char *);
+
+/**
+ * Reads the DSI packet data pointed to into dsi struct.
+ *
+ * @param dsi Pointer to the DSI data structure to be filled in.
+ * @param bufffer Pointer to the buffer of the on disc DSI data.
+ */
+void navRead_DSI(dsi_t *, unsigned char *);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* NAV_READ_H_INCLUDED */
diff --git a/dvdread/nav_types.h b/dvdread/nav_types.h
new file mode 100644
index 0000000000..8800e7f894
--- /dev/null
+++ b/dvdread/nav_types.h
@@ -0,0 +1,311 @@
+#ifndef NAV_TYPES_H_INCLUDED
+#define NAV_TYPES_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001, 2002 Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * Modified for use with MPlayer, changes contained in libdvdread_changes.diff.
+ * detailed changelog at http://svn.mplayerhq.hu/mplayer/trunk/
+ * $Id$
+ *
+ * The data structures in this file should represent the layout of the
+ * pci and dsi packets as they are stored in the stream. Information
+ * found by reading the source to VOBDUMP is the base for the structure
+ * and names of these data types.
+ *
+ * VOBDUMP: a program for examining DVD .VOB files.
+ * Copyright 1998, 1999 Eric Smith <eric@brouhaha.com>
+ *
+ * VOBDUMP is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. Note that I am not
+ * granting permission to redistribute or modify VOBDUMP under the terms
+ * of any later version of the General Public License.
+ *
+ * This program is distributed in the hope that it will be useful (or at
+ * least amusing), but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <inttypes.h>
+#include "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; /**< sector address of this nav pack */
+ uint16_t vobu_cat; /**< 'category' of vobu */
+ uint16_t zero1; /**< reserved */
+ user_ops_t vobu_uop_ctl; /**< UOP of vobu */
+ uint32_t vobu_s_ptm; /**< start presentation time of vobu */
+ uint32_t vobu_e_ptm; /**< end presentation time of vobu */
+ uint32_t vobu_se_e_ptm; /**< end ptm of sequence end in vobu */
+ dvd_time_t e_eltm; /**< Cell elapsed time */
+ char vobu_isrc[32];
+} ATTRIBUTE_PACKED pci_gi_t;
+
+/**
+ * Non Seamless Angle Information
+ */
+typedef struct {
+ uint32_t nsml_agl_dsta[9]; /**< address of destination vobu in AGL_C#n */
+} ATTRIBUTE_PACKED nsml_agli_t;
+
+/**
+ * Highlight General Information
+ *
+ * For btngrX_dsp_ty the bits have the following meaning:
+ * 000b: normal 4/3 only buttons
+ * XX1b: wide (16/9) buttons
+ * X1Xb: letterbox buttons
+ * 1XXb: pan&scan buttons
+ */
+typedef struct {
+ uint16_t hli_ss; /**< status, only low 2 bits 0: no buttons, 1: different 2: equal 3: eual except for button cmds */
+ uint32_t hli_s_ptm; /**< start ptm of hli */
+ uint32_t hli_e_ptm; /**< end ptm of hli */
+ uint32_t btn_se_e_ptm; /**< end ptm of button select */
+#ifdef WORDS_BIGENDIAN
+ unsigned int zero1 : 2; /**< reserved */
+ unsigned int btngr_ns : 2; /**< number of button groups 1, 2 or 3 with 36/18/12 buttons */
+ unsigned int zero2 : 1; /**< reserved */
+ unsigned int btngr1_dsp_ty : 3; /**< display type of subpic stream for button group 1 */
+ unsigned int zero3 : 1; /**< reserved */
+ unsigned int btngr2_dsp_ty : 3; /**< display type of subpic stream for button group 2 */
+ unsigned int zero4 : 1; /**< reserved */
+ unsigned int btngr3_dsp_ty : 3; /**< display type of subpic stream for button group 3 */
+#else
+ unsigned int btngr1_dsp_ty : 3;
+ unsigned int zero2 : 1;
+ 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; /**< button offset number range 0-255 */
+ uint8_t btn_ns; /**< number of valid buttons <= 36/18/12 (low 6 bits) */
+ uint8_t nsl_btn_ns; /**< number of buttons selectable by U_BTNNi (low 6 bits) nsl_btn_ns <= btn_ns */
+ uint8_t zero5; /**< reserved */
+ uint8_t fosl_btnn; /**< forcedly selected button (low 6 bits) */
+ uint8_t foac_btnn; /**< forcedly activated button (low 6 bits) */
+} ATTRIBUTE_PACKED hl_gi_t;
+
+
+/**
+ * Button Color Information Table
+ * Each entry beeing a 32bit word that contains the color indexs and alpha
+ * values to use. They are all represented by 4 bit number and stored
+ * like this [Ci3, Ci2, Ci1, Ci0, A3, A2, A1, A0]. The actual palette
+ * that the indexes reference is in the PGC.
+ * @TODO split the uint32_t into a struct
+ */
+typedef struct {
+ uint32_t btn_coli[3][2]; /**< [button color number-1][select:0/action:1] */
+} ATTRIBUTE_PACKED btn_colit_t;
+
+/**
+ * Button Information
+ *
+ * NOTE: I've had to change the structure from the disk layout to get
+ * the packing to work with Sun's Forte C compiler.
+ * The 4 and 7 bytes are 'rotated' was: ABC DEF GHIJ is: ABCG DEFH IJ
+ */
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+ unsigned int btn_coln : 2; /**< button color number */
+ unsigned int x_start : 10; /**< x start offset within the overlay */
+ unsigned int zero1 : 2; /**< reserved */
+ unsigned int x_end : 10; /**< x end offset within the overlay */
+
+ unsigned int zero3 : 2; /**< reserved */
+ unsigned int up : 6; /**< button index when pressing up */
+
+ unsigned int auto_action_mode : 2; /**< 0: no, 1: activated if selected */
+ unsigned int y_start : 10; /**< y start offset within the overlay */
+ unsigned int zero2 : 2; /**< reserved */
+ unsigned int y_end : 10; /**< y end offset within the overlay */
+
+ unsigned int zero4 : 2; /**< reserved */
+ unsigned int down : 6; /**< button index when pressing down */
+ unsigned int zero5 : 2; /**< reserved */
+ unsigned int left : 6; /**< button index when pressing left */
+ unsigned int zero6 : 2; /**< reserved */
+ unsigned int right : 6; /**< button index when pressing right */
+#else
+ unsigned int x_end : 10;
+ unsigned int zero1 : 2;
+ unsigned int x_start : 10;
+ unsigned int btn_coln : 2;
+
+ unsigned int up : 6;
+ unsigned int zero3 : 2;
+
+ unsigned int y_end : 10;
+ unsigned int zero2 : 2;
+ unsigned int y_start : 10;
+ unsigned int auto_action_mode : 2;
+
+ unsigned int 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; /**< sector address of this nav pack */
+ uint32_t vobu_ea; /**< end address of this VOBU */
+ uint32_t vobu_1stref_ea; /**< end address of the 1st reference image */
+ uint32_t vobu_2ndref_ea; /**< end address of the 2nd reference image */
+ uint32_t vobu_3rdref_ea; /**< end address of the 3rd reference image */
+ uint16_t vobu_vob_idn; /**< VOB Id number that this VOBU is part of */
+ uint8_t zero1; /**< reserved */
+ uint8_t vobu_c_idn; /**< Cell Id number that this VOBU is part of */
+ dvd_time_t c_eltm; /**< Cell elapsed time */
+} ATTRIBUTE_PACKED dsi_gi_t;
+
+/**
+ * Seamless Playback Information
+ */
+typedef struct {
+ uint16_t category; /**< 'category' of seamless VOBU */
+ uint32_t ilvu_ea; /**< end address of interleaved Unit */
+ uint32_t ilvu_sa; /**< start address of next interleaved unit */
+ uint16_t size; /**< size of next interleaved unit */
+ uint32_t vob_v_s_s_ptm; /**< video start ptm in vob */
+ uint32_t vob_v_e_e_ptm; /**< video end ptm in vob */
+ struct {
+ uint32_t stp_ptm1;
+ uint32_t stp_ptm2;
+ uint32_t gap_len1;
+ uint32_t gap_len2;
+ } vob_a[8];
+} ATTRIBUTE_PACKED sml_pbi_t;
+
+/**
+ * Seamless Angle Infromation for one angle
+ */
+typedef struct {
+ uint32_t address; /**< offset to next ILVU, high bit is before/after */
+ uint16_t size; /**< byte size of the ILVU pointed to by address */
+} ATTRIBUTE_PACKED sml_agl_data_t;
+
+/**
+ * Seamless Angle Infromation
+ */
+typedef struct {
+ sml_agl_data_t data[9];
+} ATTRIBUTE_PACKED sml_agli_t;
+
+/**
+ * VOBU Search Information
+ */
+typedef struct {
+ uint32_t next_video; /**< Next vobu that contains video */
+ uint32_t fwda[19]; /**< Forwards, time */
+ uint32_t next_vobu;
+ uint32_t prev_vobu;
+ uint32_t bwda[19]; /**< Backwards, time */
+ uint32_t prev_video;
+} ATTRIBUTE_PACKED vobu_sri_t;
+
+#define SRI_END_OF_CELL 0x3fffffff
+
+/**
+ * Synchronous Information
+ */
+typedef struct {
+ uint16_t a_synca[8]; /**< offset to first audio packet for this VOBU */
+ uint32_t sp_synca[32]; /**< offset to first subpicture packet */
+} ATTRIBUTE_PACKED synci_t;
+
+/**
+ * DSI packet
+ */
+typedef struct {
+ dsi_gi_t dsi_gi;
+ sml_pbi_t sml_pbi;
+ sml_agli_t sml_agli;
+ vobu_sri_t vobu_sri;
+ synci_t synci;
+ uint8_t zero1[471];
+} ATTRIBUTE_PACKED dsi_t;
+
+
+#if PRAGMA_PACK
+#pragma pack()
+#endif
+
+#endif /* NAV_TYPES_H_INCLUDED */