[Mplayer-cvslog] CVS: main/libmpdvdkit Makefile,NONE,1.1 README,NONE,1.1 bswap.h,NONE,1.1 common.h,NONE,1.1 config.h,NONE,1.1 css.c,NONE,1.1 css.h,NONE,1.1 csstables.h,NONE,1.1 dvd_reader.c,NONE,1.1 dvd_reader.h,NONE,1.1 dvd_udf.c,NONE,1.1 dvd_udf.h,NONE,1.1 dvdcss.h,NONE,1.1 get_path.c,NONE,1.1 ifo_print.c,NONE,1.1 ifo_print.h,NONE,1.1 ifo_read.c,NONE,1.1 ifo_read.h,NONE,1.1 ifo_types.h,NONE,1.1 int_types.h,NONE,1.1 ioctl.c,NONE,1.1 ioctl.h,NONE,1.1 libdvdcss.c,NONE,1.1 libdvdcss.h,NONE,1.1 nav_print.c,NONE,1.1 nav_print.h,NONE,1.1 nav_read.c,NONE,1.1 nav_read.h,NONE,1.1 nav_types.h,NONE,1.1

Arpi of Ize arpi at mplayerhq.hu
Wed Apr 24 21:28:07 CEST 2002


Update of /cvsroot/mplayer/main/libmpdvdkit
In directory mail:/var/tmp.root/cvs-serv3761

Added Files:
	Makefile README bswap.h common.h config.h css.c css.h 
	csstables.h dvd_reader.c dvd_reader.h dvd_udf.c dvd_udf.h 
	dvdcss.h get_path.c ifo_print.c ifo_print.h ifo_read.c 
	ifo_read.h ifo_types.h int_types.h ioctl.c ioctl.h libdvdcss.c 
	libdvdcss.h nav_print.c nav_print.h nav_read.c nav_read.h 
	nav_types.h 
Log Message:
mplayer-dvdkit v1.0-pre2 by .so

--- NEW FILE ---

ifneq ($(wildcard ../config.mak),)
include ../config.mak
endif

SRCS = dvd_reader.c dvd_udf.c ifo_read.c ifo_print.c nav_read.c nav_print.c \
	css.c ioctl.c libdvdcss.c
OBJS = $(SRCS:.c=.o)

LIB=libmpdvdkit

.SUFFIXES: .c .o

ifeq ($(OPTFLAGS),)
 OPTFLAGS = -mcpu=pentium -march=pentium -O4 -I.
else
 OPTFLAGS += -DHAVE_MPLAYER
endif

CFLAGS= $(OPTFLAGS) \
	-D_LARGEFILE64_SOURCE \
	-DSYS_LINUX -D_FILE_OFFSET_BITS=64 -D__USE_UNIX98 -D_REENTRANT -D_GNU_SOURCE \
	-ffast-math -funroll-loops -fomit-frame-pointer 

.c.o:
	$(CC) $(CFLAGS) -c -o $@ $<

all: $(LIB).a 
default: $(LIB).a

$(LIB).a: $(OBJS)
	ar rc $(LIB).a $(OBJS)

$(LIB).so: $(OBJS)
	$(CC) -o $(LIB).so -shared -rdynamic $(OBJS)

clean:
	rm -f $(OBJS) $(LIB).a .depend

distclean: clean

dep:    depend

depend: .depend

.depend: Makefile config.h
	$(CC) -MM $(CFLAGS) $(SRCS) 1>.depend

#
# include dependency files if they exist
#
ifneq ($(wildcard .depend),)                                                                       
include .depend                                                                                    
endif                                                                                              

--- NEW FILE ---
MPlayer DVD-kit v1.0-pre2 :)
made by Pontscho for the MPlayer project

What the hell is this?
======================
nothing special, just collection of sources and patches and fixes:

- dvdread 0.9.2 + assert() bug workaround
- libdvdcss 0.0.3 + css key cache patch + per-disc caching
- optimizations enabled, asserts disabled
everything packed together with static linking to maximize performance

Why?
====
users keep complaining about the still not fixed assert() bug, the
buggy libdvdcss 1.0 or the slow key-cracking process...
so Pontscho decided to pack these together, fix bugs and make it
available for the MPlayer users.

Installation? How? What?
========================
Just unpack this archive inside the mplayer source dir,
and MPlayer's ./configure will detects and use this!

How to create mpdvdkit shared object?
=====================================

make libmpdvdkit.so


--- NEW FILE ---
/**
 * Copyright (C) 2000, 2001 Billy Biggs <vektor at dumbterm.net>,
 *                          Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#ifndef BSWAP_H_INCLUDED
#define BSWAP_H_INCLUDED

//#include <config.h>

#if defined(WORDS_BIGENDIAN)
/* All bigendian systems are fine, just ignore the swaps. */  
#define B2N_16(x) (void)(x)
#define B2N_32(x) (void)(x)
#define B2N_64(x) (void)(x)

#else 

#if defined(__linux__)
#include <byteswap.h>
#define B2N_16(x) x = bswap_16(x)
#define B2N_32(x) x = bswap_32(x)
#define B2N_64(x) x = bswap_64(x)

#elif defined(__NetBSD__)
#include <sys/endian.h>
#define B2N_16(x) BE16TOH(x)
#define B2N_32(x) BE32TOH(x)
#define B2N_64(x) BE64TOH(x)

#elif defined(__OpenBSD__)
#include <sys/endian.h>
#define B2N_16(x) x = swap16(x)
#define B2N_32(x) x = swap32(x)
#define B2N_64(x) x = swap64(x)

/* This is a slow but portable implementation, it has multiple evaluation 
 * problems so beware.
 * FreeBSD and Solaris don't have <byteswap.h> or any other such 
 * functionality! 
 */

#elif defined(__FreeBSD__) || defined(__sun) || defined(__bsdi__)
#define B2N_16(x) \
 x = ((((x) & 0xff00) >> 8) | \
      (((x) & 0x00ff) << 8))
#define B2N_32(x) \
 x = ((((x) & 0xff000000) >> 24) | \
      (((x) & 0x00ff0000) >>  8) | \
      (((x) & 0x0000ff00) <<  8) | \
      (((x) & 0x000000ff) << 24))
#define B2N_64(x) \
 x = ((((x) & 0xff00000000000000) >> 56) | \
      (((x) & 0x00ff000000000000) >> 40) | \
      (((x) & 0x0000ff0000000000) >> 24) | \
      (((x) & 0x000000ff00000000) >>  8) | \
      (((x) & 0x00000000ff000000) <<  8) | \
      (((x) & 0x0000000000ff0000) << 24) | \
      (((x) & 0x000000000000ff00) << 40) | \
      (((x) & 0x00000000000000ff) << 56))

#else

/* If there isn't a header provided with your system with this functionality
 * add the relevant || define( ) to the portable implementation above.
 */
#error "You need to add endian swap macros for you're system"

#endif

#endif /* WORDS_BIGENDIAN */

#endif /* BSWAP_H_INCLUDED */

--- NEW FILE ---
/*****************************************************************************
 * common.h: common definitions
 * Collection of useful common types and macros definitions
 *****************************************************************************
 * Copyright (C) 1998, 1999, 2000 VideoLAN
 * $Id: common.h,v 1.1 2002/04/24 19:28:03 arpi Exp $
 *
 * Authors: Samuel Hocevar <sam at via.ecp.fr>
 *          Vincent Seguin <seguin at via.ecp.fr>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * required headers:
 *  config.h
 *****************************************************************************/

/*****************************************************************************
 * Basic types definitions
 *****************************************************************************/

#include "int_types.h"

typedef u8                  byte_t;

/* Boolean type */
typedef int                 boolean_t;

#ifdef SYS_GNU
#   define _MACH_I386_BOOLEAN_H_
#endif

/* ptrdiff_t definition */
#ifdef HAVE_STDDEF_H
#   include <stddef.h>
#else
#   include <malloc.h>
#   ifndef _PTRDIFF_T
#       define _PTRDIFF_T
/* Not portable in a 64-bit environment. */
typedef int                 ptrdiff_t;
#   endif
#endif

/* Counter for statistics and profiling */
typedef unsigned long       count_t;

/* DCT elements types */
#ifndef VDEC_DFT
typedef short dctelem_t;
#else
typedef int dctelem_t;
#endif

/*****************************************************************************
 * Classes declaration
 *****************************************************************************/

/* Plugins */
struct plugin_bank_s;
struct plugin_info_s;

typedef struct plugin_bank_s *          p_plugin_bank_t;
typedef struct plugin_info_s *          p_plugin_info_t;

/* Plugins */
struct playlist_s;
struct playlist_item_s;

typedef struct playlist_s *             p_playlist_t;
typedef struct playlist_item_s *        p_playlist_item_t;

/* Interface */
struct intf_thread_s;
struct intf_sys_s;
struct intf_console_s;
struct intf_msg_s;
struct intf_channel_s;

typedef struct intf_thread_s *          p_intf_thread_t;
typedef struct intf_sys_s *             p_intf_sys_t;
typedef struct intf_console_s *         p_intf_console_t;
typedef struct intf_msg_s *             p_intf_msg_t;
typedef struct intf_channel_s *         p_intf_channel_t;

/* Input */
struct input_thread_s;
struct input_channel_s;
struct input_cfg_s;

typedef struct input_thread_s *         p_input_thread_t;
typedef struct input_channel_s *        p_input_channel_t;
typedef struct input_cfg_s *            p_input_cfg_t;

/* Audio */
struct aout_thread_s;
struct aout_sys_s;

typedef struct aout_thread_s *          p_aout_thread_t;
typedef struct aout_sys_s *             p_aout_sys_t;

/* Video */
struct vout_thread_s;
struct vout_font_s;
struct vout_sys_s;
struct vdec_thread_s;
struct vpar_thread_s;
struct video_parser_s;

typedef struct vout_thread_s *          p_vout_thread_t;
typedef struct vout_font_s *            p_vout_font_t;
typedef struct vout_sys_s *             p_vout_sys_t;
typedef struct vdec_thread_s *          p_vdec_thread_t;
typedef struct vpar_thread_s *          p_vpar_thread_t;
typedef struct video_parser_s *         p_video_parser_t;

/* Misc */
struct macroblock_s;
struct data_packet_s;
struct es_descriptor_s;
struct pgrm_descriptor_s;

/*****************************************************************************
 * Macros and inline functions
 *****************************************************************************/
#ifdef NTOHL_IN_SYS_PARAM_H
#   include <sys/param.h>
#elif defined(WIN32)
#   include <winsock.h>
#else
#   include <netinet/in.h>
#endif

/* CEIL: division with round to nearest greater integer */
#define CEIL(n, d)  ( ((n) / (d)) + ( ((n) % (d)) ? 1 : 0) )

/* PAD: PAD(n, d) = CEIL(n ,d) * d */
#define PAD(n, d)   ( ((n) % (d)) ? ((((n) / (d)) + 1) * (d)) : (n) )

/* MAX and MIN: self explanatory */
#ifndef MAX
#   define MAX(a, b)   ( ((a) > (b)) ? (a) : (b) )
#endif
#ifndef MIN
#   define MIN(a, b)   ( ((a) < (b)) ? (a) : (b) )
#endif

/* MSB (big endian)/LSB (little endian) conversions - network order is always
 * MSB, and should be used for both network communications and files. Note that
 * byte orders other than little and big endians are not supported, but only
 * the VAX seems to have such exotic properties - note that these 'functions'
 * needs <netinet/in.h> or the local equivalent. */
/* FIXME: hton64 should be declared as an extern inline function to avoid
 * border effects (see byteorder.h) */
#if WORDS_BIGENDIAN
#   define hton16      htons
#   define hton32      htonl
#   define hton64(i)   ( i )
#   define ntoh16      ntohs
#   define ntoh32      ntohl
#   define ntoh64(i)   ( i )
#else
#   define hton16      htons
#   define hton32      htonl
#   define hton64(i)   ( ((u64)(htonl((i) & 0xffffffff)) << 32) | htonl(((i) >> 32) & 0xffffffff ) )
#   define ntoh16      ntohs
#   define ntoh32      ntohl
#   define ntoh64      hton64
#endif

/* Macros with automatic casts */
#define U64_AT(p)   ( ntoh64 ( *( (u64 *)(p) ) ) )
#define U32_AT(p)   ( ntoh32 ( *( (u32 *)(p) ) ) )
#define U16_AT(p)   ( ntoh16 ( *( (u16 *)(p) ) ) )

/* win32, cl and icl support */
#if defined( _MSC_VER )
#   define __attribute__(x)
#   define __inline__      __inline
#   define strncasecmp     strnicmp
#   define strcasecmp      stricmp
#   define S_ISBLK(m)      (0)
#   define S_ISCHR(m)      (0)
#   define S_ISFIFO(m)     (((m)&_S_IFMT) == _S_IFIFO)
#   define S_ISREG(m)      (((m)&_S_IFMT) == _S_IFREG)
#   define I64C(x)         x
#else
#   define I64C(x)         x##LL
#endif

#if defined( WIN32 )
#   ifndef _OFF_T_DEFINED
typedef __int64 off_t;
#       define _OFF_T_DEFINED
#   endif
#   ifndef snprintf
#       define snprintf _snprintf  /* snprintf not defined in mingw32 (bug?) */
#   endif
#endif

--- NEW FILE ---

/* Define if your processor stores words with the most significant
   byte first (like Motorola and SPARC, unlike Intel and VAX).  */
#ifdef HAVE_MPLAYER

#include "../config.h"

#else

#undef WORDS_BIGENDIAN

/* Define if you have the <stddef.h> header file.  */
#define HAVE_STDDEF_H 1

/* Define if <sys/dvdio.h> defines dvd_struct. */
#undef DVD_STRUCT_IN_SYS_DVDIO_H

/* Define if <sys/cdio.h> defines dvd_struct. */
#undef DVD_STRUCT_IN_SYS_CDIO_H

/* Define if <linux/cdrom.h> defines DVD_STRUCT. */
#define DVD_STRUCT_IN_LINUX_CDROM_H 1

/* Define if <dvd.h> defines DVD_STRUCT. */
#undef DVD_STRUCT_IN_DVD_H

/* Define if <extras/BSDI_dvdioctl/dvd.h> defines DVD_STRUCT. */
#undef DVD_STRUCT_IN_BSDI_DVDIOCTL_DVD_H

/* Have userspace SCSI headers. */
#undef SOLARIS_USCSI

/* Define if Linux-like dvd_struct is defined. */
#define HAVE_LINUX_DVD_STRUCT 1

/* Define if BSD-like dvd_struct is defined. */
#undef HAVE_BSD_DVD_STRUCT

#endif

/* assert support */
#undef HAVE_ASSERT_H

#ifndef HAVE_ASSERT_H
 #define assert( ... ) do {} while(0)
#endif

--- NEW FILE ---
/*****************************************************************************
 * css.c: Functions for DVD authentification and unscrambling
 *****************************************************************************
 * Copyright (C) 1999-2001 VideoLAN
 * $Id: css.c,v 1.1 2002/04/24 19:28:03 arpi Exp $
 *
 * Author: Stéphane Borel <stef at via.ecp.fr>
 *
 * based on:
 *  - css-auth by Derek Fawcus <derek at spider.com>
 *  - DVD CSS ioctls example program by Andrew T. Veliath <andrewtv at usa.net>
 *  - The Divide and conquer attack by Frank A. Stevenson <frank at funcom.com>
 *  - DeCSSPlus by Ethan Hawke
 *  - DecVOB
 *  see http://www.lemuria.org/DeCSS/ by Tom Vogt for more information.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>

#include <string.h>

//#include "config.h"
#include "common.h"

#include "dvdcss.h"
#include "libdvdcss.h"

#include "csstables.h"
#include "ioctl.h"

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static int  CSSGetASF    ( dvdcss_handle dvdcss );
static void CSSCryptKey  ( int i_key_type, int i_varient,
                           u8 const * p_challenge, u8* p_key );
static int  CSSCracker   ( int i_start, unsigned char * p_crypted,
                           unsigned char * p_decrypted,
                           dvd_key_t * p_sector_key, dvd_key_t * p_key );

/*****************************************************************************
 * CSSTest : check if the disc is encrypted or not
 *****************************************************************************/
int CSSTest( dvdcss_handle dvdcss )
{
    int i_ret, i_copyright;

    i_ret = ioctl_ReadCopyright( dvdcss->i_fd, 0 /* i_layer */, &i_copyright );

    if( i_ret < 0 )
    {
        /* Since it's the first ioctl we try to issue, we add a notice */
        _dvdcss_error( dvdcss, "css error: ioctl_ReadCopyright failed, "
                       "make sure DVD ioctls were compiled in" );

        return i_ret;
    }

    return i_copyright;
}

/*****************************************************************************
 * CSSInit : CSS Structure initialisation and DVD authentication.
 *****************************************************************************
 * It simulates the mutual authentication between logical unit and host.
 * Since we don't need the disc key to find the title key, we just run the
 * basic unavoidable commands to authenticate device and disc.
 *****************************************************************************/
int CSSInit( dvdcss_handle dvdcss )
{
    /* structures defined in cdrom.h or dvdio.h */
    unsigned char p_buffer[2048 + 4 + 1];
    char psz_warning[32];
    int  i_agid = 0;
    int  i_ret = -1;
    int  i;

    /* Test authentication success */
    switch( CSSGetASF( dvdcss ) )
    {
        case -1:
            return -1;

        case 1:
            _dvdcss_debug( dvdcss, "already authenticated" );
            return 0;

        case 0:
            _dvdcss_debug( dvdcss, "need to authenticate" );
    }

    /* Init sequence, request AGID */
    for( i = 1; i < 4 ; ++i )
    {
        sprintf( psz_warning, "requesting AGID %d", i );
        _dvdcss_debug( dvdcss, psz_warning );

        i_ret = ioctl_ReportAgid( dvdcss->i_fd, &i_agid );

        if( i_ret != -1 )
        {
            /* No error during ioctl: we know the device is authenticated */
            break;
        }

        _dvdcss_error( dvdcss, "ioctl_ReportAgid failed, invalidating" );

        i_agid = 0;
        ioctl_InvalidateAgid( dvdcss->i_fd, &i_agid );
    }

    /* Unable to authenticate without AGID */
    if( i_ret == -1 )
    {
        _dvdcss_error( dvdcss, "ioctl_ReportAgid failed, fatal" );
        return -1;
    }

    for( i = 0 ; i < 10; ++i )
    {
        dvdcss->css.disc.p_challenge[i] = i;
    }

    /* Get challenge from host */
    for( i = 0 ; i < 10 ; ++i )
    {
        p_buffer[9-i] = dvdcss->css.disc.p_challenge[i];
    }

    /* Send challenge to LU */
    if( ioctl_SendChallenge( dvdcss->i_fd, &i_agid, p_buffer ) < 0 )
    {
        _dvdcss_error( dvdcss, "ioctl_SendChallenge failed" );
        return -1;
    }

    /* Get key1 from LU */
    if( ioctl_ReportKey1( dvdcss->i_fd, &i_agid, p_buffer ) < 0)
    {
        _dvdcss_error( dvdcss, "ioctl_ReportKey1 failed" );
        return -1;
    }

    /* Send key1 to host */
    for( i = 0 ; i < KEY_SIZE ; i++ )
    {
        dvdcss->css.disc.p_key1[i] = p_buffer[4-i];
    }

    for( i = 0 ; i < 32 ; ++i )
    {
        CSSCryptKey( 0, i, dvdcss->css.disc.p_challenge,
                           dvdcss->css.disc.p_key_check );

        if( memcmp( dvdcss->css.disc.p_key_check,
                    dvdcss->css.disc.p_key1, KEY_SIZE ) == 0 )
        {
            sprintf( psz_warning, "drive authentic, using variant %d", i );
            _dvdcss_debug( dvdcss, psz_warning );
            dvdcss->css.disc.i_varient = i;
            break;
        }
    }

    if( i == 32 )
    {
        _dvdcss_error( dvdcss, "drive would not authenticate" );
        return -1;
    }

    /* Get challenge from LU */
    if( ioctl_ReportChallenge( dvdcss->i_fd, &i_agid, p_buffer ) < 0 )
    {
        _dvdcss_error( dvdcss, "ioctl_ReportKeyChallenge failed" );
        return -1;
    }

    /* Send challenge to host */
    for( i = 0 ; i < 10 ; ++i )
    {
        dvdcss->css.disc.p_challenge[i] = p_buffer[9-i];
    }

    CSSCryptKey( 1, dvdcss->css.disc.i_varient,
                    dvdcss->css.disc.p_challenge,
                    dvdcss->css.disc.p_key2 );

    /* Get key2 from host */
    for( i = 0 ; i < KEY_SIZE ; ++i )
    {
        p_buffer[4-i] = dvdcss->css.disc.p_key2[i];
    }

    /* Send key2 to LU */
    if( ioctl_SendKey2( dvdcss->i_fd, &i_agid, p_buffer ) < 0 )
    {
        _dvdcss_error( dvdcss, "ioctl_SendKey2 failed" );
        return -1;
    }

    _dvdcss_debug( dvdcss, "authentication established" );

    memcpy( dvdcss->css.disc.p_challenge,
            dvdcss->css.disc.p_key1, KEY_SIZE );
    memcpy( dvdcss->css.disc.p_challenge+KEY_SIZE,
            dvdcss->css.disc.p_key2, KEY_SIZE );

    CSSCryptKey( 2, dvdcss->css.disc.i_varient,
                    dvdcss->css.disc.p_challenge,
                    dvdcss->css.disc.p_key_check );

    _dvdcss_debug( dvdcss, "received session key" );

    if( i_agid < 0 )
    {
        return -1;
    }

    /* Test authentication success */
    switch( CSSGetASF( dvdcss ) )
    {
        case -1:
            return -1;

        case 1:
            _dvdcss_debug( dvdcss, "already authenticated" );
            return 0;

        case 0:
            _dvdcss_debug( dvdcss, "need to get disc key" );
    }

    /* Get encrypted disc key */
    if( ioctl_ReadKey( dvdcss->i_fd, &i_agid, p_buffer ) < 0 )
    {
        _dvdcss_error( dvdcss, "ioctl_ReadKey failed" );
        return -1;
    }

    /* Unencrypt disc key using bus key */
    for( i = 0 ; i < 2048 ; i++ )
    {
        p_buffer[ i ] ^= dvdcss->css.disc.p_key_check[ 4 - (i % KEY_SIZE) ];
    }
    memcpy( dvdcss->css.disc.p_key_check, p_buffer, 2048 );

    /* Test authentication success */
    switch( CSSGetASF( dvdcss ) )
    {
        case -1:
            return -1;

        case 1:
            _dvdcss_debug( dvdcss, "successfully authenticated" );
            return 0;

        case 0:
            _dvdcss_error( dvdcss, "no way to authenticate" );
            return -1;
    }

    return -1;
}

/*****************************************************************************
 * CSSGetKey : get title key.
 *****************************************************************************
 * The DVD should have been opened and authenticated before.
 *****************************************************************************/
int CSSGetKey( dvdcss_handle dvdcss, int i_pos, dvd_key_t p_titlekey )
{
    /*
     * Title key cracking method from Ethan Hawke,
     * with Frank A. Stevenson algorithm.
     * Does not use any player key table and ioctls.
     */
    u8          p_buf[0x800];
    dvd_key_t   p_key;
    boolean_t   b_encrypted;
    boolean_t   b_stop_scanning;
    int         i_blocks_read;
    int         i_best_plen;
    int         i_best_p;
    int         i,j;

    for( i = 0 ; i < KEY_SIZE ; i++ )
    {
        p_key[i] = 0;
    }

    b_encrypted = 0;
    b_stop_scanning = 0;

    do
    {
        i_pos = dvdcss_seek( dvdcss, i_pos );
        i_blocks_read = dvdcss_read( dvdcss, p_buf, 1, DVDCSS_NOFLAGS );

        /* PES_scrambling_control */
        if( p_buf[0x14] & 0x30 )
        {
            b_encrypted = 1;
            i_best_plen = 0;
            i_best_p = 0;

            for( i = 2 ; i < 0x30 ; i++ )
            {
                for( j = i+1 ;
                     j < 0x80 && ( p_buf[0x7F - (j%i)] == p_buf[0x7F-j] );
                     j++ );
                {
                    if( j > i_best_plen )
                    {
                        i_best_plen = j;
                        i_best_p = i;
                    }
                }
            }

            if( ( i_best_plen > 20 ) && ( i_best_plen / i_best_p >= 2) )
            {
                i = CSSCracker( 0,  &p_buf[0x80],
                        &p_buf[0x80 - ( i_best_plen / i_best_p) *i_best_p],
                        (dvd_key_t*)&p_buf[0x54],
                        &p_key );
                b_stop_scanning = ( i >= 0 );
            }
        }

        i_pos += i_blocks_read;

    } while( i_blocks_read == 0x1 && !b_stop_scanning );

    if( b_stop_scanning )
    {
        memcpy( p_titlekey, &p_key, sizeof(dvd_key_t) );
        _dvdcss_debug( dvdcss, "vts key initialized" );
        return 0;
    }

    if( !b_encrypted )
    {
        _dvdcss_debug( dvdcss, "file was unscrambled" );
        return 0;
    }

    return -1;
}

/*****************************************************************************
 * CSSDescrambleSector
 *****************************************************************************
 * sec : sector to descramble
 * key : title key for this sector
 *****************************************************************************/
int CSSDescrambleSector( dvd_key_t p_key, u8* p_sec )
{
    unsigned int    i_t1, i_t2, i_t3, i_t4, i_t5, i_t6;
    u8*             p_end = p_sec + 0x800;

    /* PES_scrambling_control */
    if( p_sec[0x14] & 0x30)
    {
        i_t1 = ((p_key)[0] ^ p_sec[0x54]) | 0x100;
        i_t2 = (p_key)[1] ^ p_sec[0x55];
        i_t3 = (((p_key)[2]) | ((p_key)[3] << 8) |
               ((p_key)[4] << 16)) ^ ((p_sec[0x56]) |
               (p_sec[0x57] << 8) | (p_sec[0x58] << 16));
        i_t4 = i_t3 & 7;
        i_t3 = i_t3 * 2 + 8 - i_t4;
        p_sec += 0x80;
        i_t5 = 0;

        while( p_sec != p_end )
        {
            i_t4 = p_css_tab2[i_t2] ^ p_css_tab3[i_t1];
            i_t2 = i_t1>>1;
            i_t1 = ( ( i_t1 & 1 ) << 8 ) ^ i_t4;
            i_t4 = p_css_tab5[i_t4];
            i_t6 = ((((((( i_t3 >> 3 ) ^ i_t3 ) >> 1 ) ^
                                         i_t3 ) >> 8 ) ^ i_t3 ) >> 5) & 0xff;
            i_t3 = (i_t3 << 8 ) | i_t6;
            i_t6 = p_css_tab4[i_t6];
            i_t5 += i_t6 + i_t4;
            *p_sec = p_css_tab1[*p_sec] ^( i_t5 & 0xff );
            p_sec++;
            i_t5 >>= 8;
        }
    }

    return 0;
}

/* Following functions are local */

/*****************************************************************************
 * CSSGetASF : Get Authentification success flag
 *****************************************************************************
 * Returns :
 *  -1 on ioctl error,
 *  0 if the device needs to be authenticated,
 *  1 either.
 *****************************************************************************/
static int CSSGetASF( dvdcss_handle dvdcss )
{
    int i_agid;
    int i_asf = 0;

    for( i_agid = 0 ; i_agid < 4 ; i_agid++ )
    {
        if( ioctl_ReportASF( dvdcss->i_fd, &i_agid, &i_asf ) == 0 )
        {
            if( i_asf )
            {
                _dvdcss_debug( dvdcss, "GetASF authenticated" );
            }
            else
            {
                _dvdcss_debug( dvdcss, "GetASF not authenticated" );
            }

            return i_asf;
        }
    }

    /* The ioctl process has failed */
    _dvdcss_error( dvdcss, "GetASF fatal error" );
    return -1;
}

/*****************************************************************************
 * CSSCryptKey : shuffles bits and unencrypt keys.
 *****************************************************************************
 * Used during authentication and disc key negociation in CSSInit.
 * i_key_type : 0->key1, 1->key2, 2->buskey.
 * i_varient : between 0 and 31.
 *****************************************************************************/
static void CSSCryptKey( int i_key_type, int i_varient,
                         u8 const * p_challenge, u8* p_key )
{
    /* Permutation table for challenge */
    u8      pp_perm_challenge[3][10] =
            { { 1, 3, 0, 7, 5, 2, 9, 6, 4, 8 },
              { 6, 1, 9, 3, 8, 5, 7, 4, 0, 2 },
              { 4, 0, 3, 5, 7, 2, 8, 6, 1, 9 } };

    /* Permutation table for varient table for key2 and buskey */
    u8      pp_perm_varient[2][32] =
            { { 0x0a, 0x08, 0x0e, 0x0c, 0x0b, 0x09, 0x0f, 0x0d,
                0x1a, 0x18, 0x1e, 0x1c, 0x1b, 0x19, 0x1f, 0x1d,
                0x02, 0x00, 0x06, 0x04, 0x03, 0x01, 0x07, 0x05,
                0x12, 0x10, 0x16, 0x14, 0x13, 0x11, 0x17, 0x15 },
              { 0x12, 0x1a, 0x16, 0x1e, 0x02, 0x0a, 0x06, 0x0e,
                0x10, 0x18, 0x14, 0x1c, 0x00, 0x08, 0x04, 0x0c,
                0x13, 0x1b, 0x17, 0x1f, 0x03, 0x0b, 0x07, 0x0f,
                0x11, 0x19, 0x15, 0x1d, 0x01, 0x09, 0x05, 0x0d } };

    u8      p_varients[32] =
            {   0xB7, 0x74, 0x85, 0xD0, 0xCC, 0xDB, 0xCA, 0x73,
                0x03, 0xFE, 0x31, 0x03, 0x52, 0xE0, 0xB7, 0x42,
                0x63, 0x16, 0xF2, 0x2A, 0x79, 0x52, 0xFF, 0x1B,
                0x7A, 0x11, 0xCA, 0x1A, 0x9B, 0x40, 0xAD, 0x01 };

    /* The "secret" key */
    u8      p_secret[5] = { 0x55, 0xD6, 0xC4, 0xC5, 0x28 };

    u8      p_bits[30];
    u8      p_scratch[10];
    u8      p_tmp1[5];
    u8      p_tmp2[5];
    u8      i_lfsr0_o;  /* 1 bit used */
    u8      i_lfsr1_o;  /* 1 bit used */
    u32     i_lfsr0;
    u32     i_lfsr1;
    u8      i_css_varient;
    u8      i_cse;
    u8      i_index;
    u8      i_combined;
    u8      i_carry;
    u8      i_val = 0;
    int     i_term = 0;
    int     i_bit;
    int     i;

    for (i = 9; i >= 0; --i)
        p_scratch[i] = p_challenge[pp_perm_challenge[i_key_type][i]];

    i_css_varient = ( i_key_type == 0 ) ? i_varient :
                    pp_perm_varient[i_key_type-1][i_varient];

    /*
     * This encryption engine implements one of 32 variations
     * one the same theme depending upon the choice in the
     * varient parameter (0 - 31).
     *
     * The algorithm itself manipulates a 40 bit input into
     * a 40 bit output.
     * The parameter 'input' is 80 bits.  It consists of
     * the 40 bit input value that is to be encrypted followed
     * by a 40 bit seed value for the pseudo random number
     * generators.
     */

    /* Feed the secret into the input values such that
     * we alter the seed to the LFSR's used above,  then
     * generate the bits to play with.
     */
    for( i = 5 ; --i >= 0 ; )
    {
        p_tmp1[i] = p_scratch[5 + i] ^ p_secret[i] ^ p_crypt_tab2[i];
    }

    /*
     * We use two LFSR's (seeded from some of the input data bytes) to
     * generate two streams of pseudo-random bits.  These two bit streams
     * are then combined by simply adding with carry to generate a final
     * sequence of pseudo-random bits which is stored in the buffer that
     * 'output' points to the end of - len is the size of this buffer.
     *
     * The first LFSR is of degree 25,  and has a polynomial of:
     * x^13 + x^5 + x^4 + x^1 + 1
     *
     * The second LSFR is of degree 17,  and has a (primitive) polynomial of:
     * x^15 + x^1 + 1
     *
     * I don't know if these polynomials are primitive modulo 2,  and thus
     * represent maximal-period LFSR's.
     *
     *
     * Note that we take the output of each LFSR from the new shifted in
     * bit,  not the old shifted out bit.  Thus for ease of use the LFSR's
     * are implemented in bit reversed order.
     *
     */
    
    /* In order to ensure that the LFSR works we need to ensure that the
     * initial values are non-zero.  Thus when we initialise them from
     * the seed,  we ensure that a bit is set.
     */
    i_lfsr0 = ( p_tmp1[0] << 17 ) | ( p_tmp1[1] << 9 ) |
              (( p_tmp1[2] & ~7 ) << 1 ) | 8 | ( p_tmp1[2] & 7 );
    i_lfsr1 = ( p_tmp1[3] << 9 ) | 0x100 | p_tmp1[4];

    i_index = sizeof(p_bits);
    i_carry = 0;

    do
    {
        for( i_bit = 0, i_val = 0 ; i_bit < 8 ; ++i_bit )
        {

            i_lfsr0_o = ( ( i_lfsr0 >> 24 ) ^ ( i_lfsr0 >> 21 ) ^
                        ( i_lfsr0 >> 20 ) ^ ( i_lfsr0 >> 12 ) ) & 1;
            i_lfsr0 = ( i_lfsr0 << 1 ) | i_lfsr0_o;

            i_lfsr1_o = ( ( i_lfsr1 >> 16 ) ^ ( i_lfsr1 >> 2 ) ) & 1;
            i_lfsr1 = ( i_lfsr1 << 1 ) | i_lfsr1_o;

            i_combined = !i_lfsr1_o + i_carry + !i_lfsr0_o;
            /* taking bit 1 */
            i_carry = ( i_combined >> 1 ) & 1;
            i_val |= ( i_combined & 1 ) << i_bit;
        }
    
        p_bits[--i_index] = i_val;
    } while( i_index > 0 );

    /* This term is used throughout the following to
     * select one of 32 different variations on the
     * algorithm.
     */
    i_cse = p_varients[i_css_varient] ^ p_crypt_tab2[i_css_varient];

    /* Now the actual blocks doing the encryption.  Each
     * of these works on 40 bits at a time and are quite
     * similar.
     */
    i_index = 0;
    for( i = 5, i_term = 0 ; --i >= 0 ; i_term = p_scratch[i] )
    {
        i_index = p_bits[25 + i] ^ p_scratch[i];
        i_index = p_crypt_tab1[i_index] ^ ~p_crypt_tab2[i_index] ^ i_cse;

        p_tmp1[i] = p_crypt_tab2[i_index] ^ p_crypt_tab3[i_index] ^ i_term;
    }
    p_tmp1[4] ^= p_tmp1[0];

    for( i = 5, i_term = 0 ; --i >= 0 ; i_term = p_tmp1[i] )
    {
        i_index = p_bits[20 + i] ^ p_tmp1[i];
        i_index = p_crypt_tab1[i_index] ^ ~p_crypt_tab2[i_index] ^ i_cse;

        p_tmp2[i] = p_crypt_tab2[i_index] ^ p_crypt_tab3[i_index] ^ i_term;
    }
    p_tmp2[4] ^= p_tmp2[0];

    for( i = 5, i_term = 0 ; --i >= 0 ; i_term = p_tmp2[i] )
    {
        i_index = p_bits[15 + i] ^ p_tmp2[i];
        i_index = p_crypt_tab1[i_index] ^ ~p_crypt_tab2[i_index] ^ i_cse;
        i_index = p_crypt_tab2[i_index] ^ p_crypt_tab3[i_index] ^ i_term;

        p_tmp1[i] = p_crypt_tab0[i_index] ^ p_crypt_tab2[i_index];
    }
    p_tmp1[4] ^= p_tmp1[0];

    for( i = 5, i_term = 0 ; --i >= 0 ; i_term = p_tmp1[i] )
    {
        i_index = p_bits[10 + i] ^ p_tmp1[i];
        i_index = p_crypt_tab1[i_index] ^ ~p_crypt_tab2[i_index] ^ i_cse;

        i_index = p_crypt_tab2[i_index] ^ p_crypt_tab3[i_index] ^ i_term;

        p_tmp2[i] = p_crypt_tab0[i_index] ^ p_crypt_tab2[i_index];
    }
    p_tmp2[4] ^= p_tmp2[0];

    for( i = 5, i_term = 0 ; --i >= 0 ; i_term = p_tmp2[i] )
    {
        i_index = p_bits[5 + i] ^ p_tmp2[i];
        i_index = p_crypt_tab1[i_index] ^ ~p_crypt_tab2[i_index] ^ i_cse;

        p_tmp1[i] = p_crypt_tab2[i_index] ^ p_crypt_tab3[i_index] ^ i_term;
    }
    p_tmp1[4] ^= p_tmp1[0];

    for(i = 5, i_term = 0 ; --i >= 0 ; i_term = p_tmp1[i] )
    {
        i_index = p_bits[i] ^ p_tmp1[i];
        i_index = p_crypt_tab1[i_index] ^ ~p_crypt_tab2[i_index] ^ i_cse;

        p_key[i] = p_crypt_tab2[i_index] ^ p_crypt_tab3[i_index] ^ i_term;
    }

    return;
}

/*****************************************************************************
 * CSSCracker : title key decryption by cracking
 *****************************************************************************
 * This function is called by CSSGetKeys to find a key
 *****************************************************************************/
static int CSSCracker( int i_start,
                       unsigned char * p_crypted,
                       unsigned char * p_decrypted,
                       dvd_key_t * p_sector_key,
                       dvd_key_t * p_key )
{
    unsigned char p_buffer[10];
    unsigned int i_t1, i_t2, i_t3, i_t4, i_t5, i_t6;
    unsigned int i_try;
    unsigned int i_candidate;
    unsigned int i, j;
    int i_exit = -1;


    for( i = 0 ; i < 10 ; i++ )
    {
        p_buffer[i] = p_css_tab1[p_crypted[i]] ^ p_decrypted[i];
    }

    for( i_try = i_start ; i_try < 0x10000 ; i_try++ )
    {
        i_t1 = i_try >> 8 | 0x100;
        i_t2 = i_try & 0xff;
        i_t3 = 0;               /* not needed */
        i_t5 = 0;

        /* iterate cipher 4 times to reconstruct LFSR2 */
        for( i = 0 ; i < 4 ; i++ )
        {
            /* advance LFSR1 normaly */
            i_t4 = p_css_tab2[i_t2] ^ p_css_tab3[i_t1];
            i_t2 = i_t1 >> 1;
            i_t1 = ( ( i_t1 & 1 ) << 8 ) ^ i_t4;
            i_t4 = p_css_tab5[i_t4];
            /* deduce i_t6 & i_t5 */
            i_t6 = p_buffer[i];
            if( i_t5 )
            {
                i_t6 = ( i_t6 + 0xff ) & 0x0ff;
            }
            if( i_t6 < i_t4 )
            {
                i_t6 += 0x100;
            }
            i_t6 -= i_t4;
            i_t5 += i_t6 + i_t4;
            i_t6 = p_css_tab4[ i_t6 ];
            /* feed / advance i_t3 / i_t5 */
            i_t3 = ( i_t3 << 8 ) | i_t6;
            i_t5 >>= 8;
        }

        i_candidate = i_t3;

        /* iterate 6 more times to validate candidate key */
        for( ; i < 10 ; i++ )
        {
            i_t4 = p_css_tab2[i_t2] ^ p_css_tab3[i_t1];
            i_t2 = i_t1 >> 1;
            i_t1 = ( ( i_t1 & 1 ) << 8 ) ^ i_t4;
            i_t4 = p_css_tab5[i_t4];
            i_t6 = ((((((( i_t3 >> 3 ) ^ i_t3 ) >> 1 ) ^
                                         i_t3 ) >> 8 ) ^ i_t3 ) >> 5 ) & 0xff;
            i_t3 = ( i_t3 << 8 ) | i_t6;
            i_t6 = p_css_tab4[i_t6];
            i_t5 += i_t6 + i_t4;
            if( ( i_t5 & 0xff ) != p_buffer[i] )
            {
                break;
            }

            i_t5 >>= 8;
        }

        if( i == 10 )
        {
            /* Do 4 backwards steps of iterating t3 to deduce initial state */
            i_t3 = i_candidate;
            for( i = 0 ; i < 4 ; i++ )
            {
                i_t1 = i_t3 & 0xff;
                i_t3 = ( i_t3 >> 8 );
                /* easy to code, and fast enough bruteforce
                 * search for byte shifted in */
                for( j = 0 ; j < 256 ; j++ )
                {
                    i_t3 = ( i_t3 & 0x1ffff) | ( j << 17 );
                    i_t6 = ((((((( i_t3 >> 3 ) ^ i_t3 ) >> 1 ) ^
                                   i_t3 ) >> 8 ) ^ i_t3 ) >> 5 ) & 0xff;
                    if( i_t6 == i_t1 )
                    {
                        break;
                    }
                }
            }

            i_t4 = ( i_t3 >> 1 ) - 4;
            for( i_t5 = 0 ; i_t5 < 8; i_t5++ )
            {
                if( ( ( i_t4 + i_t5 ) * 2 + 8 - ( (i_t4 + i_t5 ) & 7 ) )
                                                                      == i_t3 )
                {
                    (*p_key)[0] = i_try>>8;
                    (*p_key)[1] = i_try & 0xFF;
                    (*p_key)[2] = ( ( i_t4 + i_t5 ) >> 0) & 0xFF;
                    (*p_key)[3] = ( ( i_t4 + i_t5 ) >> 8) & 0xFF;
                    (*p_key)[4] = ( ( i_t4 + i_t5 ) >> 16) & 0xFF;
                    i_exit = i_try + 1;
                }
            }
        }
    }

    if( i_exit >= 0 )
    {
        (*p_key)[0] ^= (*p_sector_key)[0];
        (*p_key)[1] ^= (*p_sector_key)[1];
        (*p_key)[2] ^= (*p_sector_key)[2];
        (*p_key)[3] ^= (*p_sector_key)[3];
        (*p_key)[4] ^= (*p_sector_key)[4];
    }

    return i_exit;
}


--- NEW FILE ---
/*****************************************************************************
 * css.h: Structures for DVD authentification and unscrambling
 *****************************************************************************
 * Copyright (C) 1999-2001 VideoLAN
 * $Id: css.h,v 1.1 2002/04/24 19:28:03 arpi Exp $
 *
 * Author: Stéphane Borel <stef at via.ecp.fr>
 *
 * based on:
 *  - css-auth by Derek Fawcus <derek at spider.com>
 *  - DVD CSS ioctls example program by Andrew T. Veliath <andrewtv at usa.net>
 *  - DeCSSPlus by Ethan Hawke
 *  - The Divide and conquer attack by Frank A. Stevenson <frank at funcom.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/
#define KEY_SIZE 5

typedef u8 dvd_key_t[KEY_SIZE];

typedef struct disc_s
{
    u8              p_challenge[2*KEY_SIZE];
    dvd_key_t       p_key1;
    dvd_key_t       p_key2;
    dvd_key_t       p_key_check;
    u8              i_varient;
} disc_t;

typedef struct dvd_title_s
{
    int                 i_startlb;
    dvd_key_t           p_key;
    struct dvd_title_s *p_next;
} dvd_title_t;

typedef struct css_s
{
    int             i_agid;
    disc_t          disc;
    u8              p_disc_key[2048];
} css_t;

/*****************************************************************************
 * Prototypes in css.c
 *****************************************************************************/
struct css_s;

int   CSSTest             ( dvdcss_handle );
int   CSSInit             ( dvdcss_handle );
int   CSSGetKey           ( dvdcss_handle, int, dvd_key_t );
int   CSSDescrambleSector ( u8 * , u8 * );


--- NEW FILE ---
/*****************************************************************************
 * csstables.h: CSS Tables for DVD unscrambling
 *****************************************************************************
 * Copyright (C) 1999-2001 VideoLAN
 * $Id: csstables.h,v 1.1 2002/04/24 19:28:03 arpi Exp $
 *
 * Author: Stéphane Borel <stef at via.ecp.fr>
 *
 * based on:
 *  - css-auth by Derek Fawcus <derek at spider.com>
 *  - DVD CSS ioctls example program by Andrew T. Veliath <andrewtv at usa.net>
 *  - The Divide and conquer attack by Frank A. Stevenson <frank at funcom.com>
 *  - DeCSSPlus by Ethan Hawke
 *  - DecVOB
 *  see http://www.lemuria.org/DeCSS/ by Tom Vogt for more information.
 * 
 * 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.
 *****************************************************************************/

static u8 p_css_tab1[ 256 ] =
{
    0x33, 0x73, 0x3b, 0x26, 0x63, 0x23, 0x6b, 0x76,
    0x3e, 0x7e, 0x36, 0x2b, 0x6e, 0x2e, 0x66, 0x7b,
    0xd3, 0x93, 0xdb, 0x06, 0x43, 0x03, 0x4b, 0x96,
    0xde, 0x9e, 0xd6, 0x0b, 0x4e, 0x0e, 0x46, 0x9b,
    0x57, 0x17, 0x5f, 0x82, 0xc7, 0x87, 0xcf, 0x12,
    0x5a, 0x1a, 0x52, 0x8f, 0xca, 0x8a, 0xc2, 0x1f,
    0xd9, 0x99, 0xd1, 0x00, 0x49, 0x09, 0x41, 0x90,
    0xd8, 0x98, 0xd0, 0x01, 0x48, 0x08, 0x40, 0x91,
    0x3d, 0x7d, 0x35, 0x24, 0x6d, 0x2d, 0x65, 0x74,
    0x3c, 0x7c, 0x34, 0x25, 0x6c, 0x2c, 0x64, 0x75,
    0xdd, 0x9d, 0xd5, 0x04, 0x4d, 0x0d, 0x45, 0x94,
    0xdc, 0x9c, 0xd4, 0x05, 0x4c, 0x0c, 0x44, 0x95,
    0x59, 0x19, 0x51, 0x80, 0xc9, 0x89, 0xc1, 0x10,
    0x58, 0x18, 0x50, 0x81, 0xc8, 0x88, 0xc0, 0x11,
    0xd7, 0x97, 0xdf, 0x02, 0x47, 0x07, 0x4f, 0x92,
    0xda, 0x9a, 0xd2, 0x0f, 0x4a, 0x0a, 0x42, 0x9f,
    0x53, 0x13, 0x5b, 0x86, 0xc3, 0x83, 0xcb, 0x16,
    0x5e, 0x1e, 0x56, 0x8b, 0xce, 0x8e, 0xc6, 0x1b,
    0xb3, 0xf3, 0xbb, 0xa6, 0xe3, 0xa3, 0xeb, 0xf6,
    0xbe, 0xfe, 0xb6, 0xab, 0xee, 0xae, 0xe6, 0xfb,
    0x37, 0x77, 0x3f, 0x22, 0x67, 0x27, 0x6f, 0x72,
    0x3a, 0x7a, 0x32, 0x2f, 0x6a, 0x2a, 0x62, 0x7f,
    0xb9, 0xf9, 0xb1, 0xa0, 0xe9, 0xa9, 0xe1, 0xf0,
    0xb8, 0xf8, 0xb0, 0xa1, 0xe8, 0xa8, 0xe0, 0xf1,
    0x5d, 0x1d, 0x55, 0x84, 0xcd, 0x8d, 0xc5, 0x14,
    0x5c, 0x1c, 0x54, 0x85, 0xcc, 0x8c, 0xc4, 0x15,
    0xbd, 0xfd, 0xb5, 0xa4, 0xed, 0xad, 0xe5, 0xf4,
    0xbc, 0xfc, 0xb4, 0xa5, 0xec, 0xac, 0xe4, 0xf5,
    0x39, 0x79, 0x31, 0x20, 0x69, 0x29, 0x61, 0x70,
    0x38, 0x78, 0x30, 0x21, 0x68, 0x28, 0x60, 0x71,
    0xb7, 0xf7, 0xbf, 0xa2, 0xe7, 0xa7, 0xef, 0xf2,
    0xba, 0xfa, 0xb2, 0xaf, 0xea, 0xaa, 0xe2, 0xff
};

static u8 p_css_tab2[ 256 ] =
{
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x09, 0x08, 0x0b, 0x0a, 0x0d, 0x0c, 0x0f, 0x0e,
    0x12, 0x13, 0x10, 0x11, 0x16, 0x17, 0x14, 0x15,
    0x1b, 0x1a, 0x19, 0x18, 0x1f, 0x1e, 0x1d, 0x1c,
    0x24, 0x25, 0x26, 0x27, 0x20, 0x21, 0x22, 0x23,
    0x2d, 0x2c, 0x2f, 0x2e, 0x29, 0x28, 0x2b, 0x2a,
    0x36, 0x37, 0x34, 0x35, 0x32, 0x33, 0x30, 0x31,
    0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38,
    0x49, 0x48, 0x4b, 0x4a, 0x4d, 0x4c, 0x4f, 0x4e,
    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
    0x5b, 0x5a, 0x59, 0x58, 0x5f, 0x5e, 0x5d, 0x5c,
    0x52, 0x53, 0x50, 0x51, 0x56, 0x57, 0x54, 0x55,
    0x6d, 0x6c, 0x6f, 0x6e, 0x69, 0x68, 0x6b, 0x6a,
    0x64, 0x65, 0x66, 0x67, 0x60, 0x61, 0x62, 0x63,
    0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78,
    0x76, 0x77, 0x74, 0x75, 0x72, 0x73, 0x70, 0x71,
    0x92, 0x93, 0x90, 0x91, 0x96, 0x97, 0x94, 0x95,
    0x9b, 0x9a, 0x99, 0x98, 0x9f, 0x9e, 0x9d, 0x9c,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
    0x89, 0x88, 0x8b, 0x8a, 0x8d, 0x8c, 0x8f, 0x8e,
    0xb6, 0xb7, 0xb4, 0xb5, 0xb2, 0xb3, 0xb0, 0xb1,
    0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8,
    0xa4, 0xa5, 0xa6, 0xa7, 0xa0, 0xa1, 0xa2, 0xa3,
    0xad, 0xac, 0xaf, 0xae, 0xa9, 0xa8, 0xab, 0xaa,
    0xdb, 0xda, 0xd9, 0xd8, 0xdf, 0xde, 0xdd, 0xdc,
    0xd2, 0xd3, 0xd0, 0xd1, 0xd6, 0xd7, 0xd4, 0xd5,
    0xc9, 0xc8, 0xcb, 0xca, 0xcd, 0xcc, 0xcf, 0xce,
    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
    0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
    0xf6, 0xf7, 0xf4, 0xf5, 0xf2, 0xf3, 0xf0, 0xf1,
    0xed, 0xec, 0xef, 0xee, 0xe9, 0xe8, 0xeb, 0xea,
    0xe4, 0xe5, 0xe6, 0xe7, 0xe0, 0xe1, 0xe2, 0xe3
};

static u8 p_css_tab3[ 512 ] =
{
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff,
    0x00, 0x24, 0x49, 0x6d, 0x92, 0xb6, 0xdb, 0xff
};

static u8 p_css_tab4[ 256 ] =
{
    0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
    0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
    0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
    0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
    0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
    0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
    0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
    0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
    0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
    0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
    0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
    0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
    0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
    0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
    0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
    0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
    0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
    0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
    0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
    0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
    0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
    0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
    0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
    0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
    0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
    0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
    0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
    0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
    0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
    0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
    0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
    0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
};

static u8 p_css_tab5[ 256 ] =
{
    0xff, 0x7f, 0xbf, 0x3f, 0xdf, 0x5f, 0x9f, 0x1f,
    0xef, 0x6f, 0xaf, 0x2f, 0xcf, 0x4f, 0x8f, 0x0f,
    0xf7, 0x77, 0xb7, 0x37, 0xd7, 0x57, 0x97, 0x17,
    0xe7, 0x67, 0xa7, 0x27, 0xc7, 0x47, 0x87, 0x07,
    0xfb, 0x7b, 0xbb, 0x3b, 0xdb, 0x5b, 0x9b, 0x1b,
    0xeb, 0x6b, 0xab, 0x2b, 0xcb, 0x4b, 0x8b, 0x0b,
    0xf3, 0x73, 0xb3, 0x33, 0xd3, 0x53, 0x93, 0x13,
    0xe3, 0x63, 0xa3, 0x23, 0xc3, 0x43, 0x83, 0x03,
    0xfd, 0x7d, 0xbd, 0x3d, 0xdd, 0x5d, 0x9d, 0x1d,
    0xed, 0x6d, 0xad, 0x2d, 0xcd, 0x4d, 0x8d, 0x0d,
    0xf5, 0x75, 0xb5, 0x35, 0xd5, 0x55, 0x95, 0x15,
    0xe5, 0x65, 0xa5, 0x25, 0xc5, 0x45, 0x85, 0x05,
    0xf9, 0x79, 0xb9, 0x39, 0xd9, 0x59, 0x99, 0x19,
    0xe9, 0x69, 0xa9, 0x29, 0xc9, 0x49, 0x89, 0x09,
    0xf1, 0x71, 0xb1, 0x31, 0xd1, 0x51, 0x91, 0x11,
    0xe1, 0x61, 0xa1, 0x21, 0xc1, 0x41, 0x81, 0x01,
    0xfe, 0x7e, 0xbe, 0x3e, 0xde, 0x5e, 0x9e, 0x1e,
    0xee, 0x6e, 0xae, 0x2e, 0xce, 0x4e, 0x8e, 0x0e,
    0xf6, 0x76, 0xb6, 0x36, 0xd6, 0x56, 0x96, 0x16,
    0xe6, 0x66, 0xa6, 0x26, 0xc6, 0x46, 0x86, 0x06,
    0xfa, 0x7a, 0xba, 0x3a, 0xda, 0x5a, 0x9a, 0x1a,
    0xea, 0x6a, 0xaa, 0x2a, 0xca, 0x4a, 0x8a, 0x0a,
    0xf2, 0x72, 0xb2, 0x32, 0xd2, 0x52, 0x92, 0x12,
    0xe2, 0x62, 0xa2, 0x22, 0xc2, 0x42, 0x82, 0x02,
    0xfc, 0x7c, 0xbc, 0x3c, 0xdc, 0x5c, 0x9c, 0x1c,
    0xec, 0x6c, 0xac, 0x2c, 0xcc, 0x4c, 0x8c, 0x0c,
    0xf4, 0x74, 0xb4, 0x34, 0xd4, 0x54, 0x94, 0x14,
    0xe4, 0x64, 0xa4, 0x24, 0xc4, 0x44, 0x84, 0x04,
    0xf8, 0x78, 0xb8, 0x38, 0xd8, 0x58, 0x98, 0x18,
    0xe8, 0x68, 0xa8, 0x28, 0xc8, 0x48, 0x88, 0x08,
    0xf0, 0x70, 0xb0, 0x30, 0xd0, 0x50, 0x90, 0x10,
    0xe0, 0x60, 0xa0, 0x20, 0xc0, 0x40, 0x80, 0x00
};

static u8 p_crypt_tab0[ 256 ] =
{
    0xB7, 0xF4, 0x82, 0x57, 0xDA, 0x4D, 0xDB, 0xE2,
    0x2F, 0x52, 0x1A, 0xA8, 0x68, 0x5A, 0x8A, 0xFF,
    0xFB, 0x0E, 0x6D, 0x35, 0xF7, 0x5C, 0x76, 0x12,
    0xCE, 0x25, 0x79, 0x29, 0x39, 0x62, 0x08, 0x24,
    0xA5, 0x85, 0x7B, 0x56, 0x01, 0x23, 0x68, 0xCF,
    0x0A, 0xE2, 0x5A, 0xED, 0x3D, 0x59, 0xB0, 0xA9,
    0xB0, 0x2C, 0xF2, 0xB8, 0xEF, 0x32, 0xA9, 0x40,
    0x80, 0x71, 0xAF, 0x1E, 0xDE, 0x8F, 0x58, 0x88,
    0xB8, 0x3A, 0xD0, 0xFC, 0xC4, 0x1E, 0xB5, 0xA0,
    0xBB, 0x3B, 0x0F, 0x01, 0x7E, 0x1F, 0x9F, 0xD9,
    0xAA, 0xB8, 0x3D, 0x9D, 0x74, 0x1E, 0x25, 0xDB,
    0x37, 0x56, 0x8F, 0x16, 0xBA, 0x49, 0x2B, 0xAC,
    0xD0, 0xBD, 0x95, 0x20, 0xBE, 0x7A, 0x28, 0xD0,
    0x51, 0x64, 0x63, 0x1C, 0x7F, 0x66, 0x10, 0xBB,
    0xC4, 0x56, 0x1A, 0x04, 0x6E, 0x0A, 0xEC, 0x9C,
    0xD6, 0xE8, 0x9A, 0x7A, 0xCF, 0x8C, 0xDB, 0xB1,
    0xEF, 0x71, 0xDE, 0x31, 0xFF, 0x54, 0x3E, 0x5E,
    0x07, 0x69, 0x96, 0xB0, 0xCF, 0xDD, 0x9E, 0x47,
    0xC7, 0x96, 0x8F, 0xE4, 0x2B, 0x59, 0xC6, 0xEE,
    0xB9, 0x86, 0x9A, 0x64, 0x84, 0x72, 0xE2, 0x5B,
    0xA2, 0x96, 0x58, 0x99, 0x50, 0x03, 0xF5, 0x38,
    0x4D, 0x02, 0x7D, 0xE7, 0x7D, 0x75, 0xA7, 0xB8,
    0x67, 0x87, 0x84, 0x3F, 0x1D, 0x11, 0xE5, 0xFC,
    0x1E, 0xD3, 0x83, 0x16, 0xA5, 0x29, 0xF6, 0xC7,
    0x15, 0x61, 0x29, 0x1A, 0x43, 0x4F, 0x9B, 0xAF,
    0xC5, 0x87, 0x34, 0x6C, 0x0F, 0x3B, 0xA8, 0x1D,
    0x45, 0x58, 0x25, 0xDC, 0xA8, 0xA3, 0x3B, 0xD1,
    0x79, 0x1B, 0x48, 0xF2, 0xE9, 0x93, 0x1F, 0xFC,
    0xDB, 0x2A, 0x90, 0xA9, 0x8A, 0x3D, 0x39, 0x18,
    0xA3, 0x8E, 0x58, 0x6C, 0xE0, 0x12, 0xBB, 0x25,
    0xCD, 0x71, 0x22, 0xA2, 0x64, 0xC6, 0xE7, 0xFB,
    0xAD, 0x94, 0x77, 0x04, 0x9A, 0x39, 0xCF, 0x7C
};

static u8 p_crypt_tab1[ 256 ] =
{
    0x8C, 0x47, 0xB0, 0xE1, 0xEB, 0xFC, 0xEB, 0x56,
    0x10, 0xE5, 0x2C, 0x1A, 0x5D, 0xEF, 0xBE, 0x4F,
    0x08, 0x75, 0x97, 0x4B, 0x0E, 0x25, 0x8E, 0x6E,
    0x39, 0x5A, 0x87, 0x53, 0xC4, 0x1F, 0xF4, 0x5C,
    0x4E, 0xE6, 0x99, 0x30, 0xE0, 0x42, 0x88, 0xAB,
    0xE5, 0x85, 0xBC, 0x8F, 0xD8, 0x3C, 0x54, 0xC9,
    0x53, 0x47, 0x18, 0xD6, 0x06, 0x5B, 0x41, 0x2C,
    0x67, 0x1E, 0x41, 0x74, 0x33, 0xE2, 0xB4, 0xE0,
    0x23, 0x29, 0x42, 0xEA, 0x55, 0x0F, 0x25, 0xB4,
    0x24, 0x2C, 0x99, 0x13, 0xEB, 0x0A, 0x0B, 0xC9,
    0xF9, 0x63, 0x67, 0x43, 0x2D, 0xC7, 0x7D, 0x07,
    0x60, 0x89, 0xD1, 0xCC, 0xE7, 0x94, 0x77, 0x74,
    0x9B, 0x7E, 0xD7, 0xE6, 0xFF, 0xBB, 0x68, 0x14,
    0x1E, 0xA3, 0x25, 0xDE, 0x3A, 0xA3, 0x54, 0x7B,
    0x87, 0x9D, 0x50, 0xCA, 0x27, 0xC3, 0xA4, 0x50,
    0x91, 0x27, 0xD4, 0xB0, 0x82, 0x41, 0x97, 0x79,
    0x94, 0x82, 0xAC, 0xC7, 0x8E, 0xA5, 0x4E, 0xAA,
    0x78, 0x9E, 0xE0, 0x42, 0xBA, 0x28, 0xEA, 0xB7,
    0x74, 0xAD, 0x35, 0xDA, 0x92, 0x60, 0x7E, 0xD2,
    0x0E, 0xB9, 0x24, 0x5E, 0x39, 0x4F, 0x5E, 0x63,
    0x09, 0xB5, 0xFA, 0xBF, 0xF1, 0x22, 0x55, 0x1C,
    0xE2, 0x25, 0xDB, 0xC5, 0xD8, 0x50, 0x03, 0x98,
    0xC4, 0xAC, 0x2E, 0x11, 0xB4, 0x38, 0x4D, 0xD0,
    0xB9, 0xFC, 0x2D, 0x3C, 0x08, 0x04, 0x5A, 0xEF,
    0xCE, 0x32, 0xFB, 0x4C, 0x92, 0x1E, 0x4B, 0xFB,
    0x1A, 0xD0, 0xE2, 0x3E, 0xDA, 0x6E, 0x7C, 0x4D,
    0x56, 0xC3, 0x3F, 0x42, 0xB1, 0x3A, 0x23, 0x4D,
    0x6E, 0x84, 0x56, 0x68, 0xF4, 0x0E, 0x03, 0x64,
    0xD0, 0xA9, 0x92, 0x2F, 0x8B, 0xBC, 0x39, 0x9C,
    0xAC, 0x09, 0x5E, 0xEE, 0xE5, 0x97, 0xBF, 0xA5,
    0xCE, 0xFA, 0x28, 0x2C, 0x6D, 0x4F, 0xEF, 0x77,
    0xAA, 0x1B, 0x79, 0x8E, 0x97, 0xB4, 0xC3, 0xF4
};

static u8 p_crypt_tab2[ 256 ] =
{
    0xB7, 0x75, 0x81, 0xD5, 0xDC, 0xCA, 0xDE, 0x66,
    0x23, 0xDF, 0x15, 0x26, 0x62, 0xD1, 0x83, 0x77,
    0xE3, 0x97, 0x76, 0xAF, 0xE9, 0xC3, 0x6B, 0x8E,
    0xDA, 0xB0, 0x6E, 0xBF, 0x2B, 0xF1, 0x19, 0xB4,
    0x95, 0x34, 0x48, 0xE4, 0x37, 0x94, 0x5D, 0x7B,
    0x36, 0x5F, 0x65, 0x53, 0x07, 0xE2, 0x89, 0x11,
    0x98, 0x85, 0xD9, 0x12, 0xC1, 0x9D, 0x84, 0xEC,
    0xA4, 0xD4, 0x88, 0xB8, 0xFC, 0x2C, 0x79, 0x28,
    0xD8, 0xDB, 0xB3, 0x1E, 0xA2, 0xF9, 0xD0, 0x44,
    0xD7, 0xD6, 0x60, 0xEF, 0x14, 0xF4, 0xF6, 0x31,
    0xD2, 0x41, 0x46, 0x67, 0x0A, 0xE1, 0x58, 0x27,
    0x43, 0xA3, 0xF8, 0xE0, 0xC8, 0xBA, 0x5A, 0x5C,
    0x80, 0x6C, 0xC6, 0xF2, 0xE8, 0xAD, 0x7D, 0x04,
    0x0D, 0xB9, 0x3C, 0xC2, 0x25, 0xBD, 0x49, 0x63,
    0x8C, 0x9F, 0x51, 0xCE, 0x20, 0xC5, 0xA1, 0x50,
    0x92, 0x2D, 0xDD, 0xBC, 0x8D, 0x4F, 0x9A, 0x71,
    0x2F, 0x30, 0x1D, 0x73, 0x39, 0x13, 0xFB, 0x1A,
    0xCB, 0x24, 0x59, 0xFE, 0x05, 0x96, 0x57, 0x0F,
    0x1F, 0xCF, 0x54, 0xBE, 0xF5, 0x06, 0x1B, 0xB2,
    0x6D, 0xD3, 0x4D, 0x32, 0x56, 0x21, 0x33, 0x0B,
    0x52, 0xE7, 0xAB, 0xEB, 0xA6, 0x74, 0x00, 0x4C,
    0xB1, 0x7F, 0x82, 0x99, 0x87, 0x0E, 0x5E, 0xC0,
    0x8F, 0xEE, 0x6F, 0x55, 0xF3, 0x7E, 0x08, 0x90,
    0xFA, 0xB6, 0x64, 0x70, 0x47, 0x4A, 0x17, 0xA7,
    0xB5, 0x40, 0x8A, 0x38, 0xE5, 0x68, 0x3E, 0x8B,
    0x69, 0xAA, 0x9B, 0x42, 0xA5, 0x10, 0x01, 0x35,
    0xFD, 0x61, 0x9E, 0xE6, 0x16, 0x9C, 0x86, 0xED,
    0xCD, 0x2E, 0xFF, 0xC4, 0x5B, 0xA0, 0xAE, 0xCC,
    0x4B, 0x3B, 0x03, 0xBB, 0x1C, 0x2A, 0xAC, 0x0C,
    0x3F, 0x93, 0xC7, 0x72, 0x7A, 0x09, 0x22, 0x3D,
    0x45, 0x78, 0xA9, 0xA8, 0xEA, 0xC9, 0x6A, 0xF7,
    0x29, 0x91, 0xF0, 0x02, 0x18, 0x3A, 0x4E, 0x7C 
};

static u8 p_crypt_tab3[ 288 ] =
{
    0x73, 0x51, 0x95, 0xE1, 0x12, 0xE4, 0xC0, 0x58,
    0xEE, 0xF2, 0x08, 0x1B, 0xA9, 0xFA, 0x98, 0x4C,
    0xA7, 0x33, 0xE2, 0x1B, 0xA7, 0x6D, 0xF5, 0x30,
    0x97, 0x1D, 0xF3, 0x02, 0x60, 0x5A, 0x82, 0x0F,
    0x91, 0xD0, 0x9C, 0x10, 0x39, 0x7A, 0x83, 0x85,
    0x3B, 0xB2, 0xB8, 0xAE, 0x0C, 0x09, 0x52, 0xEA,
    0x1C, 0xE1, 0x8D, 0x66, 0x4F, 0xF3, 0xDA, 0x92,
    0x29, 0xB9, 0xD5, 0xC5, 0x77, 0x47, 0x22, 0x53,
    0x14, 0xF7, 0xAF, 0x22, 0x64, 0xDF, 0xC6, 0x72,
    0x12, 0xF3, 0x75, 0xDA, 0xD7, 0xD7, 0xE5, 0x02,
    0x9E, 0xED, 0xDA, 0xDB, 0x4C, 0x47, 0xCE, 0x91,
    0x06, 0x06, 0x6D, 0x55, 0x8B, 0x19, 0xC9, 0xEF,
    0x8C, 0x80, 0x1A, 0x0E, 0xEE, 0x4B, 0xAB, 0xF2,
    0x08, 0x5C, 0xE9, 0x37, 0x26, 0x5E, 0x9A, 0x90,
    0x00, 0xF3, 0x0D, 0xB2, 0xA6, 0xA3, 0xF7, 0x26,
    0x17, 0x48, 0x88, 0xC9, 0x0E, 0x2C, 0xC9, 0x02,
    0xE7, 0x18, 0x05, 0x4B, 0xF3, 0x39, 0xE1, 0x20,
    0x02, 0x0D, 0x40, 0xC7, 0xCA, 0xB9, 0x48, 0x30,
    0x57, 0x67, 0xCC, 0x06, 0xBF, 0xAC, 0x81, 0x08,
    0x24, 0x7A, 0xD4, 0x8B, 0x19, 0x8E, 0xAC, 0xB4,
    0x5A, 0x0F, 0x73, 0x13, 0xAC, 0x9E, 0xDA, 0xB6,
    0xB8, 0x96, 0x5B, 0x60, 0x88, 0xE1, 0x81, 0x3F,
    0x07, 0x86, 0x37, 0x2D, 0x79, 0x14, 0x52, 0xEA,
    0x73, 0xDF, 0x3D, 0x09, 0xC8, 0x25, 0x48, 0xD8,
    0x75, 0x60, 0x9A, 0x08, 0x27, 0x4A, 0x2C, 0xB9,
    0xA8, 0x8B, 0x8A, 0x73, 0x62, 0x37, 0x16, 0x02,
    0xBD, 0xC1, 0x0E, 0x56, 0x54, 0x3E, 0x14, 0x5F,
    0x8C, 0x8F, 0x6E, 0x75, 0x1C, 0x07, 0x39, 0x7B,
    0x4B, 0xDB, 0xD3, 0x4B, 0x1E, 0xC8, 0x7E, 0xFE,
    0x3E, 0x72, 0x16, 0x83, 0x7D, 0xEE, 0xF5, 0xCA,
    0xC5, 0x18, 0xF9, 0xD8, 0x68, 0xAB, 0x38, 0x85,
    0xA8, 0xF0, 0xA1, 0x73, 0x9F, 0x5D, 0x19, 0x0B,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x33, 0x72, 0x39, 0x25, 0x67, 0x26, 0x6D, 0x71,
    0x36, 0x77, 0x3C, 0x20, 0x62, 0x23, 0x68, 0x74,
    0xC3, 0x82, 0xC9, 0x15, 0x57, 0x16, 0x5D, 0x81
};


--- NEW FILE ---
/**
 * Copyright (C) 2001 Billy Biggs <vektor at dumbterm.net>.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h> /* For the timing of dvdcss_title crack. */
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <dirent.h>

#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__)
#define SYS_BSD 1
#endif

#if defined(__sun)
#include <sys/mnttab.h>
#elif defined(SYS_BSD)
#include <fstab.h>
#elif defined(__linux__)
#include <mntent.h>
#endif

#if defined(SYS_BSD)
typedef off_t off64_t;
#define lseek64 lseek
#define stat64 stat
#endif

/* #include "dvdcss.h" */
typedef struct dvdcss_s* dvdcss_handle;
#define DVDCSS_NOFLAGS         0
#define DVDCSS_INIT_QUIET      (1 << 0)
#define DVDCSS_INIT_DEBUG      (1 << 1)
#define DVDCSS_READ_DECRYPT    (1 << 0)

#include "dvd_udf.h"
#include "dvd_reader.h"

struct dvd_reader_s {
    /* Basic information. */
    int isImageFile;

    /* Information required for an image file. */
    dvdcss_handle dev;
    int init_keys;
    int fd;

    /* Information required for a directory path drive. */
    char *path_root;
};

struct dvd_file_s {
    /* Basic information. */
    dvd_reader_t *dvd;

    /* Information required for an image file. */
    uint32_t lb_start;
    uint32_t seek_pos;

    /* Information required for a directory path drive. */
    size_t title_sizes[ 9 ];
    int title_fds[ 9 ];

    /* Calculated at open-time, size in blocks. */
    ssize_t filesize;
};

/* Loop over all titles and call dvdcss_title to crack the keys. */
static int initAllCSSKeys( dvd_reader_t *dvd )
{
    
        struct timeval all_s, all_e;
	struct timeval t_s, t_e;
	char filename[ MAX_UDF_FILE_NAME_LEN ];
	uint32_t start, len;
	int title;
	
	fprintf( stderr, "\n" );
	fprintf( stderr, "libdvdread: Attempting to retrieve all CSS keys\n" );
	fprintf( stderr, "libdvdread: This can take a _long_ time, "
		 "please be patient\n\n" );
	
	gettimeofday(&all_s, NULL);
	
	for( title = 0; title < 100; title++ ) {
	    gettimeofday( &t_s, NULL );
	    if( title == 0 ) {
	        sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
	    } else {
	        sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 0 );
	    }
	    start = UDFFindFile( dvd, filename, &len );
	    if( start != 0 && len != 0 ) {
	        /* Perform CSS key cracking for this title. */
	        fprintf( stderr, "libdvdread: Get key for %s at 0x%08x\n", 
			 filename, start );
		if( dvdcss_title( dvd->dev, (int)start ) < 0 ) {
		    fprintf( stderr, "libdvdread: Error cracking CSS key!!\n");
		}
		gettimeofday( &t_e, NULL );
		fprintf( stderr, "libdvdread: Elapsed time %ld\n",  
			 (long int) t_e.tv_sec - t_s.tv_sec );
	    }
	    
	    if( title == 0 ) continue;
	    
	    gettimeofday( &t_s, NULL );
	    sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 1 );
	    start = UDFFindFile( dvd, filename, &len );
	    if( start == 0 || len == 0 ) break;
	    
	    /* Perform CSS key cracking for this title. */
	    fprintf( stderr, "libdvdread: Get key for %s at 0x%08x\n", 
		     filename, start );
	    if( dvdcss_title( dvd->dev, (int)start ) < 0 ) {
	        fprintf( stderr, "libdvdread: Error cracking CSS key!!\n");
	    }
	    gettimeofday( &t_e, NULL );
	    fprintf( stderr, "libdvdread: Elapsed time %ld\n",  
		     (long int) t_e.tv_sec - t_s.tv_sec );
	}
	title--;
	
	fprintf( stderr, "libdvdread: Found %d VTS's\n", title );
	gettimeofday(&all_e, NULL);
	fprintf( stderr, "libdvdread: Elapsed time %ld\n",  
		 (long int) all_e.tv_sec - all_s.tv_sec );
    return 0;
}



/**
 * Open a DVD image or block device file.
 */
static dvd_reader_t *DVDOpenImageFile( const char *location )
{
    dvd_reader_t *dvd;
    dvdcss_handle dev = 0;
    int fd = -1;
    
    dev = dvdcss_open( (char *) location, DVDCSS_INIT_DEBUG );
    if( !dev ) {
        fprintf( stderr, "libdvdread: Can't open %s for reading.\n",
                 location );
        return 0;
    }

    dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
    if( !dvd ) return 0;
    dvd->isImageFile = 1;
    dvd->dev = dev;
    dvd->init_keys = 0;
    dvd->fd = fd;
    dvd->path_root = 0;
    
    return dvd;
}

static dvd_reader_t *DVDOpenPath( const char *path_root )
{
    dvd_reader_t *dvd;

    dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
    if( !dvd ) return 0;
    dvd->isImageFile = 0;
    dvd->dev = 0;
    dvd->init_keys = 0;
    dvd->fd = -1;
    dvd->path_root = strdup( path_root );

    return dvd;
}

#if defined(__sun)
/* /dev/rdsk/c0t6d0s0 (link to /devices/...)
   /vol/dev/rdsk/c0t6d0/??
   /vol/rdsk/<name> */
static char *sun_block2char( const char *path )
{
    char *new_path;

    /* Must contain "/dsk/" */ 
    if( !strstr( path, "/dsk/" ) ) return (char *) strdup( path );

    /* Replace "/dsk/" with "/rdsk/" */
    new_path = malloc( strlen(path) + 2 );
    strcpy( new_path, path );
    strcpy( strstr( new_path, "/dsk/" ), "" );
    strcat( new_path, "/rdsk/" );
    strcat( new_path, strstr( path, "/dsk/" ) + strlen( "/dsk/" ) );

    return new_path;
}
#endif

#if defined(SYS_BSD)
/* FreeBSD /dev/(r)(a)cd0 (a is for atapi, should work without r)
   OpenBSD /dev/rcd0c
   NetBSD  /dev/rcd0d or /dev/rcd0c (for non x86)
   BSD/OS  /dev/sr0 (if not mounted) or /dev/rsr0 */
static char *bsd_block2char( const char *path )
{
    char *new_path;

    /* If it doesn't start with "/dev/" or does start with "/dev/r" exit */ 
    if( !strncmp( path, "/dev/",  5 ) || strncmp( path, "/dev/r", 6 ) ) 
      return (char *) strdup( path );

    /* Replace "/dev/" with "/dev/r" */
    new_path = malloc( strlen(path) + 2 );
    strcpy( new_path, "/dev/r" );
    strcat( new_path, path + strlen( "/dev/" ) );

    return new_path;
}
#endif

#ifndef HAVE_MPLAYER
 #include "get_path.c"
#else
 extern char * get_path( char * filename );
#endif

char * dvd_key_dir = NULL;

dvd_reader_t *DVDOpen( const char *path )
{
    struct stat64 fileinfo;
    int ret;
    char * dir;


    if( !path ) return 0;

    ret = stat64( path, &fileinfo );
    if( ret < 0 ) {
	/* If we can't stat the file, give up */
	fprintf( stderr, "libdvdread: Can't stat %s\n", path );
	perror("");
	return 0;
    }

    dir=get_path( "" );
    if ( dir ) { mkdir( dir,493 ); free( dir ); }
    dir=get_path( "DVDKeys" );
    if ( !dir ) dir=strdup( "/tmp" );
    mkdir( dir,493 );

    /* 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.
	 */
// ---
        char * device;
        FILE * img;
	char   discid[16];
#if defined(__sun)
	device = sun_block2char( path );
#elif defined(SYS_BSD)
	device = bsd_block2char( path );
#else
	device = path;
#endif

	if ( (img=fopen( device,"r" )) )
	 {
	  fseek( img,0x0000832d,SEEK_SET );
	  fread( &discid,16,1,img );
	  fclose( img );
	 } else strcpy( discid,"tmp" );

	if ( (dvd_key_dir=(char *)calloc( 1,strlen( dir ) + 17 )) == NULL ) return 0;
	sprintf( dvd_key_dir,"%s/%.16s",dir,discid );
	mkdir( dvd_key_dir,493 );

	free( dir );
	
        return DVDOpenImageFile( device );
// ---

    } else if( S_ISDIR( fileinfo.st_mode ) ) {
	dvd_reader_t *auth_drive = 0;
	char *path_copy;
#if defined(SYS_BSD)
	struct fstab* fe;
#elif defined(__sun) || defined(__linux__)
	FILE *mntfile;
#endif

	/* XXX: We should scream real loud here. */
	if( !(path_copy = strdup( path ) ) ) return 0;

	/* Resolve any symlinks and get the absolut dir name. */
	{
	    char *new_path;
	    int cdir = open( ".", O_RDONLY );
	    
	    if( cdir >= 0 ) {
		chdir( path_copy );
		new_path = getcwd( NULL, PATH_MAX );
		fchdir( cdir );
		close( cdir );
		if( new_path ) {
		    free( path_copy );
		    path_copy = new_path;
		}
	    }
	}

	/**
	 * If we're being asked to open a directory, check if that directory
	 * is the mountpoint for a DVD-ROM which we can use instead.
	 */

	if( strlen( path_copy ) > 1 ) {
	    if( path[ strlen( path_copy ) - 1 ] == '/' ) 
		path_copy[ strlen( path_copy ) - 1 ] = '\0';
	}

	if( strlen( path_copy ) > 9 ) {
	    if( !strcasecmp( &(path_copy[ strlen( path_copy ) - 9 ]), 
			     "/video_ts" ) ) {
	      path_copy[ strlen( path_copy ) - 9 ] = '\0';
	    }
	}

#if defined(SYS_BSD)
	if( ( fe = getfsfile( path_copy ) ) ) {
	    char *dev_name = bsd_block2char( fe->fs_spec );
	    fprintf( stderr,
		     "libdvdread: Attempting to use device %s"
		     " mounted on %s for CSS authentication\n",
		     dev_name,
		     fe->fs_file );
	    auth_drive = DVDOpenImageFile( dev_name );
	    free( dev_name );
	}
#elif defined(__sun)
	mntfile = fopen( MNTTAB, "r" );
	if( mntfile ) {
	    struct mnttab mp;
	    int res;

	    while( ( res = getmntent( mntfile, &mp ) ) != -1 ) {
		if( res == 0 && !strcmp( mp.mnt_mountp, path_copy ) ) {
		    char *dev_name = sun_block2char( mp.mnt_special );
		    fprintf( stderr, 
			     "libdvdread: Attempting to use device %s"
			     " mounted on %s for CSS authentication\n",
			     dev_name,
			     mp.mnt_mountp );
		    auth_drive = DVDOpenImageFile( dev_name );
		    free( dev_name );
		    break;
		}
	    }
	    fclose( mntfile );
	}
#elif defined(__linux__)
        mntfile = fopen( MOUNTED, "r" );
        if( mntfile ) {
            struct mntent *me;
 
            while( ( me = getmntent( mntfile ) ) ) {
                if( !strcmp( me->mnt_dir, path_copy ) ) {
		    fprintf( stderr, 
			     "libdvdread: Attempting to use device %s"
                             " mounted on %s for CSS authentication\n",
                             me->mnt_fsname,
			     me->mnt_dir );
                    auth_drive = DVDOpenImageFile( me->mnt_fsname );
                    break;
                }
            }
            fclose( mntfile );
	}
#endif
	if( !auth_drive ) {
	    fprintf( stderr, "libdvdread: Device inaccessible, "
		     "CSS authentication not available.\n" );
	}

	free( path_copy );

// ---
	if ( (dvd_key_dir=(char *)calloc( 1,strlen( dir ) + 17 )) == NULL ) return 0;
	sprintf( dvd_key_dir,"%s/%.16s",dir,"tmp" );
	mkdir( dvd_key_dir,493 );

	free( dir );
// ---

        /**
         * If we've opened a drive, just use that.
         */
        if( auth_drive ) return auth_drive;

        /**
         * Otherwise, we now try to open the directory tree instead.
         */
	fprintf( stderr, "libdvdread: Using normal filesystem access.\n" );
        return DVDOpenPath( path );
    }

    /* If it's none of the above, screw it. */
    fprintf( stderr, "libdvdread: Could not open %s\n", path );
    return 0;
}

void DVDClose( dvd_reader_t *dvd )
{
    if ( dvd_key_dir ) free( dvd_key_dir );
    if( dvd ) {
        if( dvd->dev ) dvdcss_close( dvd->dev );
        if( dvd->fd >= 0 ) close( dvd->fd );
        if( dvd->path_root ) free( dvd->path_root );
        free( dvd );
        dvd = 0;
    }
}

/**
 * Open an unencrypted file on a DVD image file.
 */
static dvd_file_t *DVDOpenFileUDF( dvd_reader_t *dvd, char *filename )
{
    uint32_t start, len;
    dvd_file_t *dvd_file;

    start = UDFFindFile( dvd, filename, &len );
    if( !start ) return 0;

    dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
    if( !dvd_file ) return 0;
    dvd_file->dvd = dvd;
    dvd_file->lb_start = start;
    dvd_file->seek_pos = 0;
    memset( dvd_file->title_fds, -1, sizeof( dvd_file->title_fds ) );
    dvd_file->filesize = len / DVD_VIDEO_LB_LEN;

    return dvd_file;
}

/**
 * Searches for <file> in directory <path>, ignoring case.
 * Returns 0 and full filename in <filename>.
 *     or -1 on file not found.
 *     or -2 on path not found.
 */
static int findDirFile( const char *path, const char *file, char *filename ) 
{
    DIR *dir;
    struct dirent *ent;

    dir = opendir( path );
    if( !dir ) return -2;

    while( ( ent = readdir( dir ) ) != NULL ) {
        if( !strcasecmp( ent->d_name, file ) ) {
            sprintf( filename, "%s%s%s", path,
                     ( ( path[ strlen( path ) - 1 ] == '/' ) ? "" : "/" ),
                     ent->d_name );
            return 0;
        }
    }

    return -1;
}

static int findDVDFile( dvd_reader_t *dvd, const char *file, char *filename )
{
    char video_path[ PATH_MAX + 1 ];
    const char *nodirfile;
    int ret;

    /* Strip off the directory for our search */
    if( !strncasecmp( "/VIDEO_TS/", file, 10 ) ) {
        nodirfile = &(file[ 10 ]);
    } else {
        nodirfile = file;
    }

    ret = findDirFile( dvd->path_root, nodirfile, filename );
    if( ret < 0 ) {
        /* Try also with adding the path, just in case. */
        sprintf( video_path, "%s/VIDEO_TS/", dvd->path_root );
        ret = findDirFile( video_path, nodirfile, filename );
        if( ret < 0 ) {
            /* Try with the path, but in lower case. */
            sprintf( video_path, "%s/video_ts/", dvd->path_root );
            ret = findDirFile( video_path, nodirfile, filename );
            if( ret < 0 ) {
                return 0;
            }
        }
    }

    return 1;
}

/**
 * Open an unencrypted file from a DVD directory tree.
 */
static dvd_file_t *DVDOpenFilePath( dvd_reader_t *dvd, char *filename )
{
    char full_path[ PATH_MAX + 1 ];
    dvd_file_t *dvd_file;
    struct stat fileinfo;
    int fd;

    /* Get the full path of the file. */
    if( !findDVDFile( dvd, filename, full_path ) ) return 0;

    fd = open( full_path, O_RDONLY );
    if( fd < 0 ) return 0;

    dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
    if( !dvd_file ) return 0;
    dvd_file->dvd = dvd;
    dvd_file->lb_start = 0;
    dvd_file->seek_pos = 0;
    memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
    memset( dvd_file->title_fds, -1, sizeof( dvd_file->title_fds ) );
    dvd_file->filesize = 0;

    if( stat( full_path, &fileinfo ) < 0 ) {
        fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
        free( dvd_file );
        return 0;
    }
    dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
    dvd_file->title_fds[ 0 ] = fd;
    dvd_file->filesize = dvd_file->title_sizes[ 0 ];

    return dvd_file;
}

static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *dvd, 
				  int title, int menu )
{
    char filename[ MAX_UDF_FILE_NAME_LEN ];
    uint32_t start, len;
    dvd_file_t *dvd_file;

    if( title == 0 ) {
        sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
    } else {
        sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
    }
    start = UDFFindFile( dvd, filename, &len );
    if( start == 0 ) return 0;

    dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
    if( !dvd_file ) return 0;
    dvd_file->dvd = dvd;
    dvd_file->lb_start = start;
    dvd_file->seek_pos = 0;
    memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
    memset( dvd_file->title_fds, -1, sizeof( dvd_file->title_fds ) );
    dvd_file->filesize = len / DVD_VIDEO_LB_LEN;

    /* Calculate the complete file size for every file in the VOBS */
    if( !menu ) {
        int cur;

        for( cur = 2; cur < 10; cur++ ) {
            sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur );
            if( !UDFFindFile( dvd, filename, &len ) ) break;
            dvd_file->filesize += len / DVD_VIDEO_LB_LEN;
        }
    }
    
    /* Hack to crack all the keys on the first open. */
    if( !dvd_file->dvd->init_keys ) {
	initAllCSSKeys( dvd_file->dvd );
	dvd_file->dvd->init_keys = 1;
    }
    
    /* Perform CSS key cracking for this title. */
    if( dvdcss_title( dvd_file->dvd->dev, (int)start ) < 0 ) {
        fprintf( stderr, "libdvdread: Error cracking CSS key for %s\n",
                 filename );
    }

    return dvd_file;
}

static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *dvd, 
				   int title, int menu )
{
    char filename[ MAX_UDF_FILE_NAME_LEN ];
    char full_path[ PATH_MAX + 1 ];
    struct stat fileinfo;
    dvd_file_t *dvd_file;
    int i;

    dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
    if( !dvd_file ) return 0;
    dvd_file->dvd = dvd;
    dvd_file->lb_start = 0;
    dvd_file->seek_pos = 0;
    memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
    memset( dvd_file->title_fds, -1, sizeof( dvd_file->title_fds ) );
    dvd_file->filesize = 0;

    if( menu ) {
        int fd;

        if( title == 0 ) {
            sprintf( filename, "VIDEO_TS.VOB" );
        } else {
            sprintf( filename, "VTS_%02i_0.VOB", title );
        }
        if( !findDVDFile( dvd, filename, full_path ) ) {
            free( dvd_file );
            return 0;
        }

        fd = open( full_path, O_RDONLY );
        if( fd < 0 ) {
            free( dvd_file );
            return 0;
        }

        if( stat( full_path, &fileinfo ) < 0 ) {
            fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
            free( dvd_file );
            return 0;
        }
        dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
        dvd_file->title_fds[ 0 ] = fd;
        dvd_file->filesize = dvd_file->title_sizes[ 0 ];

    } else {
        for( i = 0; i < 9; ++i ) {

            sprintf( filename, "VTS_%02i_%i.VOB", title, i + 1 );
            if( !findDVDFile( dvd, filename, full_path ) ) {
                break;
            }

            if( stat( full_path, &fileinfo ) < 0 ) {
                fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
                break;
            }

            dvd_file->title_sizes[ i ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
            dvd_file->title_fds[ i ] = open( full_path, O_RDONLY );
            dvd_file->filesize += dvd_file->title_sizes[ i ];
        }
        if( !(dvd_file->title_sizes[ 0 ]) ) {
            free( dvd_file );
            return 0;
        }
    }

    return dvd_file;
}

dvd_file_t *DVDOpenFile( dvd_reader_t *dvd, int titlenum, 
			 dvd_read_domain_t domain )
{
    char filename[ MAX_UDF_FILE_NAME_LEN ];

    switch( domain ) {
    case DVD_READ_INFO_FILE:
        if( titlenum == 0 ) {
            sprintf( filename, "/VIDEO_TS/VIDEO_TS.IFO" );
        } else {
            sprintf( filename, "/VIDEO_TS/VTS_%02i_0.IFO", titlenum );
        }
        break;
    case DVD_READ_INFO_BACKUP_FILE:
        if( titlenum == 0 ) {
            sprintf( filename, "/VIDEO_TS/VIDEO_TS.BUP" );
        } else {
            sprintf( filename, "/VIDEO_TS/VTS_%02i_0.BUP", titlenum );
        }
        break;
    case DVD_READ_MENU_VOBS:
        if( dvd->isImageFile ) {
            return DVDOpenVOBUDF( dvd, titlenum, 1 );
        } else {
            return DVDOpenVOBPath( dvd, titlenum, 1 );
        }
        break;
    case DVD_READ_TITLE_VOBS:
        if( titlenum == 0 ) return 0;
        if( dvd->isImageFile ) {
            return DVDOpenVOBUDF( dvd, titlenum, 0 );
        } else {
            return DVDOpenVOBPath( dvd, titlenum, 0 );
        }
        break;
    default:
        fprintf( stderr, "libdvdread: Invalid domain for file open.\n" );
        return 0;
    }
    
    if( dvd->isImageFile ) {
        return DVDOpenFileUDF( dvd, filename );
    } else {
        return DVDOpenFilePath( dvd, filename );
    }
}

void DVDCloseFile( dvd_file_t *dvd_file )
{
    int i;

    if( dvd_file ) {
        if( !dvd_file->dvd->isImageFile ) {
            for( i = 0; i < 9; ++i ) {
                if( dvd_file->title_fds[ i ] >= 0 )
		    close( dvd_file->title_fds[ i ] );
            }
        }

        free( dvd_file );
        dvd_file = 0;
    }
}

int64_t DVDReadLBUDF( dvd_reader_t *device, uint32_t lb_number,
		      size_t block_count, unsigned char *data, 
		      int encrypted )
{
        int ret;

        if( !device->dev ) {
            fprintf( stderr, "libdvdread: Fatal error in block read.\n" );
            return 0;
        }

        ret = dvdcss_seek( device->dev, (int) lb_number );
        if( ret != (int) lb_number ) {
	    fprintf( stderr, "libdvdread: Can't seek to block %u\n", 
		     lb_number );
            return 0;
        }

        return (int64_t) ( dvdcss_read( device->dev, (char *) data, 
					(int) block_count, encrypted ) 
			   * (uint64_t) DVD_VIDEO_LB_LEN );
}

static int64_t DVDReadBlocksUDF( dvd_file_t *dvd_file, uint32_t offset,
				 size_t block_count, unsigned char *data )
{
    return DVDReadLBUDF( dvd_file->dvd, dvd_file->lb_start + offset,
                         block_count, data, DVDCSS_READ_DECRYPT );
}

static int64_t DVDReadBlocksPath( dvd_file_t *dvd_file, size_t offset,
				  size_t block_count, unsigned char *data )
{
    int i;
    ssize_t ret, ret2;
    off64_t off;

    ret = 0;
    ret2 = 0;
    for( i = 0; i < 9; ++i ) {
        if( !dvd_file->title_sizes[ i ] ) return 0;

        if( offset < dvd_file->title_sizes[ i ] ) {
            if( ( offset + block_count ) <= dvd_file->title_sizes[ i ] ) {
	        off = lseek64( dvd_file->title_fds[ i ], 
			       offset * (int64_t) DVD_VIDEO_LB_LEN, SEEK_SET );
		if( off != ( offset * (int64_t) DVD_VIDEO_LB_LEN ) ) {
		    fprintf( stderr, "libdvdread: Can't seek to block %d\n", 
			     offset );
		    return 0;
		}
                ret = read( dvd_file->title_fds[ i ], data,
                            block_count * DVD_VIDEO_LB_LEN );
                break;
            } else {
		size_t part1_size 
		  = ( dvd_file->title_sizes[ i ] - offset ) * DVD_VIDEO_LB_LEN;
		/* FIXME: Really needs to be a while loop.
		   (This is only true if you try and read >1GB at a time) */
		
                /* Read part 1 */
                off = lseek64( dvd_file->title_fds[ i ], 
			       offset * (int64_t) DVD_VIDEO_LB_LEN, SEEK_SET );
		if( off != ( offset * (int64_t) DVD_VIDEO_LB_LEN ) ) {
		    fprintf( stderr, "libdvdread: Can't seek to block %d\n", 
			     offset );
		    return 0;
		}
                ret = read( dvd_file->title_fds[ i ], data, part1_size );
		if( ret < 0 ) return ret;
		/* FIXME: This is wrong if i is the last file in the set. 
		          also error from this read will not show in ret. */
		
                /* Read part 2 */
                lseek64( dvd_file->title_fds[ i + 1 ], (off64_t)0, SEEK_SET );
                ret2 = read( dvd_file->title_fds[ i + 1 ], data + part1_size,
                             block_count * DVD_VIDEO_LB_LEN - part1_size );
                if( ret2 < 0 ) return ret2;
		break;
            }
        } else {
            offset -= dvd_file->title_sizes[ i ];
        }
    }

    return ( (int64_t) ret + (int64_t) ret2 );
}

/* These are broken for some cases reading more than 2Gb at a time. */
ssize_t DVDReadBlocks( dvd_file_t *dvd_file, int offset, 
		       size_t block_count, unsigned char *data )
{
    int64_t ret;
  
    if( dvd_file->dvd->isImageFile ) {
	ret = DVDReadBlocksUDF( dvd_file, (uint32_t)offset, 
				block_count, data );
    } else {
	ret = DVDReadBlocksPath( dvd_file, (size_t) offset, 
				 block_count, data );
    }
    if( ret <= 0 ) {
        return (ssize_t) ret;
    }
    {
      ssize_t sret = (ssize_t) (ret / (int64_t)DVD_VIDEO_LB_LEN );
      if( sret == 0 ) {
	fprintf(stderr, "libdvdread: DVDReadBlocks got %d bytes\n", (int)ret );
      }
      return sret;
    }
}

int32_t DVDFileSeek( dvd_file_t *dvd_file, int32_t offset )
{
    if( dvd_file->dvd->isImageFile ) {
        dvd_file->seek_pos = (uint32_t) offset;
        return offset;
    } else {
        return (int32_t) ( lseek( dvd_file->title_fds[ 0 ], 
				  (off_t) offset, SEEK_SET ) );
    }
}

static ssize_t DVDReadBytesUDF( dvd_file_t *dvd_file, void *data, 
				size_t byte_size )
{
    unsigned char *secbuf;
    unsigned int numsec, seek_sector, seek_byte;
    int64_t len;
    
    seek_sector = dvd_file->seek_pos / DVD_VIDEO_LB_LEN;
    seek_byte   = dvd_file->seek_pos % DVD_VIDEO_LB_LEN;

    numsec = ( ( seek_byte + byte_size ) / DVD_VIDEO_LB_LEN ) + 1;
    secbuf = (unsigned char *) malloc( numsec * DVD_VIDEO_LB_LEN );
    if( !secbuf ) {
	fprintf( stderr, "libdvdread: Can't allocate memory " 
		 "for file read!\n" );
        return 0;
    }

    len = DVDReadLBUDF( dvd_file->dvd, dvd_file->lb_start + seek_sector,
                        numsec, secbuf, DVDCSS_NOFLAGS );
    if( len != numsec * (int64_t) DVD_VIDEO_LB_LEN ) {
        free( secbuf );
        return 0;
    }

    dvd_file->seek_pos += byte_size;

    memcpy( data, &(secbuf[ seek_byte ]), byte_size );
    free( secbuf );

    return byte_size;
}

static ssize_t DVDReadBytesPath( dvd_file_t *dvd_file, void *data, 
				 size_t byte_size )
{
    return read( dvd_file->title_fds[ 0 ], data, byte_size );
}

ssize_t DVDReadBytes( dvd_file_t *dvd_file, void *data, size_t byte_size )
{
    if( dvd_file->dvd->isImageFile ) {
        return DVDReadBytesUDF( dvd_file, data, byte_size );
    } else {
        return DVDReadBytesPath( dvd_file, data, byte_size );
    }
}

ssize_t DVDFileSize( dvd_file_t *dvd_file )
{
    return dvd_file->filesize;
}


--- NEW FILE ---
/**
 * Copyright (C) 2001 Billy Biggs <vektor at dumbterm.net>,
 *                    Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifndef DVD_READER_H_INCLUDED
#define DVD_READER_H_INCLUDED

#include <sys/types.h>
#include <inttypes.h>

/**
 * The length of one Logical Block of a DVD Video.
 */
#define DVD_VIDEO_LB_LEN 2048

/**
 * Maximum length of filenames for UDF.
 */
#define MAX_UDF_FILE_NAME_LEN 2048

#ifdef __cplusplus
extern "C" {
#endif

typedef struct dvd_reader_s dvd_reader_t;
typedef struct dvd_file_s dvd_file_t;

/**
 * Opens a block device of a DVD-ROM file, or an image file, or a directory
 * name for a mounted DVD or HD copy of a DVD.  Returns 0 if we can't get any
 * of those methods to work.
 *
 * If the given file is a block device, or is the mountpoint for a block
 * device, then that device is used for CSS authentication using libdvdcss.
 * If no device is available, then no CSS authentication is performed, 
 * and we hope that the image is decrypted.
 *
 * If the path given is a directory, then the files in that directory may be in
 * any one of these formats:
 *
 *   path/VIDEO_TS/VTS_01_1.VOB
 *   path/video_ts/vts_01_1.vob
 *   path/VTS_01_1.VOB
 *   path/vts_01_1.vob
 */
dvd_reader_t *DVDOpen( const char *path );

/**
 * Closes and cleans up the DVD reader object.  You must close all open files
 * before calling this function.
 */
void DVDClose( dvd_reader_t *dvd );

/**
 * INFO_FILE       : VIDEO_TS.IFO     (manager)
 *                   VTS_XX_0.IFO     (title)
 *
 * INFO_BACKUP_FILE: VIDEO_TS.BUP     (manager)
 *                   VTS_XX_0.BUP     (title)
 *
 * MENU_VOBS       : VIDEO_TS.VOB     (manager)
 *                   VTS_XX_0.VOB     (title)
 *
 * TITLE_VOBS      : VTS_XX_[1-9].VOB (title)
 *                   All files in the title set are opened and read as a single
 *                   file.
 */
typedef enum {
    DVD_READ_INFO_FILE,
    DVD_READ_INFO_BACKUP_FILE,
    DVD_READ_MENU_VOBS,
    DVD_READ_TITLE_VOBS
} dvd_read_domain_t;

/**
 * Opens a file on the DVD given the title number and domain.  If the title
 * number is 0, the video manager information is opened
 * (VIDEO_TS.[IFO,BUP,VOB]).  Returns a file structure which may be used for
 * reads, or 0 if the file was not found.
 */
dvd_file_t *DVDOpenFile( dvd_reader_t *dvd, int titlenum, 
			 dvd_read_domain_t domain );

/**
 * Closes a file and frees the associated structure.
 */
void DVDCloseFile( dvd_file_t *dvd_file );

/**
 * Reads block_count number of blocks from the file at the given block offset.
 * Returns number of blocks read on success, -1 on error.  This call is only
 * for reading VOB data, and should not be used when reading the IFO files.  
 * When reading from an encrypted drive, blocks are decrypted using libdvdcss 
 * where required.
 */
ssize_t DVDReadBlocks( dvd_file_t *dvd_file, int offset,
		       size_t block_count, unsigned char *data );

/**
 * Seek to the given position in the file.  Returns the resulting position in
 * bytes from the beginning of the file.  The seek position is only used for
 * byte reads from the file, the block read call always reads from the given
 * offset.
 */
int DVDFileSeek( dvd_file_t *dvd_file, int offset );

/**
 * Reads the given number of bytes from the file.  This call can only be used
 * on the information files, and may not be used for reading from a VOB.  This
 * reads from and increments the currrent seek position for the file.
 */
ssize_t DVDReadBytes( dvd_file_t *dvd_file, void *data, size_t byte_size );

/**
 * Returns the file size in blocks.
 */
ssize_t DVDFileSize( dvd_file_t *dvd_file );

/**
 * DVD CSS Key chahe dir.
 */
extern char * dvd_key_dir;

#ifdef __cplusplus
};
#endif
#endif /* DVD_READER_H_INCLUDED */

--- NEW FILE ---
/**
 * This code is based on dvdudf by:
 *   Christian Wolff <scarabaeus at convergence.de>.
 *
 * Modifications by:
 *   Billy Biggs <vektor at dumbterm.net>.
 *
 * dvdudf: parse and read the UDF volume information of a DVD Video
 * Copyright (C) 1999 Christian Wolff for convergence integrated media
 * GmbH The author can be reached at scarabaeus at convergence.de, the
 * project's page is at http://linuxtv.org/dvd/
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.  Or, point your browser to
 * http://www.gnu.org/copyleft/gpl.html
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <inttypes.h>

#include "dvd_udf.h"

extern int64_t DVDReadLBUDF( dvd_reader_t *device, uint32_t lb_number,
			     size_t block_count, unsigned char *data, 
			     int encrypted );

#ifndef NULL
#define NULL ((void *)0)
#endif

struct Partition {
    int valid;
    char VolumeDesc[128];
    uint16_t Flags;
    uint16_t Number;
    char Contents[32];
    uint32_t AccessType;
    uint32_t Start;
    uint32_t Length;
};

struct AD {
    uint32_t Location;
    uint32_t Length;
    uint8_t  Flags;
    uint16_t Partition;
};

/* For direct data access, LSB first */
#define GETN1(p) ((uint8_t)data[p])
#define GETN2(p) ((uint16_t)data[p] | ((uint16_t)data[(p) + 1] << 8))
#define GETN3(p) ((uint32_t)data[p] | ((uint32_t)data[(p) + 1] << 8) \
		  | ((uint32_t)data[(p) + 2] << 16))
#define GETN4(p) ((uint32_t)data[p] \
		  | ((uint32_t)data[(p) + 1] << 8) \
		  | ((uint32_t)data[(p) + 2] << 16) \
		  | ((uint32_t)data[(p) + 3] << 24))
/* This is wrong with regard to endianess */
#define GETN(p, n, target) memcpy(target, &data[p], n)

static int Unicodedecode( uint8_t *data, int len, char *target ) 
{
    int p = 1, i = 0;

    if( ( data[ 0 ] == 8 ) || ( data[ 0 ] == 16 ) ) do {
        if( data[ 0 ] == 16 ) p++;  /* Ignore MSB of unicode16 */
        if( p < len ) {
            target[ i++ ] = data[ p++ ];
        }
    } while( p < len );

    target[ i ] = '\0';
    return 0;
}

static int UDFDescriptor( uint8_t *data, uint16_t *TagID ) 
{
    *TagID = GETN2(0);
    // TODO: check CRC 'n stuff
    return 0;
}

static int UDFExtentAD( uint8_t *data, uint32_t *Length, uint32_t *Location ) 
{
    *Length   = GETN4(0);
    *Location = GETN4(4);
    return 0;
}

static int UDFShortAD( uint8_t *data, struct AD *ad, 
		       struct Partition *partition ) 
{
    ad->Length = GETN4(0);
    ad->Flags = ad->Length >> 30;
    ad->Length &= 0x3FFFFFFF;
    ad->Location = GETN4(4);
    ad->Partition = partition->Number; // use number of current partition
    return 0;
}

static int UDFLongAD( uint8_t *data, struct AD *ad )
{
    ad->Length = GETN4(0);
    ad->Flags = ad->Length >> 30;
    ad->Length &= 0x3FFFFFFF;
    ad->Location = GETN4(4);
    ad->Partition = GETN2(8);
    //GETN(10, 6, Use);
    return 0;
}

static int UDFExtAD( uint8_t *data, struct AD *ad )
{
    ad->Length = GETN4(0);
    ad->Flags = ad->Length >> 30;
    ad->Length &= 0x3FFFFFFF;
    ad->Location = GETN4(12);
    ad->Partition = GETN2(16);
    //GETN(10, 6, Use);
    return 0;
}

static int UDFICB( uint8_t *data, uint8_t *FileType, uint16_t *Flags )
{
    *FileType = GETN1(11);
    *Flags = GETN2(18);
    return 0;
}


static int UDFPartition( uint8_t *data, uint16_t *Flags, uint16_t *Number,
			 char *Contents, uint32_t *Start, uint32_t *Length )
{
    *Flags = GETN2(20);
    *Number = GETN2(22);
    GETN(24, 32, Contents);
    *Start = GETN4(188);
    *Length = GETN4(192);
    return 0;
}

/**
 * Reads the volume descriptor and checks the parameters.  Returns 0 on OK, 1
 * on error.
 */
static int UDFLogVolume( uint8_t *data, char *VolumeDescriptor )
{
    uint32_t lbsize, MT_L, N_PM;
    Unicodedecode(&data[84], 128, VolumeDescriptor);
    lbsize = GETN4(212);  // should be 2048
    MT_L = GETN4(264);    // should be 6
    N_PM = GETN4(268);    // should be 1
    if (lbsize != DVD_VIDEO_LB_LEN) return 1;
    return 0;
}

static int UDFFileEntry( uint8_t *data, uint8_t *FileType, 
			 struct Partition *partition, struct AD *ad )
{
    uint16_t flags;
    uint32_t L_EA, L_AD;
    unsigned int p;

    UDFICB( &data[ 16 ], FileType, &flags );
   
    /* Init ad for an empty file (i.e. there isn't a AD, L_AD == 0 ) */
    ad->Length = GETN4( 60 ); // Really 8 bytes a 56
    ad->Flags = 0;
    ad->Location = 0; // what should we put here? 
    ad->Partition = partition->Number; // use number of current partition

    L_EA = GETN4( 168 );
    L_AD = GETN4( 172 );
    p = 176 + L_EA;
    while( p < 176 + L_EA + L_AD ) {
        switch( flags & 0x0007 ) {
            case 0: UDFShortAD( &data[ p ], ad, partition ); p += 8;  break;
            case 1: UDFLongAD( &data[ p ], ad );  p += 16; break;
            case 2: UDFExtAD( &data[ p ], ad );   p += 20; break;
            case 3:
                switch( L_AD ) {
                    case 8:  UDFShortAD( &data[ p ], ad, partition ); break;
                    case 16: UDFLongAD( &data[ p ], ad );  break;
                    case 20: UDFExtAD( &data[ p ], ad );   break;
                }
                p += L_AD;
                break;
            default:
                p += L_AD; break;
        }
    }
    return 0;
}

static int UDFFileIdentifier( uint8_t *data, uint8_t *FileCharacteristics,
			      char *FileName, struct AD *FileICB )
{
    uint8_t L_FI;
    uint16_t L_IU;

    *FileCharacteristics = GETN1(18);
    L_FI = GETN1(19);
    UDFLongAD(&data[20], FileICB);
    L_IU = GETN2(36);
    if (L_FI) Unicodedecode(&data[38 + L_IU], L_FI, FileName);
    else FileName[0] = '\0';
    return 4 * ((38 + L_FI + L_IU + 3) / 4);
}

/**
 * Maps ICB to FileAD
 * ICB: Location of ICB of directory to scan
 * FileType: Type of the file
 * File: Location of file the ICB is pointing to
 * return 1 on success, 0 on error;
 */
static int UDFMapICB( dvd_reader_t *device, struct AD ICB, uint8_t *FileType,
		      struct Partition *partition, struct AD *File ) 
{
    uint8_t LogBlock[DVD_VIDEO_LB_LEN];
    uint32_t lbnum;
    uint16_t TagID;

    lbnum = partition->Start + ICB.Location;
    do {
        if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) {
            TagID = 0;
        } else {
            UDFDescriptor( LogBlock, &TagID );
        }

        if( TagID == 261 ) {
            UDFFileEntry( LogBlock, FileType, partition, File );
            return 1;
        };
    } while( ( lbnum <= partition->Start + ICB.Location + ( ICB.Length - 1 )
             / DVD_VIDEO_LB_LEN ) && ( TagID != 261 ) );

    return 0;
}

/**
 * Dir: Location of directory to scan
 * FileName: Name of file to look for
 * FileICB: Location of ICB of the found file
 * return 1 on success, 0 on error;
 */
static int UDFScanDir( dvd_reader_t *device, struct AD Dir, char *FileName,
                       struct Partition *partition, struct AD *FileICB ) 
{
    char filename[ MAX_UDF_FILE_NAME_LEN ];
    uint8_t directory[ 2 * DVD_VIDEO_LB_LEN ];
    uint32_t lbnum;
    uint16_t TagID;
    uint8_t filechar;
    unsigned int p;

    /* Scan dir for ICB of file */
    lbnum = partition->Start + Dir.Location;

    if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) {
        return 0;
    }

    p = 0;
    while( p < Dir.Length ) {
        if( p > DVD_VIDEO_LB_LEN ) {
            ++lbnum;
            p -= DVD_VIDEO_LB_LEN;
            Dir.Length -= DVD_VIDEO_LB_LEN;
            if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) {
                return 0;
            }
        }
        UDFDescriptor( &directory[ p ], &TagID );
        if( TagID == 257 ) {
            p += UDFFileIdentifier( &directory[ p ], &filechar,
                                    filename, FileICB );
            if( !strcasecmp( FileName, filename ) ) {
                return 1;
            }
        } else {
            return 0;
        }
    }

    return 0;
}

/**
 * Looks for partition on the disc.  Returns 1 if partition found, 0 on error.
 *   partnum: Number of the partition, starting at 0.
 *   part: structure to fill with the partition information
 */
static int UDFFindPartition( dvd_reader_t *device, int partnum,
			     struct Partition *part ) 
{
    uint8_t LogBlock[ DVD_VIDEO_LB_LEN ], Anchor[ DVD_VIDEO_LB_LEN ];
    uint32_t lbnum, MVDS_location, MVDS_length;
    uint16_t TagID;
    uint32_t lastsector;
    int i, terminate, volvalid;

    /* Find Anchor */
    lastsector = 0;
    lbnum = 256;   /* Try #1, prime anchor */
    terminate = 0;

    for(;;) {
        if( DVDReadLBUDF( device, lbnum, 1, Anchor, 0 ) > 0 ) {
            UDFDescriptor( Anchor, &TagID );
        } else {
            TagID = 0;
        }
        if (TagID != 2) {
            /* Not an anchor */
            if( terminate ) return 0; /* Final try failed */

            if( lastsector ) {

                /* We already found the last sector.  Try #3, alternative
                 * backup anchor.  If that fails, don't try again.
                 */
                lbnum = lastsector;
                terminate = 1;
            } else {
                /* TODO: Find last sector of the disc (this is optional). */
                if( lastsector ) {
                    /* Try #2, backup anchor */
                    lbnum = lastsector - 256;
                } else {
                    /* Unable to find last sector */
                    return 0;
                }
            }
        } else {
            /* It's an anchor! We can leave */
            break;
        }
    }
    /* Main volume descriptor */
    UDFExtentAD( &Anchor[ 16 ], &MVDS_length, &MVDS_location );
	
    part->valid = 0;
    volvalid = 0;
    part->VolumeDesc[ 0 ] = '\0';
    i = 1;
    do {
        /* Find Volume Descriptor */
        lbnum = MVDS_location;
        do {

            if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) {
                TagID = 0;
            } else {
                UDFDescriptor( LogBlock, &TagID );
            }

            if( ( TagID == 5 ) && ( !part->valid ) ) {
                /* Partition Descriptor */
                UDFPartition( LogBlock, &part->Flags, &part->Number,
                              part->Contents, &part->Start, &part->Length );
                part->valid = ( partnum == part->Number );
            } else if( ( TagID == 6 ) && ( !volvalid ) ) {
                /* Logical Volume Descriptor */
                if( UDFLogVolume( LogBlock, part->VolumeDesc ) ) {  
                    /* TODO: sector size wrong! */
                } else {
                    volvalid = 1;
                }
            }

        } while( ( lbnum <= MVDS_location + ( MVDS_length - 1 )
                 / DVD_VIDEO_LB_LEN ) && ( TagID != 8 )
                 && ( ( !part->valid ) || ( !volvalid ) ) );

        if( ( !part->valid) || ( !volvalid ) ) {
            /* Backup volume descriptor */
            UDFExtentAD( &Anchor[ 24 ], &MVDS_length, &MVDS_location );
        }
    } while( i-- && ( ( !part->valid ) || ( !volvalid ) ) );

    /* We only care for the partition, not the volume */
    return part->valid;
}

uint32_t UDFFindFile( dvd_reader_t *device, char *filename,
		      uint32_t *filesize )
{
    uint8_t LogBlock[ DVD_VIDEO_LB_LEN ];
    uint32_t lbnum;
    uint16_t TagID;
    struct Partition partition;
    struct AD RootICB, File, ICB;
    char tokenline[ MAX_UDF_FILE_NAME_LEN ];
    char *token;
    uint8_t filetype;
	
    *filesize = 0;
    tokenline[0] = '\0';
    strcat( tokenline, filename );

    /* Find partition, 0 is the standard location for DVD Video.*/
    if( !UDFFindPartition( device, 0, &partition ) ) return 0;

    /* Find root dir ICB */
    lbnum = partition.Start;
    do {
        if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) {
            TagID = 0;
        } else {
            UDFDescriptor( LogBlock, &TagID );
        }

        /* File Set Descriptor */
        if( TagID == 256 ) {  // File Set Descriptor
            UDFLongAD( &LogBlock[ 400 ], &RootICB );
        }
    } while( ( lbnum < partition.Start + partition.Length )
             && ( TagID != 8 ) && ( TagID != 256 ) );

    /* Sanity checks. */
    if( TagID != 256 ) return 0;
    if( RootICB.Partition != 0 ) return 0;
	
    /* Find root dir */
    if( !UDFMapICB( device, RootICB, &filetype, &partition, &File ) ) return 0;
    if( filetype != 4 ) return 0;  /* Root dir should be dir */

    /* Tokenize filepath */
    token = strtok(tokenline, "/");
    while( token != NULL ) {
        if( !UDFScanDir( device, File, token, &partition, &ICB ) ) return 0;
        if( !UDFMapICB( device, ICB, &filetype, &partition, &File ) ) return 0;
        token = strtok( NULL, "/" );
    }
    
    /* Sanity check. */
    if( File.Partition != 0 ) return 0;
   
    *filesize = File.Length;
    /* Hack to not return partition.Start for empty files. */
    if( !File.Location )
      return 0;
    else
      return partition.Start + File.Location;
}

--- NEW FILE ---
/**
 * This code is based on dvdudf by:
 *   Christian Wolff <scarabaeus at convergence.de>.
 *
 * Modifications by:
 *   Billy Biggs <vektor at dumbterm.net>.
 *
 * dvdudf: parse and read the UDF volume information of a DVD Video
 * Copyright (C) 1999 Christian Wolff for convergence integrated media
 * GmbH The author can be reached at scarabaeus at convergence.de, the
 * project's page is at http://linuxtv.org/dvd/
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.  Or, point your browser to
 * http://www.gnu.org/copyleft/gpl.html
 */

#ifndef DVD_UDF_H_INCLUDED
#define DVD_UDF_H_INCLUDED

#include "dvd_reader.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
 * Looks for a file on the UDF disc/imagefile and returns the block number
 * where it begins, or 0 if it is not found.  The filename should be an
 * absolute pathname on the UDF filesystem, starting with '/'.  For example,
 * '/VIDEO_TS/VTS_01_1.IFO'.  On success, filesize will be set to the size of
 * the file in bytes.
 */
uint32_t UDFFindFile( dvd_reader_t *device, char *filename, uint32_t *size );

#ifdef __cplusplus
};
#endif
#endif /* DVD_UDF_H_INCLUDED */

--- NEW FILE ---
/*****************************************************************************
 * libdvdcss.h: DVD reading library, exported functions.
 *****************************************************************************
 * Copyright (C) 1998-2001 VideoLAN
 * $Id: dvdcss.h,v 1.1 2002/04/24 19:28:03 arpi Exp $
 *
 * Authors: Stéphane Borel <stef at via.ecp.fr>
 *          Samuel Hocevar <sam at zoy.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * The libdvdcss structure
 *****************************************************************************/
typedef struct dvdcss_s* dvdcss_handle;

/*****************************************************************************
 * Flags
 *****************************************************************************/
#define DVDCSS_NOFLAGS         0

#define DVDCSS_INIT_QUIET      (1 << 0)
#define DVDCSS_INIT_DEBUG      (1 << 1)

#define DVDCSS_READ_DECRYPT    (1 << 0)

#define DVDCSS_BLOCK_SIZE      2048

/*****************************************************************************
 * Exported prototypes
 *****************************************************************************/
extern dvdcss_handle dvdcss_open  ( char *psz_target, int i_flags );
extern int           dvdcss_close ( dvdcss_handle );
extern int           dvdcss_title ( dvdcss_handle, int i_block );
extern int           dvdcss_seek  ( dvdcss_handle, int i_blocks );
extern int           dvdcss_read  ( dvdcss_handle, void *p_buffer, int i_blocks, int i_flags );
extern int           dvdcss_readv ( dvdcss_handle, void *p_iovec, int i_blocks, int i_flags );
extern char *        dvdcss_error ( dvdcss_handle );


--- NEW FILE ---

static char *get_path(char *filename){
	char *homedir;
	char *buff;
	static char *config_dir = "/.mplayer";
	int len;

	if ((homedir = getenv("HOME")) == NULL)
		return NULL;
	len = strlen(homedir) + strlen(config_dir) + 1;
	if (filename == NULL) {
		if ((buff = (char *) malloc(len)) == NULL)
			return NULL;
		sprintf(buff, "%s%s", homedir, config_dir);
	} else {
		len += strlen(filename) + 1;
		if ((buff = (char *) malloc(len)) == NULL)
			return NULL;
		sprintf(buff, "%s%s/%s", homedir, config_dir, filename);
	}
//	mp_msg(MSGT_GLOBAL,MSGL_V,"get_path('%s') -> '%s'\n",filename,buff);
	return buff;
}

--- NEW FILE ---
/* 
 * Copyright (C) 2000 Björn Englund <d4bjorn at dtek.chalmers.se>, 
 *                    Håkan Hjort <d95hjort at 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
 */

[...1009 lines suppressed...]
    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);
}


--- NEW FILE ---
/**
 * Copyright (C) 2000 Björn Englund <d4bjorn at dtek.chalmers.se>,
 *                    Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef IFO_PRINT_H_INCLUDED
#define IFO_PRINT_H_INCLUDED

#include <dvdread/ifo_types.h>
#include <dvdread/dvd_reader.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * This file provides example functions for printing information about the IFO
 * file to stdout.
 */

/**
 * Print the complete parsing information for the given file.
 */
void ifoPrint(dvd_reader_t *dvd, int title);

void ifoPrint_VMGI_MAT(vmgi_mat_t *vmgi_mat);
void ifoPrint_VTSI_MAT(vtsi_mat_t *vtsi_mat);

void ifoPrint_PTL_MAIT(ptl_mait_t *ptl_mait);
void ifoPrint_VTS_ATRT(vts_atrt_t *vts_atrt);
void ifoPrint_TT_SRPT(tt_srpt_t *vmg_ptt_srpt);
void ifoPrint_VTS_PTT_SRPT(vts_ptt_srpt_t *vts_ptt_srpt);
void ifoPrint_PGC(pgc_t *pgc);
void ifoPrint_PGCIT(pgcit_t *pgcit);
void ifoPrint_PGCI_UT(pgci_ut_t *pgci_ut);
void ifoPrint_C_ADT(c_adt_t *c_adt);
void ifoPrint_VOBU_ADMAP(vobu_admap_t *vobu_admap);

#ifdef __cplusplus
};
#endif
#endif /* IFO_PRINT_H_INCLUDED */

--- NEW FILE ---
/*
 * Copyright (C) 2000 Björn Englund <d4bjorn at dtek.chalmers.se>, 
 *                    Håkan Hjort <d95hjort at 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
 */

[...1743 lines suppressed...]
    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;
  }
}


--- NEW FILE ---
/**
 * Copyright (C) 2000 Björn Englund <d4bjorn at dtek.chalmers.se>,
 *                    Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef IFO_READ_H_INCLUDED
#define IFO_READ_H_INCLUDED

#include <dvdread/ifo_types.h>
#include <dvdread/dvd_reader.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * Opens an IFO and reads in all the data for the IFO file corresponding to the
 * given title.  If title 0 is given, the video manager IFO file is read.
 * Returns a handle to a completely parsed structure.
 */
ifo_handle_t *ifoOpen(dvd_reader_t *dvd, int title);

/**
 * Opens an IFO and reads in _only_ the vmgi_mat data.  This call can be used
 * together with the calls below to read in each segment of the IFO file on
 * demand.
 */
ifo_handle_t *ifoOpenVMGI(dvd_reader_t *dvd);

/**
 * Opens an IFO and reads in _only_ the vtsi_mat data.  This call can be used
 * together with the calls below to read in each segment of the IFO file on
 * demand.
 */
ifo_handle_t *ifoOpenVTSI(dvd_reader_t *dvd, int title);

/**
 * Cleans up the IFO information.  This will free all data allocated for the
 * substructures.
 */
void ifoClose(ifo_handle_t *ifofile);

/**
 * The following functions are for reading only part of the VMGI/VTSI files.
 * Returns 1 if the data was successfully read and 0 on error.
 */

/**
 * Read in the Parental Management Information table, filling the
 * ifofile->ptl_mait structure and its substructures.  This data is only
 * located in the video manager information file.  This fills the
 * ifofile->ptl_mait structure and all its substructures.
 */
int ifoRead_PTL_MAIT(ifo_handle_t *ifofile);

/**
 * Read in the attribute table for the main menu vob, filling the
 * ifofile->vts_atrt structure and its substructures.  Only located in the
 * video manager information file.  This fills in the ifofile->vts_atrt
 * structure and all its substructures.
 */
int ifoRead_VTS_ATRT(ifo_handle_t *ifofile);

/**
 * Reads the title info for the main menu, filling the ifofile->tt_srpt
 * structure and its substructures.  This data is only located in the video
 * manager information file.  This structure is mandatory in the IFO file.
 */
int ifoRead_TT_SRPT(ifo_handle_t *ifofile);

/**
 * Reads in the part of title search pointer table, filling the
 * ifofile->vts_ptt_srpt structure and its substructures.  This data is only
 * located in the video title set information file.  This structure is
 * mandatory, and must be included in the VTSI file.
 */
int ifoRead_VTS_PTT_SRPT(ifo_handle_t *ifofile);

/**
 * Reads in the first play program chain data, filling the
 * ifofile->first_play_pgc structure.  This data is only located in the video
 * manager information file.  This structure is mandatory, and must be included
 * in the VMGI file.
 */
int ifoRead_FP_PGC(ifo_handle_t *ifofile);

/**
 * Reads in the program chain information table for the video title set.  Fills
 * in the ifofile->vts_pgcit structure and its substructures, which includes
 * the data for each program chain in the set.  This data is only located in
 * the video title set information file.  This structure is mandatory, and must
 * be included in the VTSI file.
 */
int ifoRead_PGCIT(ifo_handle_t *ifofile);

/**
 * Reads in the menu PGCI unit table for the menu VOB.  For the video manager,
 * this corresponds to the VIDEO_TS.VOB file, and for each title set, this
 * corresponds to the VTS_XX_0.VOB file.  This data is located in both the
 * video manager and video title set information files.  For VMGI files, this
 * fills the ifofile->vmgi_pgci_ut structure and all its substructures.  For
 * VTSI files, this fills the ifofile->vtsm_pgci_ut structure.
 */
int ifoRead_PGCI_UT(ifo_handle_t *ifofile);

/**
 * Reads in the cell address table for the menu VOB.  For the video manager,
 * this corresponds to the VIDEO_TS.VOB file, and for each title set, this
 * corresponds to the VTS_XX_0.VOB file.  This data is located in both the
 * video manager and video title set information files.  For VMGI files, this
 * fills the ifofile->vmgm_c_adt structure and all its substructures.  For VTSI
 * files, this fills the ifofile->vtsm_c_adt structure.
 */
int ifoRead_C_ADT(ifo_handle_t *ifofile);

/**
 * Reads in the cell address table for the video title set corresponding to
 * this IFO file.  This data is only located in the video title set information
 * file.  This structure is mandatory, and must be included in the VTSI file.
 * This call fills the ifofile->vts_c_adt structure and its substructures.
 */
int ifoRead_TITLE_C_ADT(ifo_handle_t *ifofile);

/**
 * Reads in the VOBU address map for the menu VOB.  For the video manager, this
 * corresponds to the VIDEO_TS.VOB file, and for each title set, this
 * corresponds to the VTS_XX_0.VOB file.  This data is located in both the
 * video manager and video title set information files.  For VMGI files, this
 * fills the ifofile->vmgm_vobu_admap structure and all its substructures.  For
 * VTSI files, this fills the ifofile->vtsm_vobu_admap structure.
 */
int ifoRead_VOBU_ADMAP(ifo_handle_t *ifofile);

/**
 * Reads in the VOBU address map for the associated video title set.  This data
 * is only located in the video title set information file.  This structure is
 * mandatory, and must be included in the VTSI file.  Fills the
 * ifofile->vts_vobu_admap structure and its substructures.
 */
int ifoRead_TITLE_VOBU_ADMAP(ifo_handle_t *ifofile);

/**
 * Reads in the text data strings for the DVD.  Fills the ifofile->txtdt_mgi
 * structure and all its substructures.  This data is only located in the video
 * manager information file.  This structure is mandatory, and must be included
 * in the VMGI file.
 */
int ifoRead_TXTDT_MGI(ifo_handle_t *ifofile);

/**
 * The following functions are used for freeing parsed sections of the
 * ifo_handle_t structure and the allocated substructures.  The free calls
 * below are safe:  they will not mind if you attempt to free part of an IFO
 * file which was not read in or which does not exist.
 */
void ifoFree_PTL_MAIT(ifo_handle_t *ifofile);
void ifoFree_VTS_ATRT(ifo_handle_t *ifofile);
void ifoFree_TT_SRPT(ifo_handle_t *ifofile);
void ifoFree_VTS_PTT_SRPT(ifo_handle_t *ifofile);
void ifoFree_FP_PGC(ifo_handle_t *ifofile);
void ifoFree_PGCIT(ifo_handle_t *ifofile);
void ifoFree_PGCI_UT(ifo_handle_t *ifofile);
void ifoFree_C_ADT(ifo_handle_t *ifofile);
void ifoFree_TITLE_C_ADT(ifo_handle_t *ifofile);
void ifoFree_VOBU_ADMAP(ifo_handle_t *ifofile);
void ifoFree_TITLE_VOBU_ADMAP(ifo_handle_t *ifofile);
void ifoFree_TXTDT_MGI(ifo_handle_t *ifofile);

#ifdef __cplusplus
};
#endif
#endif /* IFO_READ_H_INCLUDED */

--- NEW FILE ---
/**
 * Copyright (C) 2000 Björn Englund <d4bjorn at dtek.chalmers.se>,
 *                    Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef IFO_TYPES_H_INCLUDED
#define IFO_TYPES_H_INCLUDED

#include <inttypes.h>
#include <dvdread/dvd_reader.h>


#undef ATTRIBUTE_PACKED
#undef PRAGMA_PACK_BEGIN 
#undef PRAGMA_PACK_END

#if defined(__GNUC__)
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
#define ATTRIBUTE_PACKED __attribute__ ((packed))
#define PRAGMA_PACK 0
#endif
#endif

#if !defined(ATTRIBUTE_PACKED)
#define ATTRIBUTE_PACKED
#define PRAGMA_PACK 1
#endif

#if PRAGMA_PACK
#pragma pack(1)
#endif


/**
 * Common
 *
 * The following structures are used in both the VMGI and VTSI.
 */


/**
 * DVD Time Information.
 */
typedef struct {
  uint8_t hour;
  uint8_t minute;
  uint8_t second;
  uint8_t frame_u; // The two high bits are the frame rate.
} ATTRIBUTE_PACKED dvd_time_t;

/**
 * Type to store per-command data.
 */
typedef struct {
  uint8_t bytes[8];
} ATTRIBUTE_PACKED vm_cmd_t;
#define COMMAND_DATA_SIZE 8


/**
 * Video Attributes.
 */
typedef struct {
#ifdef WORDS_BIGENDIAN
  unsigned int mpeg_version         : 2;
  unsigned int video_format         : 2;
  unsigned int display_aspect_ratio : 2;
  unsigned int permitted_df         : 2;
  
  unsigned int line21_cc_1          : 1;
  unsigned int line21_cc_2          : 1;
  unsigned int unknown1             : 2;
  
  unsigned int picture_size         : 2;
  unsigned int letterboxed          : 1;
  unsigned int film_mode            : 1;
#else
  unsigned int permitted_df         : 2;
  unsigned int display_aspect_ratio : 2;
  unsigned int video_format         : 2;
  unsigned int mpeg_version         : 2;
  
  unsigned int film_mode            : 1;
  unsigned int letterboxed          : 1;
  unsigned int picture_size         : 2;
  
  unsigned int unknown1             : 2;
  unsigned int line21_cc_2          : 1;
  unsigned int line21_cc_1          : 1;
#endif
} ATTRIBUTE_PACKED video_attr_t;

/**
 * Audio Attributes. (Incomplete/Wrong?)
 */
typedef struct {
#ifdef WORDS_BIGENDIAN
  unsigned int audio_format           : 3;
  unsigned int multichannel_extension : 1;
  unsigned int lang_type              : 2;
  unsigned int application_mode       : 2;
  
  unsigned int quantization           : 2;
  unsigned int sample_frequency       : 2;
  unsigned int unknown1               : 1;
  unsigned int channels               : 3;
#else
  unsigned int application_mode       : 2;
  unsigned int lang_type              : 2;
  unsigned int multichannel_extension : 1;
  unsigned int audio_format           : 3;
  
  unsigned int channels               : 3;
  unsigned int unknown1               : 1;
  unsigned int sample_frequency       : 2;
  unsigned int quantization           : 2;
#endif
  uint16_t lang_code;
  uint8_t  lang_code2; // ??
  uint8_t  lang_extension;
  uint16_t unknown2;
} ATTRIBUTE_PACKED audio_attr_t;

/**
 * Subpicture Attributes.(Incomplete/Wrong)
 */
typedef struct {
  /*
   * type: 0 not specified
   *       1 language
   *       2 other
   * coding mode: 0 run length
   *              1 extended
   *              2 other
   * language: indicates language if type == 1
   * lang extension: if type == 1 contains the lang extension
   */
  uint8_t type;
  uint8_t zero1;
  uint16_t lang_code;
  uint8_t lang_extension;
  uint8_t zero2;
} ATTRIBUTE_PACKED subp_attr_t;



/**
 * PGC Command Table.
 */ 
typedef struct {
  uint16_t nr_of_pre;
  uint16_t nr_of_post;
  uint16_t nr_of_cell;
  uint16_t zero_1;
  vm_cmd_t *pre_cmds;
  vm_cmd_t *post_cmds;
  vm_cmd_t *cell_cmds;
} ATTRIBUTE_PACKED pgc_command_tbl_t;
#define PGC_COMMAND_TBL_SIZE 8

/**
 * PGC Program Map
 */
typedef uint8_t pgc_program_map_t; 

/**
 * Cell Playback Information.
 */
typedef struct {
#ifdef WORDS_BIGENDIAN
  unsigned int block_mode       : 2;
  unsigned int block_type       : 2;
  unsigned int seamless_play    : 1;
  unsigned int interleaved      : 1;
  unsigned int stc_discontinuity: 1;
  unsigned int seamless_angle   : 1;
  
  unsigned int unknown1         : 1;
  unsigned int restricted       : 1;
  unsigned int unknown2         : 6;
#else
  unsigned int seamless_angle   : 1;
  unsigned int stc_discontinuity: 1;
  unsigned int interleaved      : 1;
  unsigned int seamless_play    : 1;
  unsigned int block_type       : 2;
  unsigned int block_mode       : 2;
  
  unsigned int unknown2         : 6;
  unsigned int restricted       : 1;
  unsigned int unknown1         : 1;
#endif
  uint8_t still_time;
  uint8_t cell_cmd_nr;
  dvd_time_t playback_time;
  uint32_t first_sector;
  uint32_t first_ilvu_end_sector;
  uint32_t last_vobu_start_sector;
  uint32_t last_sector;
} ATTRIBUTE_PACKED cell_playback_t;

#define BLOCK_TYPE_NONE         0x0
#define BLOCK_TYPE_ANGLE_BLOCK  0x1

#define BLOCK_MODE_NOT_IN_BLOCK 0x0
#define BLOCK_MODE_FIRST_CELL   0x1
#define BLOCK_MODE_IN_BLOCK     0x2
#define BLOCK_MODE_LAST_CELL    0x3

/**
 * Cell Position Information.
 */
typedef struct {
  uint16_t vob_id_nr;
  uint8_t  zero_1;
  uint8_t  cell_nr;
} ATTRIBUTE_PACKED cell_position_t;

/**
 * User Operations.
 */
typedef struct {
#ifdef WORDS_BIGENDIAN
  unsigned int zero                           : 7; // 25-31
  unsigned int video_pres_mode_change         : 1; // 24
  
  unsigned int karaoke_audio_pres_mode_change : 1; // 23
  unsigned int angle_change                   : 1; // 22
  unsigned int subpic_stream_change           : 1; // 21
  unsigned int audio_stream_change            : 1; // 20
  unsigned int pause_on                       : 1; // 19
  unsigned int still_off                      : 1; // 18
  unsigned int button_select_or_activate      : 1; // 17
  unsigned int resume                         : 1; // 16
  
  unsigned int chapter_menu_call              : 1; // 15
  unsigned int angle_menu_call                : 1; // 14
  unsigned int audio_menu_call                : 1; // 13
  unsigned int subpic_menu_call               : 1; // 12
  unsigned int root_menu_call                 : 1; // 11
  unsigned int title_menu_call                : 1; // 10
  unsigned int backward_scan                  : 1; // 9
  unsigned int forward_scan                   : 1; // 8
  
  unsigned int next_pg_search                 : 1; // 7
  unsigned int prev_or_top_pg_search          : 1; // 6
  unsigned int time_or_chapter_search         : 1; // 5
  unsigned int go_up                          : 1; // 4
  unsigned int stop                           : 1; // 3
  unsigned int title_play                     : 1; // 2
  unsigned int chapter_search_or_play         : 1; // 1
  unsigned int title_or_time_play             : 1; // 0
#else
  unsigned int video_pres_mode_change         : 1; // 24
  unsigned int zero                           : 7; // 25-31
  
  unsigned int resume                         : 1; // 16
  unsigned int button_select_or_activate      : 1; // 17
  unsigned int still_off                      : 1; // 18
  unsigned int pause_on                       : 1; // 19
  unsigned int audio_stream_change            : 1; // 20
  unsigned int subpic_stream_change           : 1; // 21
  unsigned int angle_change                   : 1; // 22
  unsigned int karaoke_audio_pres_mode_change : 1; // 23
  
  unsigned int forward_scan                   : 1; // 8
  unsigned int backward_scan                  : 1; // 9
  unsigned int title_menu_call                : 1; // 10
  unsigned int root_menu_call                 : 1; // 11
  unsigned int subpic_menu_call               : 1; // 12
  unsigned int audio_menu_call                : 1; // 13
  unsigned int angle_menu_call                : 1; // 14
  unsigned int chapter_menu_call              : 1; // 15
  
  unsigned int title_or_time_play             : 1; // 0
  unsigned int chapter_search_or_play         : 1; // 1
  unsigned int title_play                     : 1; // 2
  unsigned int stop                           : 1; // 3
  unsigned int go_up                          : 1; // 4
  unsigned int time_or_chapter_search         : 1; // 5
  unsigned int prev_or_top_pg_search          : 1; // 6
  unsigned int next_pg_search                 : 1; // 7
#endif
} ATTRIBUTE_PACKED user_ops_t;

/**
 * Program Chain Information.
 */
typedef struct {
  uint16_t zero_1;
  uint8_t  nr_of_programs;
  uint8_t  nr_of_cells;
  dvd_time_t playback_time;
  user_ops_t prohibited_ops;
  uint16_t audio_control[8]; /* New type? */
  uint32_t subp_control[32]; /* New type? */
  uint16_t next_pgc_nr;
  uint16_t prev_pgc_nr;
  uint16_t goup_pgc_nr;
  uint8_t  still_time;
  uint8_t  pg_playback_mode;
  uint32_t palette[16]; /* New type struct {zero_1, Y, Cr, Cb} ? */
  uint16_t command_tbl_offset;
  uint16_t program_map_offset;
  uint16_t cell_playback_offset;
  uint16_t cell_position_offset;
  pgc_command_tbl_t *command_tbl;
  pgc_program_map_t  *program_map;
  cell_playback_t *cell_playback;
  cell_position_t *cell_position;
} ATTRIBUTE_PACKED pgc_t;
#define PGC_SIZE 236

/**
 * Program Chain Information Search Pointer.
 */
typedef struct {
  uint8_t  entry_id;
#ifdef WORDS_BIGENDIAN
  unsigned int block_mode : 2;
  unsigned int block_type : 2;
  unsigned int unknown1   : 4;
#else
  unsigned int unknown1   : 4;
  unsigned int block_type : 2;
  unsigned int block_mode : 2;
#endif  
  uint16_t ptl_id_mask;
  uint32_t pgc_start_byte;
  pgc_t *pgc;
} ATTRIBUTE_PACKED pgci_srp_t;
#define PGCI_SRP_SIZE 8

/**
 * Program Chain Information Table.
 */
typedef struct {
  uint16_t nr_of_pgci_srp;
  uint16_t zero_1;
  uint32_t last_byte;
  pgci_srp_t *pgci_srp;
} ATTRIBUTE_PACKED pgcit_t;
#define PGCIT_SIZE 8

/**
 * Menu PGCI Language Unit.
 */
typedef struct {
  uint16_t lang_code;
  uint8_t  zero_1;
  uint8_t  exists;
  uint32_t lang_start_byte;
  pgcit_t *pgcit;
} ATTRIBUTE_PACKED pgci_lu_t;
#define PGCI_LU_SIZE 8

/**
 * Menu PGCI Unit Table.
 */
typedef struct {
  uint16_t nr_of_lus;
  uint16_t zero_1;
  uint32_t last_byte;
  pgci_lu_t *lu;
} ATTRIBUTE_PACKED pgci_ut_t;
#define PGCI_UT_SIZE 8

/**
 * Cell Address Information.
 */
typedef struct {
  uint16_t vob_id;
  uint8_t  cell_id;
  uint8_t  zero_1;
  uint32_t start_sector;
  uint32_t last_sector;
} ATTRIBUTE_PACKED cell_adr_t;

/**
 * Cell Address Table.
 */
typedef struct {
  uint16_t nr_of_vobs; /* VOBs */
  uint16_t zero_1;
  uint32_t last_byte;
  cell_adr_t *cell_adr_table;
} ATTRIBUTE_PACKED c_adt_t;
#define C_ADT_SIZE 8

/**
 * VOBU Address Map.
 */
typedef struct {
  uint32_t last_byte;
  uint32_t *vobu_start_sectors;
} ATTRIBUTE_PACKED vobu_admap_t;
#define VOBU_ADMAP_SIZE 4




/**
 * VMGI
 *
 * The following structures relate to the Video Manager.
 */

/**
 * Video Manager Information Management Table.
 */
typedef struct {
  char     vmg_identifier[12];
  uint32_t vmg_last_sector;
  uint8_t  zero_1[12];
  uint32_t vmgi_last_sector;
  uint8_t  zero_2;
  uint8_t  specification_version;
  uint32_t vmg_category;
  uint16_t vmg_nr_of_volumes;
  uint16_t vmg_this_volume_nr;
  uint8_t  disc_side;
  uint8_t  zero_3[19];
  uint16_t vmg_nr_of_title_sets;  /* Number of VTSs. */
  char     provider_identifier[32];
  uint64_t vmg_pos_code;
  uint8_t  zero_4[24];
  uint32_t vmgi_last_byte;
  uint32_t first_play_pgc;
  uint8_t  zero_5[56];
  uint32_t vmgm_vobs;             /* sector */
  uint32_t tt_srpt;               /* sector */
  uint32_t vmgm_pgci_ut;          /* sector */
  uint32_t ptl_mait;              /* sector */
  uint32_t vts_atrt;              /* sector */
  uint32_t txtdt_mgi;             /* sector */
  uint32_t vmgm_c_adt;            /* sector */
  uint32_t vmgm_vobu_admap;       /* sector */
  uint8_t  zero_6[32];
  
  video_attr_t vmgm_video_attr;
  uint8_t  zero_7;
  uint8_t  nr_of_vmgm_audio_streams; // should be 0 or 1
  audio_attr_t vmgm_audio_attr;
  audio_attr_t zero_8[7];
  uint8_t  zero_9[17];
  uint8_t  nr_of_vmgm_subp_streams; // should be 0 or 1
  subp_attr_t  vmgm_subp_attr;
  subp_attr_t  zero_10[27];  /* XXX: how much 'padding' here? */
} ATTRIBUTE_PACKED vmgi_mat_t;

typedef struct {
#ifdef WORDS_BIGENDIAN
  unsigned int zero_1                    : 1;
  unsigned int multi_or_random_pgc_title : 1; // 0 == one sequential pgc title
  unsigned int jlc_exists_in_cell_cmd    : 1;
  unsigned int jlc_exists_in_prepost_cmd : 1;
  unsigned int jlc_exists_in_button_cmd  : 1;
  unsigned int jlc_exists_in_tt_dom      : 1;
  unsigned int chapter_search_or_play    : 1; // UOP 1
  unsigned int title_or_time_play        : 1; // UOP 0
#else
  unsigned int title_or_time_play        : 1; // UOP 0
  unsigned int chapter_search_or_play    : 1; // UOP 1
  unsigned int jlc_exists_in_tt_dom      : 1;
  unsigned int jlc_exists_in_button_cmd  : 1;
  unsigned int jlc_exists_in_prepost_cmd : 1;
  unsigned int jlc_exists_in_cell_cmd    : 1;
  unsigned int multi_or_random_pgc_title : 1; // 0 == one sequential pgc title
  unsigned int zero_1                    : 1;
#endif
} ATTRIBUTE_PACKED playback_type_t;

/**
 * Title Information.
 */
typedef struct {
  playback_type_t pb_ty;
  uint8_t  nr_of_angles;
  uint16_t nr_of_ptts;
  uint16_t parental_id;
  uint8_t  title_set_nr;
  uint8_t  vts_ttn;
  uint32_t title_set_sector;
} ATTRIBUTE_PACKED title_info_t;

/**
 * PartOfTitle Search Pointer Table.
 */
typedef struct {
  uint16_t nr_of_srpts;
  uint16_t zero_1;
  uint32_t last_byte;
  title_info_t *title;
} ATTRIBUTE_PACKED tt_srpt_t;
#define TT_SRPT_SIZE 8

/**
 * Parental Management Information Unit Table.
 */
typedef struct {
  uint16_t country_code;
  uint16_t zero_1;
  uint16_t pf_ptl_mai_start_byte;
  uint16_t zero_2;
  /* uint16_t *pf_ptl_mai // table of nr_of_vtss+1 x 8 */
} ATTRIBUTE_PACKED ptl_mait_country_t;
#define PTL_MAIT_COUNTRY_SIZE 8

/**
 * Parental Management Information Table.
 */
typedef struct {
  uint16_t nr_of_countries;
  uint16_t nr_of_vtss;
  uint32_t last_byte;
  ptl_mait_country_t *countries;
} ATTRIBUTE_PACKED ptl_mait_t;
#define PTL_MAIT_SIZE 8

/**
 * Video Title Set Attributes.
 */
typedef struct {
  uint32_t last_byte;
  uint32_t vts_cat;
  
  video_attr_t vtsm_vobs_attr;
  uint8_t  zero_1;
  uint8_t  nr_of_vtsm_audio_streams; // should be 0 or 1
  audio_attr_t vtsm_audio_attr;
  audio_attr_t zero_2[7];  
  uint8_t  zero_3[16];
  uint8_t  zero_4;
  uint8_t  nr_of_vtsm_subp_streams; // should be 0 or 1
  subp_attr_t vtsm_subp_attr;
  subp_attr_t zero_5[27];
  
  uint8_t  zero_6[2];
  
  video_attr_t vtstt_vobs_video_attr;
  uint8_t  zero_7;
  uint8_t  nr_of_vtstt_audio_streams;
  audio_attr_t vtstt_audio_attr[8];
  uint8_t  zero_8[16];
  uint8_t  zero_9;
  uint8_t  nr_of_vtstt_subp_streams;
  subp_attr_t vtstt_subp_attr[32];
} ATTRIBUTE_PACKED vts_attributes_t;
#define VTS_ATTRIBUTES_SIZE 542
#define VTS_ATTRIBUTES_MIN_SIZE 356

/**
 * Video Title Set Attribute Table.
 */
typedef struct {
  uint16_t nr_of_vtss;
  uint16_t zero_1;
  uint32_t last_byte;
  vts_attributes_t *vts;
} ATTRIBUTE_PACKED vts_atrt_t;
#define VTS_ATRT_SIZE 8

/**
 * Text Data. (Incomplete)
 */
typedef struct {
  uint32_t last_byte;    /* offsets are relative here */
  uint16_t offsets[100]; /* == nr_of_srpts + 1 (first is disc title) */
#if 0  
  uint16_t unknown; // 0x48 ?? 0x48 words (16bit) info following
  uint16_t zero_1;
  
  uint8_t type_of_info;//?? 01 == disc, 02 == Title, 04 == Title part 
  uint8_t unknown1;
  uint8_t unknown2;
  uint8_t unknown3;
  uint8_t unknown4;//?? allways 0x30 language?, text format?
  uint8_t unknown5;
  uint16_t offset; // from first 
  
  char text[12]; // ended by 0x09
#endif
} ATTRIBUTE_PACKED txtdt_t;

/**
 * Text Data Language Unit. (Incomplete)
 */ 
typedef struct {
  uint16_t lang_code;
  uint16_t unknown;      /* 0x0001, title 1? disc 1? side 1? */
  uint32_t txtdt_start_byte;  /* prt, rel start of vmg_txtdt_mgi  */
  txtdt_t  *txtdt;
} ATTRIBUTE_PACKED txtdt_lu_t;
#define TXTDT_LU_SIZE 8

/**
 * Text Data Manager Information. (Incomplete)
 */
typedef struct {
  char disc_name[14];            /* how many bytes?? */
  uint16_t nr_of_language_units; /* 32bit??          */
  uint32_t last_byte;
  txtdt_lu_t *lu;
} ATTRIBUTE_PACKED txtdt_mgi_t;
#define TXTDT_MGI_SIZE 20


/**
 * VTS
 *
 * Structures relating to the Video Title Set (VTS).
 */

/**
 * Video Title Set Information Management Table.
 */
typedef struct {
  char vts_identifier[12];
  uint32_t vts_last_sector;
  uint8_t  zero_1[12];
  uint32_t vtsi_last_sector;
  uint8_t  zero_2;
  uint8_t  specification_version;
  uint32_t vts_category;
  uint16_t zero_3;
  uint16_t zero_4;
  uint8_t  zero_5;
  uint8_t  zero_6[19];
  uint16_t zero_7;
  uint8_t  zero_8[32];
  uint64_t zero_9;
  uint8_t  zero_10[24];
  uint32_t vtsi_last_byte;
  uint32_t zero_11;
  uint8_t  zero_12[56];
  uint32_t vtsm_vobs;       /* sector */
  uint32_t vtstt_vobs;      /* sector */
  uint32_t vts_ptt_srpt;    /* sector */
  uint32_t vts_pgcit;       /* sector */
  uint32_t vtsm_pgci_ut;    /* sector */
  uint32_t vts_tmapt;       /* sector */  // XXX: FIXME TODO Implement
  uint32_t vtsm_c_adt;      /* sector */
  uint32_t vtsm_vobu_admap; /* sector */
  uint32_t vts_c_adt;       /* sector */
  uint32_t vts_vobu_admap;  /* sector */
  uint8_t  zero_13[24];
  
  video_attr_t vtsm_video_attr;
  uint8_t  zero_14;
  uint8_t  nr_of_vtsm_audio_streams; // should be 0 or 1
  audio_attr_t vtsm_audio_attr;
  audio_attr_t zero_15[7];
  uint8_t  zero_16[17];
  uint8_t  nr_of_vtsm_subp_streams; // should be 0 or 1
  subp_attr_t vtsm_subp_attr;
  subp_attr_t zero_17[27];
  uint8_t  zero_18[2];
  
  video_attr_t vts_video_attr;
  uint8_t  zero_19;
  uint8_t  nr_of_vts_audio_streams;
  audio_attr_t vts_audio_attr[8];
  uint8_t  zero_20[17];
  uint8_t  nr_of_vts_subp_streams;
  subp_attr_t vts_subp_attr[32];
  /* XXX: how much 'padding' here, if any? */
} ATTRIBUTE_PACKED vtsi_mat_t;

/**
 * PartOfTitle Unit Information.
 */
typedef struct {
  uint16_t pgcn;
  uint16_t pgn;
} ATTRIBUTE_PACKED ptt_info_t;

/**
 * PartOfTitle Information.
 */
typedef struct {
  uint16_t nr_of_ptts;
  ptt_info_t *ptt;
} ATTRIBUTE_PACKED ttu_t;

/**
 * PartOfTitle Search Pointer Table.
 */
typedef struct {
  uint16_t nr_of_srpts;
  uint16_t zero_1;
  uint32_t last_byte;
  ttu_t  *title;
} ATTRIBUTE_PACKED vts_ptt_srpt_t;
#define VTS_PTT_SRPT_SIZE 8


#if PRAGMA_PACK
#pragma pack()
#endif


/**
 * The following structure defines an IFO file.  The structure is divided into
 * two parts, the VMGI, or Video Manager Information, which is read from the
 * VIDEO_TS.[IFO,BUP] file, and the VTSI, or Video Title Set Information, which
 * is read in from the VTS_XX_0.[IFO,BUP] files.
 */
typedef struct {
  dvd_file_t *file;
  
  /* VMGI */
  vmgi_mat_t     *vmgi_mat;
  tt_srpt_t      *tt_srpt;
  pgc_t          *first_play_pgc;    
  ptl_mait_t     *ptl_mait;
  vts_atrt_t     *vts_atrt;
  txtdt_mgi_t    *txtdt_mgi;
  
  /* Common */
  pgci_ut_t      *pgci_ut;
  c_adt_t        *menu_c_adt;
  vobu_admap_t   *menu_vobu_admap;
  
  /* VTSI */
  vtsi_mat_t     *vtsi_mat;
  vts_ptt_srpt_t *vts_ptt_srpt;
  pgcit_t        *vts_pgcit;
  int            *vts_tmapt; // FIXME add/correct the type
  c_adt_t        *vts_c_adt;
  vobu_admap_t   *vts_vobu_admap;
} ifo_handle_t;

#endif /* IFO_TYPES_H_INCLUDED */

--- NEW FILE ---
/*****************************************************************************
 * int_types.h: internal types
 *****************************************************************************
 * Copyright (C) 1999, 2000 VideoLAN
 * $Id: int_types.h,v 1.1 2002/04/24 19:28:03 arpi Exp $
 *
 * Authors: Vincent Seguin <seguin at via.ecp.fr>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/* Basic types definitions */
typedef unsigned char           u8;
typedef signed char             s8;
typedef unsigned short          u16;
typedef signed short            s16;
typedef unsigned int            u32;
typedef signed int              s32;
#if defined( _MSC_VER )
typedef unsigned __int64        u64;
typedef signed __int64          s64;
#else
typedef unsigned long long      u64;
typedef signed long long        s64;
#endif

--- NEW FILE ---
/*****************************************************************************
 * ioctl.c: DVD ioctl replacement function
 *****************************************************************************
 * Copyright (C) 1999-2001 VideoLAN
 * $Id: ioctl.c,v 1.1 2002/04/24 19:28:03 arpi Exp $
 *
 * Authors: Markus Kuespert <ltlBeBoy at beosmail.com>
 *          Samuel Hocevar <sam at zoy.org>
 *          Jon Lech Johansen <jon-vl at nanocrew.net>
 *          Håkan Hjort <d95hjort at 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
[...1129 lines suppressed...]
    hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
    if( hEvent == NULL )
    {
        return -1;
    }

    p_ssc->SRB_PostProc  = hEvent;
    p_ssc->SRB_HaId      = LOBYTE( fd->i_sid );
    p_ssc->SRB_Target    = HIBYTE( fd->i_sid );

    ResetEvent( hEvent );
    if( fd->lpSendCommand( (void*) p_ssc ) == SS_PENDING )
        WaitForSingleObject( hEvent, INFINITE );

    CloseHandle( hEvent );

    return p_ssc->SRB_Status == SS_COMP ? 0 : -1;
}
#endif


--- NEW FILE ---
/*****************************************************************************
 * ioctl.h: DVD ioctl replacement function
 *****************************************************************************
 * Copyright (C) 1999-2001 VideoLAN
 * $Id: ioctl.h,v 1.1 2002/04/24 19:28:03 arpi Exp $
 *
 * Authors: Samuel Hocevar <sam at zoy.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

int ioctl_ReadCopyright     ( int, int, int * );
int ioctl_ReadKey           ( int, int *, u8 * );

int ioctl_ReportAgid        ( int, int * );
int ioctl_ReportChallenge   ( int, int *, u8 * );
int ioctl_ReportKey1        ( int, int *, u8 * );
int ioctl_ReportASF         ( int, int *, int * );
int ioctl_InvalidateAgid    ( int, int * );
int ioctl_SendChallenge     ( int, int *, u8 * );
int ioctl_SendKey2          ( int, int *, u8 * );

/*****************************************************************************
 * Common macro, BeOS specific
 *****************************************************************************/
#if defined( SYS_BEOS )
#define INIT_RDC( TYPE, SIZE ) \
    raw_device_command rdc; \
    u8 p_buffer[ (SIZE) ]; \
    memset( &rdc, 0, sizeof( raw_device_command ) ); \
    rdc.data = (char *)p_buffer; \
    rdc.data_length = (SIZE); \
    BeInitRDC( &rdc, (TYPE) );
#endif

/*****************************************************************************
 * Common macro, Solaris specific
 *****************************************************************************/
#if defined( SOLARIS_USCSI )
#define USCSI_TIMEOUT( SC, TO ) ( (SC)->uscsi_timeout = (TO) )
#define USCSI_RESID( SC )       ( (SC)->uscsi_resid )
#define INIT_USCSI( TYPE, SIZE ) \
    struct uscsi_cmd sc; \
    union scsi_cdb rs_cdb; \
    u8 p_buffer[ (SIZE) ]; \
    memset( &sc, 0, sizeof( struct uscsi_cmd ) ); \
    sc.uscsi_cdb = (caddr_t)&rs_cdb; \
    sc.uscsi_bufaddr = p_buffer; \
    sc.uscsi_buflen = (SIZE); \
    SolarisInitUSCSI( &sc, (TYPE) );
#endif

/*****************************************************************************
 * Common macro, Darwin specific
 *****************************************************************************/
#if defined( SYS_DARWIN )
#define INIT_DVDIOCTL( SIZE ) \
    dvdioctl_data_t dvdioctl; \
    u8 p_buffer[ (SIZE) ]; \
    dvdioctl.p_buffer = p_buffer; \
    dvdioctl.i_size = (SIZE); \
    dvdioctl.i_keyclass = kCSS_CSS2_CPRM; \
    memset( p_buffer, 0, (SIZE) );
#endif

/*****************************************************************************
 * Common macro, win32 (ASPI) specific
 *****************************************************************************/
#if defined( WIN32 )
#define INIT_SSC( TYPE, SIZE ) \
    struct SRB_ExecSCSICmd ssc; \
    u8 p_buffer[ (SIZE) ]; \
    memset( &ssc, 0, sizeof( struct SRB_ExecSCSICmd ) ); \
    ssc.SRB_BufPointer = (char *)p_buffer; \
    ssc.SRB_BufLen = (SIZE); \
    WinInitSSC( &ssc, (TYPE) );
#endif

/*****************************************************************************
 * Various DVD I/O tables
 *****************************************************************************/

#if defined( SYS_BEOS ) || defined( WIN32 ) || defined ( SOLARIS_USCSI )
    /* The generic packet command opcodes for CD/DVD Logical Units,
     * From Table 57 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
#   define GPCMD_READ_DVD_STRUCTURE 0xad
#   define GPCMD_REPORT_KEY         0xa4
#   define GPCMD_SEND_KEY           0xa3
    /* DVD struct types */
#   define DVD_STRUCT_PHYSICAL      0x00
#   define DVD_STRUCT_COPYRIGHT     0x01
#   define DVD_STRUCT_DISCKEY       0x02
#   define DVD_STRUCT_BCA           0x03
#   define DVD_STRUCT_MANUFACT      0x04
    /* Key formats */
#   define DVD_REPORT_AGID          0x00
#   define DVD_REPORT_CHALLENGE     0x01
#   define DVD_SEND_CHALLENGE       0x01
#   define DVD_REPORT_KEY1          0x02
#   define DVD_SEND_KEY2            0x03
#   define DVD_REPORT_ASF           0x05
#   define DVD_INVALIDATE_AGID      0x3f
#endif

#if defined( WIN32 )

/*****************************************************************************
 * win32 ioctl specific
 *****************************************************************************/

#define IOCTL_DVD_START_SESSION         CTL_CODE(FILE_DEVICE_DVD, 0x0400, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_DVD_READ_KEY              CTL_CODE(FILE_DEVICE_DVD, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_DVD_SEND_KEY              CTL_CODE(FILE_DEVICE_DVD, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS)
#define IOCTL_DVD_END_SESSION           CTL_CODE(FILE_DEVICE_DVD, 0x0403, METHOD_BUFFERED, FILE_READ_ACCESS)

#define IOCTL_SCSI_PASS_THROUGH_DIRECT  CTL_CODE(FILE_DEVICE_CONTROLLER, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)

#define DVD_CHALLENGE_KEY_LENGTH        (12 + sizeof(DVD_COPY_PROTECT_KEY))
#define DVD_BUS_KEY_LENGTH              (8 + sizeof(DVD_COPY_PROTECT_KEY))
#define DVD_DISK_KEY_LENGTH             (2048 + sizeof(DVD_COPY_PROTECT_KEY))
#define DVD_ASF_LENGTH                  (sizeof(DVD_ASF) + sizeof(DVD_COPY_PROTECT_KEY))

#define SCSI_IOCTL_DATA_OUT             0
#define SCSI_IOCTL_DATA_IN              1

typedef ULONG DVD_SESSION_ID, *PDVD_SESSION_ID;

typedef enum
{
    DvdChallengeKey = 0x01,
    DvdBusKey1,
    DvdBusKey2,
    DvdTitleKey,
    DvdAsf,
    DvdSetRpcKey = 0x6,
    DvdGetRpcKey = 0x8,
    DvdDiskKey = 0x80,
    DvdInvalidateAGID = 0x3f
} DVD_KEY_TYPE;

typedef struct _DVD_COPY_PROTECT_KEY
{
    ULONG KeyLength;
    DVD_SESSION_ID SessionId;
    DVD_KEY_TYPE KeyType;
    ULONG KeyFlags;
    union
    {
        struct
        {
            ULONG FileHandle;
            ULONG Reserved;   // used for NT alignment
        };
        LARGE_INTEGER TitleOffset;
    } Parameters;
    UCHAR KeyData[0];
} DVD_COPY_PROTECT_KEY, *PDVD_COPY_PROTECT_KEY;

typedef struct _DVD_ASF
{
    UCHAR Reserved0[3];
    UCHAR SuccessFlag:1;
    UCHAR Reserved1:7;
} DVD_ASF, * PDVD_ASF;

typedef struct _SCSI_PASS_THROUGH_DIRECT
{
    USHORT Length;
    UCHAR ScsiStatus;
    UCHAR PathId;
    UCHAR TargetId;
    UCHAR Lun;
    UCHAR CdbLength;
    UCHAR SenseInfoLength;
    UCHAR DataIn;
    ULONG DataTransferLength;
    ULONG TimeOutValue;
    PVOID DataBuffer;
    ULONG SenseInfoOffset;
    UCHAR Cdb[16];
} SCSI_PASS_THROUGH_DIRECT, *PSCSI_PASS_THROUGH_DIRECT;

/*****************************************************************************
 * win32 aspi specific
 *****************************************************************************/

#define WIN2K               ( GetVersion() < 0x80000000 )
#define ASPI_HAID           0
#define ASPI_TARGET         0

#define SENSE_LEN           0x0E
#define SC_EXEC_SCSI_CMD    0x02
#define SC_GET_DISK_INFO    0x06
#define SS_COMP             0x01
#define SS_PENDING          0x00
#define SS_NO_ADAPTERS      0xE8
#define SRB_DIR_IN          0x08
#define SRB_DIR_OUT         0x10
#define SRB_EVENT_NOTIFY    0x40

struct w32_aspidev
{
    long  hASPI;
    short i_sid;
    int   i_blocks;
    long  (*lpSendCommand)( void* );
};

#pragma pack(1)

struct SRB_GetDiskInfo
{
    unsigned char   SRB_Cmd;
    unsigned char   SRB_Status;
    unsigned char   SRB_HaId;
    unsigned char   SRB_Flags;
    unsigned long   SRB_Hdr_Rsvd;
    unsigned char   SRB_Target;
    unsigned char   SRB_Lun;
    unsigned char   SRB_DriveFlags;
    unsigned char   SRB_Int13HDriveInfo;
    unsigned char   SRB_Heads;
    unsigned char   SRB_Sectors;
    unsigned char   SRB_Rsvd1[22];
};

struct SRB_ExecSCSICmd
{
    unsigned char   SRB_Cmd;
    unsigned char   SRB_Status;
    unsigned char   SRB_HaId;
    unsigned char   SRB_Flags;
    unsigned long   SRB_Hdr_Rsvd;
    unsigned char   SRB_Target;
    unsigned char   SRB_Lun;
    unsigned short  SRB_Rsvd1;
    unsigned long   SRB_BufLen;
    unsigned char   *SRB_BufPointer;
    unsigned char   SRB_SenseLen;
    unsigned char   SRB_CDBLen;
    unsigned char   SRB_HaStat;
    unsigned char   SRB_TargStat;
    unsigned long   *SRB_PostProc;
    unsigned char   SRB_Rsvd2[20];
    unsigned char   CDBByte[16];
    unsigned char   SenseArea[SENSE_LEN+2];
};

#pragma pack()

#endif


--- NEW FILE ---
/*****************************************************************************
 * libdvdcss.c: DVD reading library.
 *****************************************************************************
 * Copyright (C) 1998-2001 VideoLAN
 * $Id: libdvdcss.c,v 1.1 2002/04/24 19:28:03 arpi Exp $
 *
 * Authors: Stéphane Borel <stef at via.ecp.fr>
 *          Samuel Hocevar <sam at zoy.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#if defined( WIN32 )
#   include <io.h>                                                 /* read() */
#else
#   include <sys/uio.h>                                      /* struct iovec */
#endif

#include "common.h"

#if defined( WIN32 )
#   include "input_iovec.h"
#endif

#include "dvdcss.h"
#include "dvd_reader.h"
#include "libdvdcss.h"
#include "ioctl.h"

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static int _dvdcss_open  ( dvdcss_handle, char *psz_target );
static int _dvdcss_close ( dvdcss_handle );
static int _dvdcss_seek  ( dvdcss_handle, int i_blocks );
static int _dvdcss_read  ( dvdcss_handle, void *p_buffer, int i_blocks );
static int _dvdcss_readv ( dvdcss_handle, struct iovec *p_iovec, int i_blocks );

/*****************************************************************************
 * Local prototypes, win32 specific
 *****************************************************************************/
#if defined( WIN32 )
static int _win32_dvdcss_readv  ( int i_fd, struct iovec *p_iovec,
                                  int i_num_buffers, char *p_tmp_buffer );
static int _win32_dvdcss_aopen  ( char c_drive, dvdcss_handle dvdcss );
static int _win32_dvdcss_aclose ( int i_fd );
static int _win32_dvdcss_aseek  ( int i_fd, int i_blocks, int i_method );
static int _win32_dvdcss_aread  ( int i_fd, void *p_data, int i_blocks );
#endif

/*****************************************************************************
 * dvdcss_open: initialize library, open a DVD device, crack CSS key
 *****************************************************************************/
extern dvdcss_handle dvdcss_open ( char *psz_target, int i_flags )
{
    int i_ret;

    dvdcss_handle dvdcss;

    /* Allocate the library structure */
    dvdcss = malloc( sizeof( struct dvdcss_s ) );
    if( dvdcss == NULL )
    {
        if( ! (i_flags & DVDCSS_INIT_QUIET) )
        {
            DVDCSS_ERROR( "could not initialize library" );
        }

        return NULL;
    }

    /* Initialize structure */
    dvdcss->p_titles = NULL;
    dvdcss->b_debug = i_flags & DVDCSS_INIT_DEBUG;
    dvdcss->b_errors = !(i_flags & DVDCSS_INIT_QUIET);
    dvdcss->psz_error = "no error";

    i_ret = _dvdcss_open( dvdcss, psz_target );
    if( i_ret < 0 )
    {
        free( dvdcss );
        return NULL;
    }

    i_ret = CSSTest( dvdcss );
    if( i_ret < 0 )
    {
        _dvdcss_error( dvdcss, "css test failed" );
        _dvdcss_close( dvdcss );
        free( dvdcss );
        return NULL;
    }

    dvdcss->b_encrypted = i_ret;

    /* If drive is encrypted, crack its key */
    if( dvdcss->b_encrypted )
    {
        i_ret = CSSInit( dvdcss );

        if( i_ret < 0 )
        {
            _dvdcss_close( dvdcss );
            free( dvdcss );
            return NULL;
        }
    }

    return dvdcss;
}

/*****************************************************************************
 * dvdcss_error: return the last libdvdcss error message
 *****************************************************************************/
extern char * dvdcss_error ( dvdcss_handle dvdcss )
{
    return dvdcss->psz_error;
}

/*****************************************************************************
 * dvdcss_seek: seek into the device
 *****************************************************************************/
extern int dvdcss_seek ( dvdcss_handle dvdcss, int i_blocks )
{
    return _dvdcss_seek( dvdcss, i_blocks );
}

/*****************************************************************************
 * dvdcss_title: crack the current title key if needed
 *****************************************************************************/
extern int dvdcss_title ( dvdcss_handle dvdcss, int i_block )
{
    dvd_title_t *p_title;
    dvd_key_t p_key;
    int i_ret;
    char * key_file = NULL;

    if( ! dvdcss->b_encrypted )
    {
        return 0;
    }

    //fprintf( stderr, "looking for a key for offset %i\n", i_block );

    /* Check if we've already cracked this key */
    p_title = dvdcss->p_titles;
    while( p_title != NULL
            && p_title->p_next != NULL
            && p_title->p_next->i_startlb <= i_block )
    {
        p_title = p_title->p_next;
    }

    if( p_title != NULL
         && p_title->i_startlb == i_block )
    {
        /* We've already cracked this key, nothing to do */
        return 0;
    }

    if ( dvd_key_dir )
     {
      int fd;
      
      if ( (key_file=calloc( 1,strlen( dvd_key_dir ) + 11 )) == NULL ) return -1;
      sprintf( key_file,"%s/%0.10x",dvd_key_dir,i_block );

      fprintf( stderr,"dvdcss: opening %s to look for key\n",key_file );
      if ( (fd=open( key_file,O_RDONLY ) ) < 0 )
       {
	fprintf(stderr, "dvdcss: no such file, cracking normal\n");
	goto normal;
       }
      read(fd, p_key, 5);
      close( fd );
      goto goon;
     }

    normal:
    /* Crack CSS title key for current VTS */
    i_ret = CSSGetKey( dvdcss, i_block, p_key );

    if( i_ret < 0 )
    {
        _dvdcss_error( dvdcss, "fatal error in vts css key" );
        return i_ret;
    }
    else if( i_ret > 0 )
    {
        _dvdcss_error( dvdcss, "decryption unavailable" );
        return -1;
    }
	goon:

    //fprintf(stderr, "cracked key is %.2x %.2x %.2x %.2x %.2x\n",
    //         p_key[0], p_key[1], p_key[2], p_key[3], p_key[4] );

    /* Add key to keytable if it isn't empty */
    if( p_key[0] | p_key[1] | p_key[2] | p_key[3] | p_key[4] )
     {
      dvd_title_t *p_newtitle;

      if ( dvd_key_dir )
       {
	int fd;
	fprintf( stderr,"dvdcss: writing key to %s\n",key_file );
	if ( (fd=open( key_file,O_RDWR|O_CREAT|O_EXCL,0644 ) ) < 0 ) 
	 {
//	    fprintf(stderr, "dvdcss: file %s exists or write/permission error, doing nothing\n", tmp);
          goto normal2;
	 }
	write( fd,p_key,5 );
        close( fd );
	free( key_file );
       }

       normal2:
       /* Find our spot in the list */
       p_newtitle = NULL;
       p_title = dvdcss->p_titles;
       while( p_title != NULL && p_title->i_startlb < i_block )
        {
         p_newtitle = p_title;
         p_title = p_title->p_next;
        }

       /* Save the found title */
       p_title = p_newtitle;

       /* Write in the new title and its key */
       p_newtitle = malloc( sizeof( dvd_title_t ) );
       p_newtitle->i_startlb = i_block;
       memcpy( p_newtitle->p_key, p_key, KEY_SIZE );

       /* Link the new title, either at the beginning or inside the list */
       if( p_title == NULL )
        {
         dvdcss->p_titles = p_newtitle;
         p_newtitle->p_next = NULL;
        }
        else
         {
          p_newtitle->p_next = p_title->p_next;
          p_title->p_next = p_newtitle;
         }
    }
 return 0;
}

/*****************************************************************************
 * dvdcss_read: read data from the device, decrypt if requested
 *****************************************************************************/
extern int dvdcss_read ( dvdcss_handle dvdcss, void *p_buffer,
                                               int i_blocks,
                                               int i_flags )
{
    dvd_title_t *p_title;
    int i_ret, i_index;

    i_ret = _dvdcss_read( dvdcss, p_buffer, i_blocks );

    if( i_ret <= 0
         || !dvdcss->b_encrypted
         || !(i_flags & DVDCSS_READ_DECRYPT) )
    {
        return i_ret;
    }

    /* find our key */
    p_title = dvdcss->p_titles;
    while( p_title != NULL
            && p_title->p_next
            && p_title->p_next->i_startlb <= dvdcss->i_seekpos )
    {
        p_title = p_title->p_next;
    }

    if( p_title == NULL )
    {
        /* no css key found to use, so no decryption to do */
        return 0;
    }

    /* Decrypt the blocks we managed to read */
    for( i_index = i_ret; i_index; i_index-- )
    {
        CSSDescrambleSector( p_title->p_key, p_buffer );
        ((u8*)p_buffer)[0x14] &= 0x8f;
        (u8*)p_buffer += DVDCSS_BLOCK_SIZE;
    }

    return i_ret;
}

/*****************************************************************************
 * dvdcss_readv: read data to an iovec structure, decrypt if reaquested
 *****************************************************************************/
extern int dvdcss_readv ( dvdcss_handle dvdcss, void *p_iovec,
                                                int i_blocks,
                                                int i_flags )
{
#define P_IOVEC ((struct iovec*)p_iovec)
    dvd_title_t *p_title;
    int i_ret, i_index;
    void *iov_base;
    size_t iov_len;

    i_ret = _dvdcss_readv( dvdcss, P_IOVEC, i_blocks );

    if( i_ret <= 0
         || !dvdcss->b_encrypted
         || !(i_flags & DVDCSS_READ_DECRYPT) )
    {
        return i_ret;
    }

    /* Find our key */
    p_title = dvdcss->p_titles;
    while( p_title != NULL
            && p_title->p_next != NULL
            && p_title->p_next->i_startlb <= dvdcss->i_seekpos )
    {
        p_title = p_title->p_next;
    }

    if( p_title == NULL )
    {
        /* no css key found to use, so no decryption to do */
        return 0;
    }

    /* Initialize loop for decryption */
    iov_base = P_IOVEC->iov_base;
    iov_len = P_IOVEC->iov_len;

    /* Decrypt the blocks we managed to read */
    for( i_index = i_ret; i_index; i_index-- )
    {
        /* Check that iov_len is a multiple of 2048 */
        if( iov_len & 0x7ff )
        {
            return -1;
        }

        while( iov_len == 0 )
        {
            P_IOVEC++;
            iov_base = P_IOVEC->iov_base;
            iov_len = P_IOVEC->iov_len;
        }

        CSSDescrambleSector( p_title->p_key, iov_base );
        ((u8*)iov_base)[0x14] &= 0x8f;

        (u8*)iov_base += DVDCSS_BLOCK_SIZE;
        (u8*)iov_len -= DVDCSS_BLOCK_SIZE;
    }

    return i_ret;
#undef P_IOVEC
}

/*****************************************************************************
 * dvdcss_close: close the DVD device and clean up the library
 *****************************************************************************/
extern int dvdcss_close ( dvdcss_handle dvdcss )
{
    dvd_title_t *p_title;
    int i_ret;

    /* Free our list of keys */
    p_title = dvdcss->p_titles;
    while( p_title )
    {
        dvd_title_t *p_tmptitle = p_title->p_next;
        free( p_title );
        p_title = p_tmptitle;
    }

    i_ret = _dvdcss_close( dvdcss );

    if( i_ret < 0 )
    {
        return i_ret;
    }

    free( dvdcss );

    return 0;
}

/* Following functions are local */

static int _dvdcss_open ( dvdcss_handle dvdcss, char *psz_target )
{
#if defined( WIN32 )
    if( WIN2K )
    {
        char psz_dvd[7];
        _snprintf( psz_dvd, 7, "\\\\.\\%c:", psz_target[0] );
        (HANDLE) dvdcss->i_fd =
                CreateFile( psz_dvd, GENERIC_READ | GENERIC_WRITE,
                                FILE_SHARE_READ | FILE_SHARE_WRITE,
                                NULL, OPEN_EXISTING, 0, NULL );
        if( (HANDLE) dvdcss->i_fd == INVALID_HANDLE_VALUE )
        {
            _dvdcss_error( dvdcss, "failed opening device" );
            return -1;
        }
    }
    else
    {
        dvdcss->i_fd = _win32_dvdcss_aopen( psz_target[0], dvdcss );
        if( dvdcss->i_fd == -1 )
        {
            _dvdcss_error( dvdcss, "failed opening device" );
            return -1;
        }
    }

    /* initialise readv temporary buffer */
    dvdcss->p_readv_buffer   = NULL;
    dvdcss->i_readv_buf_size = 0;

#else
    dvdcss->i_fd = open( psz_target, 0 );

    if( dvdcss->i_fd == -1 )
    {
        _dvdcss_error( dvdcss, "failed opening device" );
        return -1;
    }

#endif

    return 0;
}

static int _dvdcss_close ( dvdcss_handle dvdcss )
{
#if defined( WIN32 )
    if( WIN2K )
    {
        CloseHandle( (HANDLE) dvdcss->i_fd );
    }
    else
    {
        _win32_dvdcss_aclose( dvdcss->i_fd );
    }

    /* Free readv temporary buffer */
    if( dvdcss->p_readv_buffer )
    {
        free( dvdcss->p_readv_buffer );
        dvdcss->p_readv_buffer   = NULL;
        dvdcss->i_readv_buf_size = 0;
    }

#else
    close( dvdcss->i_fd );

#endif

    return 0;
}

static int _dvdcss_seek ( dvdcss_handle dvdcss, int i_blocks )
{
#if defined( WIN32 )
    dvdcss->i_seekpos = i_blocks;

    if( WIN2K )
    {
        LARGE_INTEGER li_read;

#ifndef INVALID_SET_FILE_POINTER
#define INVALID_SET_FILE_POINTER ((DWORD)-1)
#endif

        li_read.QuadPart = (LONGLONG)i_blocks * DVDCSS_BLOCK_SIZE;

        li_read.LowPart = SetFilePointer( (HANDLE) dvdcss->i_fd,
                                          li_read.LowPart,
                                          &li_read.HighPart, FILE_BEGIN );
        if( (li_read.LowPart == INVALID_SET_FILE_POINTER)
            && GetLastError() != NO_ERROR)
        {
            li_read.QuadPart = -DVDCSS_BLOCK_SIZE;
        }

        li_read.QuadPart /= DVDCSS_BLOCK_SIZE;
        return (int)li_read.QuadPart;
    }
    else
    {
        return ( _win32_dvdcss_aseek( dvdcss->i_fd, i_blocks, SEEK_SET ) );
    }
#else
    off_t i_read;

    dvdcss->i_seekpos = i_blocks;

    i_read = lseek( dvdcss->i_fd,
                    (off_t)i_blocks * (off_t)DVDCSS_BLOCK_SIZE, SEEK_SET );

    return i_read / DVDCSS_BLOCK_SIZE;
#endif

}

static int _dvdcss_read ( dvdcss_handle dvdcss, void *p_buffer, int i_blocks )
{
#if defined( WIN32 ) 
    if( WIN2K )
    {
        int i_bytes;

        if( !ReadFile( (HANDLE) dvdcss->i_fd, p_buffer,
                  i_blocks * DVDCSS_BLOCK_SIZE,
                  (LPDWORD)&i_bytes, NULL ) )
        {
            return -1;
        }
        return i_bytes / DVDCSS_BLOCK_SIZE;
    }
    else
    {
        return _win32_dvdcss_aread( dvdcss->i_fd, p_buffer, i_blocks );
    }

#else
    int i_bytes;

    i_bytes = read( dvdcss->i_fd, p_buffer,
                    (size_t)i_blocks * DVDCSS_BLOCK_SIZE );
    return i_bytes / DVDCSS_BLOCK_SIZE;
#endif

}

static int _dvdcss_readv ( dvdcss_handle dvdcss, struct iovec *p_iovec,
                           int i_blocks )
{
    int i_read;

#if defined( WIN32 )
    /* Check the size of the readv temp buffer, just in case we need to
     * realloc something bigger */
    if( dvdcss->i_readv_buf_size < i_blocks * DVDCSS_BLOCK_SIZE )
    {
        dvdcss->i_readv_buf_size = i_blocks * DVDCSS_BLOCK_SIZE;

        if( dvdcss->p_readv_buffer ) free( dvdcss->p_readv_buffer );

        /* Allocate a buffer which will be used as a temporary storage
         * for readv */
        dvdcss->p_readv_buffer = malloc( dvdcss->i_readv_buf_size );
        if( !dvdcss->p_readv_buffer )
        {
            _dvdcss_error( dvdcss, " failed (readv)" );
            return -1;
        }
    }

    i_read = _win32_dvdcss_readv( dvdcss->i_fd, p_iovec, i_blocks,
                                  dvdcss->p_readv_buffer );
    return i_read;

#else
    i_read = readv( dvdcss->i_fd, p_iovec, i_blocks );
    return i_read / DVDCSS_BLOCK_SIZE;

#endif
}


#if defined( WIN32 )

/*****************************************************************************
 * _win32_dvdcss_readv: vectored read using ReadFile for Win2K and
 *                      _win32_dvdcss_aread for win9x
 *****************************************************************************/
static int _win32_dvdcss_readv( int i_fd, struct iovec *p_iovec,
                                int i_num_buffers, char *p_tmp_buffer )
{
    int i_index;
    int i_blocks, i_blocks_total = 0;

    for( i_index = i_num_buffers; i_index; i_index-- )
    {
        i_blocks_total += p_iovec[i_index-1].iov_len; 
    }

    if( i_blocks_total <= 0 ) return 0;

    i_blocks_total /= DVDCSS_BLOCK_SIZE;

    if( WIN2K )
    {
        unsigned long int i_bytes;
        if( !ReadFile( (HANDLE)i_fd, p_tmp_buffer,
                       i_blocks_total * DVDCSS_BLOCK_SIZE, &i_bytes, NULL ) )
        {
            return -1;
            /* The read failed... too bad.
               As in the posix spec the file postition is left
               unspecified after a failure */
        }
        i_blocks = i_bytes / DVDCSS_BLOCK_SIZE;
    }
    else /* Win9x */
    {
        i_blocks = _win32_dvdcss_aread( i_fd, p_tmp_buffer, i_blocks_total );
        if( i_blocks < 0 )
        {
            return -1;  /* idem */
        }
    }

    /* We just have to copy the content of the temp buffer into the iovecs */
    i_index = 0;
    i_blocks_total = i_blocks;
    while( i_blocks_total > 0 )
    {
        memcpy( p_iovec[i_index].iov_base,
                &p_tmp_buffer[(i_blocks - i_blocks_total) * DVDCSS_BLOCK_SIZE],
                p_iovec[i_index].iov_len );
        /* if we read less blocks than asked, we'll just end up copying
           garbage, this isn't an issue as we return the number of
           blocks actually read */
        i_blocks_total -= ( p_iovec[i_index].iov_len / DVDCSS_BLOCK_SIZE );
        i_index++;
    } 

    return i_blocks;
}

/*****************************************************************************
 * _win32_dvdcss_aopen: open dvd drive (load aspi and init w32_aspidev
 *                      structure)
 *****************************************************************************/
static int _win32_dvdcss_aopen( char c_drive, dvdcss_handle dvdcss )
{
    HMODULE hASPI;
    DWORD dwSupportInfo;
    struct w32_aspidev *fd;
    int i, j, i_hostadapters;
    long (*lpGetSupport)( void );
    long (*lpSendCommand)( void* );
     
    hASPI = LoadLibrary( "wnaspi32.dll" );
    if( hASPI == NULL )
    {
        _dvdcss_error( dvdcss, "unable to load wnaspi32.dll" );
        return -1;
    }

    (FARPROC) lpGetSupport = GetProcAddress( hASPI, "GetASPI32SupportInfo" );
    (FARPROC) lpSendCommand = GetProcAddress( hASPI, "SendASPI32Command" );
 
    if(lpGetSupport == NULL || lpSendCommand == NULL )
    {
        _dvdcss_debug( dvdcss, "unable to get aspi function pointers" );
        FreeLibrary( hASPI );
        return -1;
    }

    dwSupportInfo = lpGetSupport();

    if( HIBYTE( LOWORD ( dwSupportInfo ) ) == SS_NO_ADAPTERS )
    {
        _dvdcss_debug( dvdcss, "no host adapters found (aspi)" );
        FreeLibrary( hASPI );
        return -1;
    }

    if( HIBYTE( LOWORD ( dwSupportInfo ) ) != SS_COMP )
    {
        _dvdcss_error( dvdcss, "unable to initalize aspi layer" );
        FreeLibrary( hASPI );
        return -1;
    }

    i_hostadapters = LOBYTE( LOWORD( dwSupportInfo ) );
    if( i_hostadapters == 0 )
    {
        FreeLibrary( hASPI );
        return -1;
    }

    fd = malloc( sizeof( struct w32_aspidev ) );
    if( fd == NULL )
    {
        FreeLibrary( hASPI );
        return -1;
    }

    fd->i_blocks = 0;
    fd->hASPI = (long) hASPI;
    fd->lpSendCommand = lpSendCommand;

    c_drive = c_drive > 'Z' ? c_drive - 'a' : c_drive - 'A';

    for( i = 0; i < i_hostadapters; i++ )
    {
        for( j = 0; j < 15; j++ )
        {
            struct SRB_GetDiskInfo srbDiskInfo;

            srbDiskInfo.SRB_Cmd         = SC_GET_DISK_INFO;
            srbDiskInfo.SRB_HaId        = i;
            srbDiskInfo.SRB_Flags       = 0;
            srbDiskInfo.SRB_Hdr_Rsvd    = 0;
            srbDiskInfo.SRB_Target      = j;
            srbDiskInfo.SRB_Lun         = 0;

            lpSendCommand( (void*) &srbDiskInfo );

            if( (srbDiskInfo.SRB_Status == SS_COMP) &&
                (srbDiskInfo.SRB_Int13HDriveInfo == c_drive) )
            {
                fd->i_sid = MAKEWORD( i, j );
                return (int) fd;
            }
        }
    }

    free( (void*) fd );
    FreeLibrary( hASPI );
    _dvdcss_debug( dvdcss, "unable to get haid and target (aspi)" );
    return( -1 );        
}

/*****************************************************************************
 * _win32_dvdcss_aclose: close dvd drive (unload aspi and free w32_aspidev
 *                       structure)
 *****************************************************************************/
static int _win32_dvdcss_aclose( int i_fd )
{
    struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;

    FreeLibrary( (HMODULE) fd->hASPI );
    free( (void*) i_fd );

    return 0;
}

/*****************************************************************************
 * _win32_dvdcss_aseek: aspi version of _dvdcss_seek
 * 
 * returns the number of blocks read.
 *****************************************************************************/
static int _win32_dvdcss_aseek( int i_fd, int i_blocks, int i_method )
{
    int i_old_blocks;
    char sz_buf[ DVDCSS_BLOCK_SIZE ];
    struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;
    
    i_old_blocks = fd->i_blocks;
    fd->i_blocks = i_blocks;

    if( _win32_dvdcss_aread( i_fd, sz_buf, 1 ) == -1 )
    {
        fd->i_blocks = i_old_blocks;
        return -1;
    }

    (fd->i_blocks)--;

    return fd->i_blocks;
}

/*****************************************************************************
 * _win32_dvdcss_aread: aspi version of _dvdcss_read
 *
 * returns the number of blocks read.
 *****************************************************************************/
static int _win32_dvdcss_aread( int i_fd, void *p_data, int i_blocks )
{
    HANDLE hEvent;
    struct SRB_ExecSCSICmd ssc;
    struct w32_aspidev *fd = (struct w32_aspidev *) i_fd;

    /* Create the transfer completion event */
    hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
    if( hEvent == NULL )
    {
        return -1;
    }

    memset( &ssc, 0, sizeof( ssc ) );

    ssc.SRB_Cmd         = SC_EXEC_SCSI_CMD;
    ssc.SRB_Flags       = SRB_DIR_IN | SRB_EVENT_NOTIFY;
    ssc.SRB_HaId        = LOBYTE( fd->i_sid );
    ssc.SRB_Target      = HIBYTE( fd->i_sid );
    ssc.SRB_SenseLen    = SENSE_LEN;
    
    ssc.SRB_PostProc = (LPVOID) hEvent;
    ssc.SRB_BufPointer  = p_data;
    ssc.SRB_CDBLen      = 12;
    
    ssc.CDBByte[0]      = 0xA8; /* RAW */
    ssc.CDBByte[2]      = (UCHAR) (fd->i_blocks >> 24);
    ssc.CDBByte[3]      = (UCHAR) (fd->i_blocks >> 16) & 0xff;
    ssc.CDBByte[4]      = (UCHAR) (fd->i_blocks >> 8) & 0xff;
    ssc.CDBByte[5]      = (UCHAR) (fd->i_blocks) & 0xff;
    
    /* We have to break down the reads into 64kb pieces (ASPI restriction) */
    if( i_blocks > 32 )
    {
        ssc.SRB_BufLen = 32 * DVDCSS_BLOCK_SIZE;
        ssc.CDBByte[9] = 32;
        fd->i_blocks  += 32;

        /* Initiate transfer */  
        ResetEvent( hEvent );
        fd->lpSendCommand( (void*) &ssc );

        /* transfer the next 64kb (_win32_dvdcss_aread is called recursively)
         * We need to check the status of the read on return */
        if( _win32_dvdcss_aread( i_fd, (u8*) p_data + 32 * DVDCSS_BLOCK_SIZE,
                                 i_blocks - 32) < 0 )
        {
            return -1;
        }
    }
    else
    {
        /* This is the last transfer */
        ssc.SRB_BufLen   = i_blocks * DVDCSS_BLOCK_SIZE;
        ssc.CDBByte[9]   = (UCHAR) i_blocks;
        fd->i_blocks += i_blocks;

        /* Initiate transfer */  
        ResetEvent( hEvent );
        fd->lpSendCommand( (void*) &ssc );

    }

    /* If the command has still not been processed, wait until it's finished */
    if( ssc.SRB_Status == SS_PENDING )
    {
        WaitForSingleObject( hEvent, INFINITE );
    }
    CloseHandle( hEvent );

    /* check that the transfer went as planned */
    if( ssc.SRB_Status != SS_COMP )
    {
      return -1;
    }

    return i_blocks;
}

#endif


--- NEW FILE ---
/*****************************************************************************
 * private.h: private DVD reading library data
 *****************************************************************************
 * Copyright (C) 1998-2001 VideoLAN
 * $Id: libdvdcss.h,v 1.1 2002/04/24 19:28:03 arpi Exp $
 *
 * Authors: Stéphane Borel <stef at via.ecp.fr>
 *          Samuel Hocevar <sam at zoy.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Needed headers
 *****************************************************************************/
#include "css.h"

/*****************************************************************************
 * The libdvdcss structure
 *****************************************************************************/
struct dvdcss_s
{
    /* File descriptor */
    int i_fd;
    int i_seekpos;

    /* Decryption stuff */
    css_t        css;
    boolean_t    b_encrypted;
    dvd_title_t *p_titles;

    /* Error management */
    char     *psz_error;
    boolean_t b_errors;
    boolean_t b_debug;

#if defined( WIN32 )
    char *p_readv_buffer;
    int  i_readv_buf_size;
#endif
};

/*****************************************************************************
 * Error management
 *****************************************************************************/
#if defined( _WIN32 ) && defined( _MSC_VER )
#   define DVDCSS_ERROR( x ) fprintf( stderr, "libdvdcss error: %s\n", x );
#   define DVDCSS_DEBUG( x ) fprintf( stderr, "libdvdcss debug: %s\n", x );
#else
#   define DVDCSS_ERROR( x... ) fprintf( stderr, "libdvdcss error: %s\n", ##x );
#   define DVDCSS_DEBUG( x... ) fprintf( stderr, "libdvdcss debug: %s\n", ##x );
#endif

static __inline__ void _dvdcss_error( dvdcss_handle dvdcss, char *psz_string )
{
    if( dvdcss->b_errors )
    {
        DVDCSS_ERROR( psz_string );
    }

    dvdcss->psz_error = psz_string;
}

static __inline__ void _dvdcss_debug( dvdcss_handle dvdcss, char *psz_string )
{
    if( dvdcss->b_debug )
    {
        DVDCSS_DEBUG( psz_string );
    }
}



--- NEW FILE ---
/*
 * Copyright (C) 2000 Håkan Hjort <d95hjort at 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 at brouhaha.com>
 *
 * VOBDUMP is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.  Note that I am not
 * granting permission to redistribute or modify VOBDUMP under the
 * terms of any later version of the General Public License.
 *
 * This program is distributed in the hope that it will be useful (or
 * at least amusing), but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE.  See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdio.h>
#include <inttypes.h>

#include "config.h"

#ifdef HAVE_ASSERT_H
 #include <assert.h>
#endif

#include "nav_types.h"
#include "nav_print.h"

static void print_time(dvd_time_t *dtime) {
  const char *rate;
  assert((dtime->hour>>4) < 0xa && (dtime->hour&0xf) < 0xa);
  assert((dtime->minute>>4) < 0x7 && (dtime->minute&0xf) < 0xa);
  assert((dtime->second>>4) < 0x7 && (dtime->second&0xf) < 0xa);
  assert((dtime->frame_u&0xf) < 0xa);
  
  printf("%02x:%02x:%02x.%02x", 
	 dtime->hour,
	 dtime->minute,
	 dtime->second,
	 dtime->frame_u & 0x3f);
  switch((dtime->frame_u & 0xc0) >> 6) {
  case 1:
    rate = "25.00";
    break;
  case 3:
    rate = "29.97";
    break;
  default:
    rate = "(please send a bug report)";
    break;
  } 
  printf(" @ %s fps", rate);
}


static void navPrint_PCI_GI(pci_gi_t *pci_gi) {
  int i;

  printf("pci_gi:\n");
  printf("nv_pck_lbn    0x%08x\n", pci_gi->nv_pck_lbn);
  printf("vobu_cat      0x%04x\n", pci_gi->vobu_cat);
  printf("vobu_uop_ctl  0x%08x\n", *(uint32_t*)&pci_gi->vobu_uop_ctl);
  printf("vobu_s_ptm    0x%08x\n", pci_gi->vobu_s_ptm);
  printf("vobu_e_ptm    0x%08x\n", pci_gi->vobu_e_ptm);
  printf("vobu_se_e_ptm 0x%08x\n", pci_gi->vobu_se_e_ptm);
  printf("e_eltm        ");
  print_time(&pci_gi->e_eltm);
  printf("\n");
  
  printf("vobu_isrc     \"");
  for(i = 0; i < 32; i++) {
    char c = pci_gi->vobu_isrc[i];
    if((c >= ' ') && (c <= '~'))
      printf("%c", c);
    else
      printf(".");
  }
  printf("\"\n");
}

static void navPrint_NSML_AGLI(nsml_agli_t *nsml_agli) {
  int i, j = 0;
  
  for(i = 0; i < 9; i++)
    j |= nsml_agli->nsml_agl_dsta[i];
  if(j == 0)
    return;
  
  printf("nsml_agli:\n");
  for(i = 0; i < 9; i++)
    if(nsml_agli->nsml_agl_dsta[i])
      printf("nsml_agl_c%d_dsta  0x%08x\n", i + 1, 
	     nsml_agli->nsml_agl_dsta[i]);
}

static void navPrint_HL_GI(hl_gi_t *hl_gi, int *btngr_ns, int *btn_ns) {
  
  if((hl_gi->hli_ss & 0x03) == 0)
    return;
  
  printf("hl_gi:\n");
  printf("hli_ss        0x%01x\n", hl_gi->hli_ss & 0x03);
  printf("hli_s_ptm     0x%08x\n", hl_gi->hli_s_ptm);
  printf("hli_e_ptm     0x%08x\n", hl_gi->hli_e_ptm);
  printf("btn_se_e_ptm  0x%08x\n", hl_gi->btn_se_e_ptm);

  *btngr_ns = hl_gi->btngr_ns;
  printf("btngr_ns      %d\n",  hl_gi->btngr_ns);
  printf("btngr%d_dsp_ty    0x%02x\n", 1, hl_gi->btngr1_dsp_ty);
  printf("btngr%d_dsp_ty    0x%02x\n", 2, hl_gi->btngr2_dsp_ty);
  printf("btngr%d_dsp_ty    0x%02x\n", 3, hl_gi->btngr3_dsp_ty);
  
  printf("btn_ofn       %d\n", hl_gi->btn_ofn);
  *btn_ns = hl_gi->btn_ns;
  printf("btn_ns        %d\n", hl_gi->btn_ns);
  printf("nsl_btn_ns    %d\n", hl_gi->nsl_btn_ns);
  printf("fosl_btnn     %d\n", hl_gi->fosl_btnn);
  printf("foac_btnn     %d\n", hl_gi->foac_btnn);
}

static void navPrint_BTN_COLIT(btn_colit_t *btn_colit) {
  int i, j;
  
  j = 0;
  for(i = 0; i < 6; i++)
    j |= btn_colit->btn_coli[i/2][i&1];
  if(j == 0)
    return;
  
  printf("btn_colit:\n");
  for(i = 0; i < 3; i++)
    for(j = 0; j < 2; j++)
      printf("btn_cqoli %d  %s_coli:  %08x\n",
	     i, (j == 0) ? "sl" : "ac",
	     btn_colit->btn_coli[i][j]);
}

static void navPrint_BTNIT(btni_t *btni_table, int btngr_ns, int btn_ns) {
  int i, j;
  
  printf("btnit:\n");
  printf("btngr_ns: %i\n", btngr_ns);
  printf("btn_ns: %i\n", btn_ns);
  
  if(btngr_ns == 0)
    return;
  
  for(i = 0; i < btngr_ns; i++) {
    for(j = 0; j < (36 / btngr_ns); j++) {
      if(j < btn_ns) {
	btni_t *btni = &btni_table[(36 / btngr_ns) * i + j];
	
	printf("group %d btni %d:  ", i+1, j+1);
	printf("btn_coln %d, auto_action_mode %d\n",
	       btni->btn_coln, btni->auto_action_mode);
	printf("coords   (%d, %d) .. (%d, %d)\n",
	       btni->x_start, btni->y_start, btni->x_end, btni->y_end);
	
	printf("up %d, ", btni->up);
	printf("down %d, ", btni->down);
	printf("left %d, ", btni->left);
	printf("right %d\n", btni->right);
	
	// ifoPrint_COMMAND(&btni->cmd);
	printf("\n");
      }
    }
  }
}

static void navPrint_HLI(hli_t *hli) {
  int btngr_ns = 0, btn_ns = 0;
  
  printf("hli:\n");
  navPrint_HL_GI(&hli->hl_gi, & btngr_ns, & btn_ns);
  navPrint_BTN_COLIT(&hli->btn_colit);
  navPrint_BTNIT(hli->btnit, btngr_ns, btn_ns);
}

void navPrint_PCI(pci_t *pci) {
  printf("pci packet:\n");
  navPrint_PCI_GI(&pci->pci_gi);
  navPrint_NSML_AGLI(&pci->nsml_agli);
  navPrint_HLI(&pci->hli);
}

static void navPrint_DSI_GI(dsi_gi_t *dsi_gi) {
  printf("dsi_gi:\n");
  printf("nv_pck_scr     0x%08x\n", dsi_gi->nv_pck_scr);
  printf("nv_pck_lbn     0x%08x\n", dsi_gi->nv_pck_lbn );
  printf("vobu_ea        0x%08x\n", dsi_gi->vobu_ea);
  printf("vobu_1stref_ea 0x%08x\n", dsi_gi->vobu_1stref_ea);
  printf("vobu_2ndref_ea 0x%08x\n", dsi_gi->vobu_2ndref_ea);
  printf("vobu_3rdref_ea 0x%08x\n", dsi_gi->vobu_3rdref_ea);
  printf("vobu_vob_idn   0x%04x\n", dsi_gi->vobu_vob_idn);
  printf("vobu_c_idn     0x%02x\n", dsi_gi->vobu_c_idn);
  printf("c_eltm         ");
  print_time(&dsi_gi->c_eltm);
  printf("\n");
}

static void navPrint_SML_PBI(sml_pbi_t *sml_pbi) {
  printf("sml_pbi:\n");
  printf("category 0x%04x\n", sml_pbi->category);
  if(sml_pbi->category & 0x8000)
    printf("VOBU is in preunit\n");
  if(sml_pbi->category & 0x4000)
    printf("VOBU is in ILVU\n");
  if(sml_pbi->category & 0x2000)
    printf("VOBU at the beginning of ILVU\n");
  if(sml_pbi->category & 0x1000)
    printf("VOBU at end of PREU of ILVU\n");
  
  printf("ilvu_ea       0x%08x\n", sml_pbi->ilvu_ea);
  printf("nxt_ilvu_sa   0x%08x\n", sml_pbi->ilvu_sa);
  printf("nxt_ilvu_size 0x%04x\n", sml_pbi->size);
  
  printf("vob_v_s_s_ptm 0x%08x\n", sml_pbi->vob_v_s_s_ptm);
  printf("vob_v_e_e_ptm 0x%08x\n", sml_pbi->vob_v_e_e_ptm);
  
  /* $$$ more code needed here */
}

static void navPrint_SML_AGLI(sml_agli_t *sml_agli) {
  int i;
  printf("sml_agli:\n");
  for(i = 0; i < 9; i++) {
    printf("agl_c%d address: 0x%08x size 0x%04x\n", i,
	   sml_agli->data[i].address, sml_agli->data[i].size);
  }
}

static void navPrint_VOBU_SRI(vobu_sri_t *vobu_sri) {
  int i;
  int stime[19] = { 240, 120, 60, 20, 15, 14, 13, 12, 11, 
		     10,   9,  8,  7,  6,  5,  4,  3,  2, 1};
  printf("vobu_sri:\n");
  printf("Next VOBU with Video %08x\n", vobu_sri->next_video);
  for(i = 0; i < 19; i++) {
    printf("%3.1f %08x ", stime[i]/2.0, vobu_sri->fwda[i]);
  }
  printf("\n");
  printf("Next VOBU %08x\n", vobu_sri->next_vobu);
  printf("--\n");
  printf("Prev VOBU %08x\n", vobu_sri->prev_vobu);
  for(i = 0; i < 19; i++) {
    printf("%3.1f %08x ", stime[18 - i]/2.0, vobu_sri->bwda[i]);
  }
  printf("\n");
  printf("Prev VOBU with Video %08x\n", vobu_sri->prev_video);
}

static void navPrint_SYNCI(synci_t *synci) {
  int i;
  
  printf("synci:\n");
  /* $$$ more code needed here */
  for(i = 0; i < 8; i++)
    printf("%04x ", synci->a_synca[i]);
  for(i = 0; i < 32; i++)
    printf("%08x ", synci->sp_synca[i]);
}

void navPrint_DSI(dsi_t *dsi) {
  printf("dsi packet:\n");
  navPrint_DSI_GI(&dsi->dsi_gi);
  navPrint_SML_PBI(&dsi->sml_pbi);
  navPrint_SML_AGLI(&dsi->sml_agli);
  navPrint_VOBU_SRI(&dsi->vobu_sri);
  navPrint_SYNCI(&dsi->synci);
}



--- NEW FILE ---
/**
 * Copyright (C) 2001 Billy Biggs <vektor at dumbterm.net>,
 *                    Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifndef NAV_PRINT_H_INCLUDED
#define NAV_PRINT_H_INCLUDED

#include <stdio.h>
#include <dvdread/nav_types.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * This file provides example functions for printing information about the NAV
 * packet to stdout.
 */

void navPrint_PCI(pci_t *pci);
void navPrint_DSI(dsi_t *dsi);

#ifdef __cplusplus
};
#endif
#endif /* NAV_PRINT_H_INCLUDED */

--- NEW FILE ---
/**
 * Copyright (C) 2000 Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdio.h>
#include <string.h>
#include <inttypes.h>

#include "config.h"

#ifdef HAVE_ASSERT_H
 #include <assert.h>
#endif

#include "bswap.h"
#include "nav_types.h"
#include "nav_read.h"

void navRead_PCI(pci_t *pci, unsigned char *buffer) {
  int i, j;

  assert(sizeof(pci_t) == PCI_BYTES - 1); // -1 for substream id
  
  memcpy(pci, buffer, sizeof(pci_t));

  /* Endian conversions  */

  /* pci pci_gi */
  B2N_32(pci->pci_gi.nv_pck_lbn);
  B2N_16(pci->pci_gi.vobu_cat);
  B2N_32(pci->pci_gi.vobu_s_ptm);
  B2N_32(pci->pci_gi.vobu_e_ptm);
  B2N_32(pci->pci_gi.vobu_se_e_ptm);

  /* pci nsml_agli */
  for(i = 0; i < 9; i++)
    B2N_32(pci->nsml_agli.nsml_agl_dsta[i]);

  /* pci hli hli_gi */
  B2N_16(pci->hli.hl_gi.hli_ss);
  B2N_32(pci->hli.hl_gi.hli_s_ptm);
  B2N_32(pci->hli.hl_gi.hli_e_ptm);
  B2N_32(pci->hli.hl_gi.btn_se_e_ptm);

  /* pci hli btn_colit */
  for(i = 0; i < 3; i++)
    for(j = 0; j < 2; j++)
      B2N_32(pci->hli.btn_colit.btn_coli[i][j]);


  /* pci hli btni */
  /* There are some issues with this bitfiled with some compilers 
     because they stradle word boundaries. */
  
#if !defined(WORDS_BIGENDIAN)
  for(i = 0; i < 36; i++) {
#if 0 /* Wierd Sun CC code that does not work */
    unsigned char m[6];
    memcpy(m, &pci->hli.btnit[i], 6);
    pci->hli.btnit[i].zero1   = (m[1] >> 2);
    pci->hli.btnit[i].x_start = (m[0] << 4) | (m[1] >> 4);
    pci->hli.btnit[i].x_end   = (m[1] << 8) | m[2];
    pci->hli.btnit[i].y_start = (m[3] << 4) | (m[4] >> 4);
    pci->hli.btnit[i].y_end   = (m[4] << 8) | m[5];
    pci->hli.btnit[i].zero2   = (m[4] >> 2);
    pci->hli.btnit[i].btn_coln = (m[0] >> 6);
    pci->hli.btnit[i].auto_action_mode = (m[3] >> 6);
#else
    char tmp[6], swap;
    memcpy(tmp, &(pci->hli.btnit[i]), 6);
    /* This is a B2N_24() */
    swap = tmp[0]; tmp[0] = tmp[2]; tmp[2] = swap;
    /* This is a B2N_24() */
    swap = tmp[3]; tmp[3] = tmp[5]; tmp[5] = swap;
    memcpy(&(pci->hli.btnit[i]), tmp, 6);
#endif
  }
#endif


  /* Asserts */

  /* pci pci gi */ 
  assert(pci->pci_gi.zero1 == 0);

  /* pci hli hli_gi */
  assert(pci->hli.hl_gi.zero1 == 0);
  assert(pci->hli.hl_gi.zero2 == 0);
  assert(pci->hli.hl_gi.zero3 == 0);
  assert(pci->hli.hl_gi.zero4 == 0);
  assert(pci->hli.hl_gi.zero5 == 0);

  /* Are there buttons defined here? */
  if((pci->hli.hl_gi.hli_ss & 0x03) != 0) {
    assert(pci->hli.hl_gi.btn_ns != 0); 
    assert(pci->hli.hl_gi.btngr_ns != 0); 
  } else {
    assert((pci->hli.hl_gi.btn_ns != 0 && pci->hli.hl_gi.btngr_ns != 0) 
	   || (pci->hli.hl_gi.btn_ns == 0 && pci->hli.hl_gi.btngr_ns == 0));
  }

  /* pci hli btnit */
  
#if NDEBUG
  for(i = 0; i < pci->hli.hl_gi.btngr_ns; i++) {
    for(j = 0; j < (36 / pci->hli.hl_gi.btngr_ns); j++) {
      int n = (36 / pci->hli.hl_gi.btngr_ns) * i + j;
      assert(pci->hli.btnit[n].zero1 == 0);
      assert(pci->hli.btnit[n].zero2 == 0);
      assert(pci->hli.btnit[n].zero3 == 0);
      assert(pci->hli.btnit[n].zero4 == 0);
      assert(pci->hli.btnit[n].zero5 == 0);
      assert(pci->hli.btnit[n].zero6 == 0);
      
      if (j < pci->hli.hl_gi.btn_ns) {	
	assert(pci->hli.btnit[n].x_start <= pci->hli.btnit[n].x_end);
	assert(pci->hli.btnit[n].y_start <= pci->hli.btnit[n].y_end);
	assert(pci->hli.btnit[n].up <= pci->hli.hl_gi.btn_ns);
	assert(pci->hli.btnit[n].down <= pci->hli.hl_gi.btn_ns);
	assert(pci->hli.btnit[n].left <= pci->hli.hl_gi.btn_ns);
	assert(pci->hli.btnit[n].right <= pci->hli.hl_gi.btn_ns);
	//vmcmd_verify(pci->hli.btnit[n].cmd);
      } else {
	int k;
	assert(pci->hli.btnit[n].btn_coln == 0);
	assert(pci->hli.btnit[n].auto_action_mode == 0);
	assert(pci->hli.btnit[n].x_start == 0);
	assert(pci->hli.btnit[n].y_start == 0);
	assert(pci->hli.btnit[n].x_end == 0);
	assert(pci->hli.btnit[n].y_end == 0);
	assert(pci->hli.btnit[n].up == 0);
	assert(pci->hli.btnit[n].down == 0);
	assert(pci->hli.btnit[n].left == 0);
	assert(pci->hli.btnit[n].right == 0);
	for (k = 0; k < 8; k++)
	  assert(pci->hli.btnit[n].cmd.bytes[k] == 0); //CHECK_ZERO?
      }
    }
  }
#endif
}

void navRead_DSI(dsi_t *dsi, unsigned char *buffer) {
  int i;

  assert(sizeof(dsi_t) == DSI_BYTES - 1); // -1 for substream id
  
  memcpy(dsi, buffer, sizeof(dsi_t));

  /* Endian conversions */

  /* dsi dsi gi */
  B2N_32(dsi->dsi_gi.nv_pck_scr);
  B2N_32(dsi->dsi_gi.nv_pck_lbn);
  B2N_32(dsi->dsi_gi.vobu_ea);
  B2N_32(dsi->dsi_gi.vobu_1stref_ea);
  B2N_32(dsi->dsi_gi.vobu_2ndref_ea);
  B2N_32(dsi->dsi_gi.vobu_3rdref_ea);
  B2N_16(dsi->dsi_gi.vobu_vob_idn);

  /* dsi sml pbi */
  B2N_16(dsi->sml_pbi.category);
  B2N_32(dsi->sml_pbi.ilvu_ea);
  B2N_32(dsi->sml_pbi.ilvu_sa);
  B2N_16(dsi->sml_pbi.size);
  B2N_32(dsi->sml_pbi.vob_v_s_s_ptm);
  B2N_32(dsi->sml_pbi.vob_v_e_e_ptm);

  /* dsi sml agli */
  for(i = 0; i < 9; i++) {
    B2N_32(dsi->sml_agli.data[ i ].address);
    B2N_16(dsi->sml_agli.data[ i ].size);
  }

  /* dsi vobu sri */
  B2N_32(dsi->vobu_sri.next_video);
  for(i = 0; i < 19; i++)
    B2N_32(dsi->vobu_sri.fwda[i]);
  B2N_32(dsi->vobu_sri.next_vobu);
  B2N_32(dsi->vobu_sri.prev_vobu);
  for(i = 0; i < 19; i++)
    B2N_32(dsi->vobu_sri.bwda[i]);
  B2N_32(dsi->vobu_sri.prev_video);

  /* dsi synci */
  for(i = 0; i < 8; i++)
    B2N_16(dsi->synci.a_synca[i]);
  for(i = 0; i < 32; i++)
    B2N_32(dsi->synci.sp_synca[i]);

  
  /* Asserts */

  /* dsi dsi gi */
  assert(dsi->dsi_gi.zero1 == 0);
}


--- NEW FILE ---
/**
 * Copyright (C) 2000, 2001 Håkan Hjort <d95hjort at dtek.chalmers.se>.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef NAV_READ_H_INCLUDED
#define NAV_READ_H_INCLUDED

#include <dvdread/nav_types.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * Reads the PCI packet which begins at buffer into pci.
 */
void navRead_PCI(pci_t *pci, unsigned char *buffer);

/**
 * Reads the DSI packet which begins at buffer into dsi.
 */
void navRead_DSI(dsi_t *dsi, unsigned char *buffer);

#ifdef __cplusplus
};
#endif
#endif /* NAV_READ_H_INCLUDED */

--- NEW FILE ---
/**
 * Copyright (C) 2000 Håkan Hjort <d95hjort at dtek.chalmers.se>
 *
 * The data structures in this file should represent the layout of the
 * pci and dsi packets as they are stored in the stream.  Information
 * found by reading the source to VOBDUMP is the base for the structure
 * and names of these data types.
 *
 * VOBDUMP: a program for examining DVD .VOB files.
 * Copyright 1998, 1999 Eric Smith <eric at brouhaha.com>
 *
 * VOBDUMP is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.  Note that I am not
 * granting permission to redistribute or modify VOBDUMP under the terms
 * of any later version of the General Public License.
 *
 * This program is distributed in the hope that it will be useful (or at
 * least amusing), but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 * the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 */

#ifndef NAV_TYPES_H_INCLUDED
#define NAV_TYPES_H_INCLUDED

#include <inttypes.h>
#include <dvdread/ifo_types.h> // only dvd_time_t, vm_cmd_t and user_ops_t


#undef ATTRIBUTE_PACKED
#undef PRAGMA_PACK_BEGIN 
#undef PRAGMA_PACK_END

#if defined(__GNUC__)
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
#define ATTRIBUTE_PACKED __attribute__ ((packed))
#define PRAGMA_PACK 0
#endif
#endif

#if !defined(ATTRIBUTE_PACKED)
#define ATTRIBUTE_PACKED
#define PRAGMA_PACK 1
#endif


/* The length including the substream id byte. */
#define PCI_BYTES 0x3d4
#define DSI_BYTES 0x3fa

#define PS2_PCI_SUBSTREAM_ID 0x00
#define PS2_DSI_SUBSTREAM_ID 0x01

/* Remove this */
#define DSI_START_BYTE 1031


#if PRAGMA_PACK
#pragma pack(1)
#endif


/**
 * PCI General Information 
 */
typedef struct {
  uint32_t nv_pck_lbn;
  uint16_t vobu_cat;
  uint16_t zero1;
  user_ops_t vobu_uop_ctl;
  uint32_t vobu_s_ptm;
  uint32_t vobu_e_ptm;
  uint32_t vobu_se_e_ptm;
  dvd_time_t e_eltm;
  char vobu_isrc[32];
} ATTRIBUTE_PACKED pci_gi_t;

/**
 * Non Seamless Angle Information
 */
typedef struct {
  uint32_t nsml_agl_dsta[9]; 
} ATTRIBUTE_PACKED nsml_agli_t;

/** 
 * Highlight General Information 
 */
typedef struct {
  uint16_t hli_ss; // only low 2 bits
  uint32_t hli_s_ptm;
  uint32_t hli_e_ptm;
  uint32_t btn_se_e_ptm;
#ifdef WORDS_BIGENDIAN
  unsigned int zero1 : 2;
  unsigned int btngr_ns : 2;
  unsigned int zero2 : 1;
  unsigned int btngr1_dsp_ty : 3;
  unsigned int zero3 : 1;
  unsigned int btngr2_dsp_ty : 3;
  unsigned int zero4 : 1;
  unsigned int btngr3_dsp_ty : 3;
#else
  unsigned int btngr1_dsp_ty : 3;
  unsigned int zero2 : 1;
  unsigned int btngr_ns : 2;
  unsigned int zero1 : 2;
  unsigned int btngr3_dsp_ty : 3;
  unsigned int zero4 : 1;
  unsigned int btngr2_dsp_ty : 3;
  unsigned int zero3 : 1;
#endif
  uint8_t btn_ofn;
  uint8_t btn_ns;     // only low 6 bits
  uint8_t nsl_btn_ns; // only low 6 bits
  uint8_t zero5;
  uint8_t fosl_btnn;  // only low 6 bits
  uint8_t foac_btnn;  // only low 6 bits
} ATTRIBUTE_PACKED hl_gi_t;


/** 
 * Button Color Information Table 
 */
typedef struct {
  uint32_t btn_coli[3][2];
} ATTRIBUTE_PACKED btn_colit_t;


/*
  btn_coln         11000000 00000000 00000000 00000000 00000000 00000000
  x_start          00111111 11110000 00000000 00000000 00000000 00000000
  zero1            00000000 00001100 00000000 00000000 00000000 00000000
  x_end            00000000 00000011 11111111 00000000 00000000 00000000
  auto_action_mode 00000000 00000000 00000000 11000000 00000000 00000000
  y_start          00000000 00000000 00000000 00111111 11110000 00000000
  zero2            00000000 00000000 00000000 00000000 00001100 00000000
  y_end            00000000 00000000 00000000 00000000 00000011 11111111

  unsigned int btn_coln         : 2;  //  0 - m[0]>>6
  unsigned int x_start          : 10; //  2 - m[0]<<4 | m[1]>>4
  unsigned int zero1            : 2;  // 12 - m[1]>>2
  unsigned int x_end            : 10; // 14 - m[1]<<8 | m[2]
  
  unsigned int auto_action_mode : 2;  // 24 - m[3]>>6
  unsigned int y_start          : 10; // 26 - m[3]<<4 | m[4]>>4
  unsigned int zero2            : 2;  // 36 - m[4]>>2
  unsigned int y_end            : 10; // 38 - m[4]<<8 | m[5]
 */

/** 
 * Button Information
 */
typedef struct {
#if 0 /* Wierd Sun CC code that does not work */
  unsigned int zero1            : 2;
  unsigned int x_start          : 10;
  unsigned int x_end            : 10;
  unsigned int y_start          : 10;
  
  unsigned int zero2            : 2;  
  unsigned int btn_coln         : 2;
  unsigned int auto_action_mode : 2;
  unsigned int y_end            : 10;
#endif
#ifdef WORDS_BIGENDIAN
  unsigned int btn_coln         : 2;
  unsigned int x_start          : 10;
  unsigned int zero1            : 2;
  unsigned int x_end            : 10;
  unsigned int auto_action_mode : 2;
  unsigned int y_start          : 10;
  unsigned int zero2            : 2;
  unsigned int y_end            : 10;

  unsigned int zero3            : 2;
  unsigned int up               : 6;
  unsigned int zero4            : 2;
  unsigned int down             : 6;
  unsigned int zero5            : 2;
  unsigned int left             : 6;
  unsigned int zero6            : 2;
  unsigned int right            : 6;
#else
  unsigned int x_end            : 10;
  unsigned int zero1            : 2;
  unsigned int x_start          : 10;
  unsigned int btn_coln         : 2;
  unsigned int y_end            : 10;
  unsigned int zero2            : 2;
  unsigned int y_start          : 10;
  unsigned int auto_action_mode : 2;

  unsigned int up               : 6;
  unsigned int zero3            : 2;
  unsigned int down             : 6;
  unsigned int zero4            : 2;
  unsigned int left             : 6;
  unsigned int zero5            : 2;
  unsigned int right            : 6;
  unsigned int zero6            : 2;
#endif
  vm_cmd_t cmd;
} ATTRIBUTE_PACKED btni_t;

/**
 * Highlight Information 
 */
typedef struct {
  hl_gi_t     hl_gi;
  btn_colit_t btn_colit;
  btni_t      btnit[36];
} ATTRIBUTE_PACKED hli_t;

/**
 * PCI packet
 */
typedef struct {
  pci_gi_t    pci_gi;
  nsml_agli_t nsml_agli;
  hli_t       hli;
  uint8_t     zero1[189];
} ATTRIBUTE_PACKED pci_t;




/**
 * DSI General Information 
 */
typedef struct {
  uint32_t nv_pck_scr;
  uint32_t nv_pck_lbn;
  uint32_t vobu_ea;
  uint32_t vobu_1stref_ea;
  uint32_t vobu_2ndref_ea;
  uint32_t vobu_3rdref_ea;
  uint16_t vobu_vob_idn;
  uint8_t  zero1;
  uint8_t  vobu_c_idn;
  dvd_time_t c_eltm;
} ATTRIBUTE_PACKED dsi_gi_t;

/**
 * Seamless Playback Information
 */
typedef struct {
  uint16_t category; // category of seamless VOBU
  uint32_t ilvu_ea;  // end address of interleaved Unit (sectors)
  uint32_t ilvu_sa;  // start address of next interleaved unit (sectors)
  uint16_t size;     // size of next interleaved unit (sectors)
  uint32_t vob_v_s_s_ptm; /* video start ptm in vob */
  uint32_t vob_v_e_e_ptm; /* video end ptm in vob */
  struct {
    uint32_t stp_ptm1;
    uint32_t stp_ptm2;
    uint32_t gap_len1;
    uint32_t gap_len2;      
  } vob_a[8];
} ATTRIBUTE_PACKED sml_pbi_t;

/**
 * Seamless Angle Infromation for one angle
 */
typedef struct {
    uint32_t address; // Sector offset to next ILVU, high bit is before/after
    uint16_t size;    // Byte size of the ILVU poited to by address.
} ATTRIBUTE_PACKED sml_agl_data_t;

/**
 * Seamless Angle Infromation
 */
typedef struct {
  sml_agl_data_t data[9];
} ATTRIBUTE_PACKED sml_agli_t;

/**
 * VOBU Search Information 
 */
typedef struct {
  uint32_t next_video; // Next vobu that contains video
  uint32_t fwda[19];   // Forwards, time
  uint32_t next_vobu;
  uint32_t prev_vobu;
  uint32_t bwda[19];   // Backwards, time
  uint32_t prev_video;
} ATTRIBUTE_PACKED vobu_sri_t;

#define SRI_END_OF_CELL 0x3fffffff

/**
 * Synchronous Information
 */ 
typedef struct {
  uint16_t a_synca[8];   // Sector offset to first audio packet for this VOBU
  uint32_t sp_synca[32]; // Sector offset to first subpicture packet
} ATTRIBUTE_PACKED synci_t;

/**
 * DSI packet
 */
typedef struct {
  dsi_gi_t   dsi_gi;
  sml_pbi_t  sml_pbi;
  sml_agli_t sml_agli;
  vobu_sri_t vobu_sri;
  synci_t    synci;
  uint8_t    zero1[471];
} ATTRIBUTE_PACKED dsi_t;


#if PRAGMA_PACK
#pragma pack()
#endif

#endif /* NAV_TYPES_H_INCLUDED */




More information about the MPlayer-cvslog mailing list