summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRich J Wareham <richwareham@users.sourceforge.net>2002-08-08 17:49:20 +0000
committerRich J Wareham <richwareham@users.sourceforge.net>2002-08-08 17:49:20 +0000
commit892715d418f93897aa941bf8aaee69e74628099f (patch)
treeb0954b9c584b9a79a83fcb186d40bb947e5af430
parent2f9f3c0ec0e38ca2c6cbfea9d6a4f8fe5fde78b9 (diff)
downloadxine-lib-892715d418f93897aa941bf8aaee69e74628099f.tar.gz
xine-lib-892715d418f93897aa941bf8aaee69e74628099f.tar.bz2
First stage of DVD plugin -> dvdnav conversion
CVS patchset: 2404 CVS date: 2002/08/08 17:49:20
-rw-r--r--configure.in2
-rw-r--r--src/input/Makefile.am15
-rw-r--r--src/input/dvd_udf.c707
-rw-r--r--src/input/input_dvd.c1841
-rw-r--r--src/input/libdvdnav/Makefile.am30
-rw-r--r--src/input/libdvdnav/decoder.c768
-rw-r--r--src/input/libdvdnav/decoder.h107
-rw-r--r--src/input/libdvdnav/dvd_types.h300
-rw-r--r--src/input/libdvdnav/dvdnav.c1136
-rw-r--r--src/input/libdvdnav/dvdnav.h879
-rw-r--r--src/input/libdvdnav/dvdnav_events.h124
-rw-r--r--src/input/libdvdnav/dvdnav_internal.h168
-rw-r--r--src/input/libdvdnav/highlight.c344
-rw-r--r--src/input/libdvdnav/navigation.c164
-rw-r--r--src/input/libdvdnav/read_cache.c543
-rw-r--r--src/input/libdvdnav/read_cache.h51
-rw-r--r--src/input/libdvdnav/searching.c443
-rw-r--r--src/input/libdvdnav/settings.c120
-rw-r--r--src/input/libdvdnav/vm.c1905
-rw-r--r--src/input/libdvdnav/vm.h154
-rw-r--r--src/input/libdvdnav/vmcmd.c488
-rw-r--r--src/input/libdvdnav/vmcmd.h35
-rw-r--r--src/input/libdvdread/Makefile.am29
-rw-r--r--src/input/libdvdread/bswap.h87
-rw-r--r--src/input/libdvdread/dvd_input.c318
-rw-r--r--src/input/libdvdread/dvd_input.h51
-rw-r--r--src/input/libdvdread/dvd_reader.c898
-rw-r--r--src/input/libdvdread/dvd_reader.h151
-rw-r--r--src/input/libdvdread/dvd_udf.c494
-rw-r--r--src/input/libdvdread/dvd_udf.h53
-rw-r--r--src/input/libdvdread/ifo_print.c1049
-rw-r--r--src/input/libdvdread/ifo_print.h58
-rw-r--r--src/input/libdvdread/ifo_read.c1798
-rw-r--r--src/input/libdvdread/ifo_read.h217
-rw-r--r--src/input/libdvdread/ifo_types.h747
-rw-r--r--src/input/libdvdread/nav_print.c279
-rw-r--r--src/input/libdvdread/nav_print.h42
-rw-r--r--src/input/libdvdread/nav_read.c186
-rw-r--r--src/input/libdvdread/nav_read.h41
-rw-r--r--src/input/libdvdread/nav_types.h288
-rw-r--r--src/input/read_cache.c298
41 files changed, 15744 insertions, 1664 deletions
diff --git a/configure.in b/configure.in
index 6a5798e7f..cd1e6e4e7 100644
--- a/configure.in
+++ b/configure.in
@@ -1045,6 +1045,8 @@ src/libxinevdec/Makefile
src/libxineadec/Makefile
src/xine-utils/Makefile
src/input/Makefile
+src/input/libdvdread/Makefile
+src/input/libdvdnav/Makefile
src/xine-engine/Makefile
misc/Makefile
misc/xine-config
diff --git a/src/input/Makefile.am b/src/input/Makefile.am
index 1ba11e519..5f40efecd 100644
--- a/src/input/Makefile.am
+++ b/src/input/Makefile.am
@@ -2,6 +2,8 @@
## Process this file with automake to produce Makefile.in
##
+SUBDIRS = libdvdread libdvdnav
+
LIBTOOL = $(SHELL) $(top_builddir)/libtool-nofpic
libdir = $(XINE_PLUGINDIR)
@@ -18,6 +20,14 @@ in_vcd = xineplug_inp_vcd.la
in_cda = xineplug_inp_cda.la
endif
+# For DVD
+DVD_CFLAGS = -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE \
+ -Ilibdvdnav/ \
+ -Ilibdvdread/
+
+CFLAGS = @CFLAGS@ $(DVD_CFLAGS)
+DEBUG_CFLAGS = @DEBUG_CFLAGS@ $(DVD_CFLAGS)
+
lib_LTLIBRARIES = xineplug_inp_file.la $(in_dvd) $(in_vcd) $(in_cda) \
xineplug_inp_stdin_fifo.la xineplug_inp_net.la \
xineplug_inp_rtp.la xineplug_inp_http.la xineplug_inp_mms.la
@@ -26,8 +36,9 @@ xineplug_inp_file_la_SOURCES = input_file.c
xineplug_inp_file_la_LIBADD = $(top_builddir)/src/xine-engine/libxine.la
xineplug_inp_file_la_LDFLAGS = -avoid-version -module
-xineplug_inp_dvd_la_SOURCES = input_dvd.c dvd_udf.c read_cache.c
-xineplug_inp_dvd_la_LIBADD = $(top_builddir)/src/xine-engine/libxine.la
+xineplug_inp_dvd_la_SOURCES = input_dvd.c
+xineplug_inp_dvd_la_LIBADD = $(top_builddir)/src/xine-engine/libxine.la \
+ libdvdnav/libdvdnav.la libdvdread/libdvdread.la
xineplug_inp_dvd_la_LDFLAGS = -avoid-version -module
xineplug_inp_net_la_SOURCES = input_net.c net_buf_ctrl.c
diff --git a/src/input/dvd_udf.c b/src/input/dvd_udf.c
deleted file mode 100644
index e86012ff7..000000000
--- a/src/input/dvd_udf.c
+++ /dev/null
@@ -1,707 +0,0 @@
-/*
- * dvdudf: parse and read the UDF volume information of a DVD Video
- * Copyright (C) 1999 Christian Wolff for convergence integrated media GmbH
- * minor modifications by Thomas Mirlacher
- * dir support and bugfixes by Guenter Bartsch for use in xine
- *
- * 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
- *
- * The author can be reached at scarabaeus@convergence.de,
- * the project's page is at http://linuxtv.org/dvd/
- */
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <fcntl.h>
-
-#include "dvd_udf.h"
-#include "compat.h"
-
-static int _Unicodedecode (uint8_t *data, int len, char *target);
-
-#define MAX_FILE_LEN 2048
-
-struct Partition {
- int valid;
- uint8_t VolumeDesc[128];
- uint16_t Flags;
- uint16_t Number;
- uint8_t Contents[32];
- uint32_t AccessType;
- uint32_t Start;
- uint32_t Length;
-} partition;
-
-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 GETN4(p) ((uint32_t)data[p]|((uint32_t)data[(p)+1]<<8)|((uint32_t)data[(p)+2]<<16)|((uint32_t)data[(p)+3]<<24))
-#define GETN(p,n,target) memcpy(target,&data[p],n)
-
-
-static const char MALLOC_FAILED[] = "dvd_udf: %s: failed to malloc %d bytes\n";
-
-
-/*
- * reads absolute Logical Block of the disc
- * returns number of read bytes on success, 0 on error
- */
-
-int UDFReadLB (int fd, off_t lb_number, size_t block_count, uint8_t *data)
-{
- if (fd < 0)
- return 0;
-
- if (lseek (fd, lb_number * (off_t) DVD_VIDEO_LB_LEN, SEEK_SET) < 0)
- return 0; /* position not found */
-
- return read (fd, data, block_count*DVD_VIDEO_LB_LEN);
-}
-
-
-static int _Unicodedecode (uint8_t *data, int len, char *target)
-{
- int p=1,i=0;
-
- if (!(data[0] & 0x18)) {
- target[0] ='\0';
- return 0;
- }
-
- if (data[0] & 0x10) { /* ignore MSB of unicode16 */
- p++;
-
- while (p<len)
- target[i++]=data[p+=2];
- } else {
- while (p<len)
- target[i++]=data[p++];
- }
-
- target[i]='\0';
-
- return 0;
-}
-
-
-int UDFEntity (uint8_t *data, uint8_t *Flags, char *Identifier)
-{
- Flags[0] = data[0];
- strncpy (Identifier, &data[1], 5);
-
- return 0;
-}
-
-
-int UDFDescriptor (uint8_t *data, uint16_t *TagID)
-{
- TagID[0] = GETN2(0);
- /* TODO: check CRC n stuff */
-
- return 0;
-}
-
-
-int UDFExtentAD (uint8_t *data, uint32_t *Length, uint32_t *Location)
-{
- Length[0] =GETN4(0);
- Location[0]=GETN4(4);
-
- return 0;
-}
-
-#define UDFADshort 1
-#define UDFADlong 2
-#define UDFADext 4
-
-int UDFAD (uint8_t *data, struct AD *ad, uint8_t type)
-{
- ad->Length = GETN4(0);
- ad->Flags = ad->Length>>30;
- ad->Length &= 0x3FFFFFFF;
-
- switch (type) {
- case UDFADshort:
- ad->Location = GETN4(4);
- ad->Partition = partition.Number; /* use number of current partition */
- break;
- case UDFADlong:
- ad->Location = GETN4(4);
- ad->Partition = GETN2(8);
- break;
- case UDFADext:
- ad->Location = GETN4(12);
- ad->Partition = GETN2(16);
- break;
- }
-
- return 0;
-}
-
-int UDFICB (uint8_t *data, uint8_t *FileType, uint16_t *Flags)
-{
- FileType[0]=GETN1(11);
- Flags[0]=GETN2(18);
-
- return 0;
-}
-
-int UDFPartition (uint8_t *data, uint16_t *Flags, uint16_t *Number, char *Contents,
- uint32_t *Start, uint32_t *Length)
-{
- Flags[0] = GETN2(20);
- Number[0] = GETN2(22);
- GETN(24,32,Contents);
- Start[0] = GETN4(188);
- Length[0] = GETN4(192);
-
- return 0;
-}
-
-
-/*
- * reads the volume descriptor and checks the parameters
- * returns 0 on OK, 1 on error
- */
-
-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;
-}
-
-
-int UDFFileEntry (uint8_t *data, uint8_t *FileType, struct AD *ad)
-{
- uint8_t filetype;
- uint16_t flags;
- uint32_t L_EA,L_AD;
- int p;
-
- UDFICB(&data[16],&filetype,&flags);
- FileType[0]=filetype;
- L_EA=GETN4(168);
- L_AD=GETN4(172);
- p=176+L_EA;
-
- while (p<176+L_EA+L_AD) {
- switch (flags&0x07) {
- case 0:
- UDFAD (&data[p], ad, UDFADshort);
- p += 0x08;
- break;
- case 1:
- UDFAD (&data[p], ad, UDFADlong);
- p += 0x10;
- break;
- case 2: UDFAD (&data[p], ad, UDFADext);
- p += 0x14;
- break;
- case 3:
- switch (L_AD) {
- case 0x08:
- UDFAD (&data[p], ad, UDFADshort);
- break;
- case 0x10:
- UDFAD (&data[p], ad, UDFADlong);
- break;
- case 0x14:
- UDFAD (&data[p], ad, UDFADext);
- break;
- }
- default:
- p += L_AD;
- break;
- }
- }
-
- return 0;
-}
-
-
-int UDFFileIdentifier (uint8_t *data, uint8_t *FileCharacteristics, char *FileName, struct AD *FileICB)
-{
- uint8_t L_FI;
- uint16_t L_IU;
-
- FileCharacteristics[0]=GETN1(18);
- L_FI=GETN1(19);
- UDFAD(&data[20],FileICB,UDFADlong);
- 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;
- */
-
-int UDFMapICB (int fd, const struct AD *ICB, uint8_t *FileType, struct AD *File)
-{
- uint8_t *LogBlock;
- uint32_t lbnum;
- uint16_t TagID;
-
- if ((LogBlock = (uint8_t*)malloc(DVD_VIDEO_LB_LEN)) == NULL) {
- fprintf(stderr, "%s: malloc failed\n", __XINE_FUNCTION__);
- return 0;
- }
-
- lbnum=partition.Start+ICB->Location;
-
- do {
- if (!UDFReadLB(fd, lbnum++,1,LogBlock)) TagID=0;
- else UDFDescriptor(LogBlock,&TagID);
-
- if (TagID==261) {
- UDFFileEntry(LogBlock,FileType,File);
- free(LogBlock);
- return 1;
- };
- } while ((lbnum<=partition.Start+ICB->Location+(ICB->Length-1)/DVD_VIDEO_LB_LEN) && (TagID!=261));
-
- free(LogBlock);
- return 0;
-}
-
-/*
- * Dir: Location of directory to scan
- * FileName: Name of file to look for
- * FileICB: Location of ICB of the found file
- * return 1 on success, 0 on error;
- */
-
-int UDFScanDir (int fd, const struct AD *Dir, char *FileName, struct AD *FileICB)
-{
- uint8_t *LogBlock;
- uint32_t lbnum, lb_dir_end, offset;
- uint16_t TagID;
- uint8_t filechar;
- char *filename;
- int p, retval = 0;
-
- LogBlock = (uint8_t*)malloc(DVD_VIDEO_LB_LEN * 30);
- if ( !LogBlock ) {
- fprintf(stderr, MALLOC_FAILED, __XINE_FUNCTION__, DVD_VIDEO_LB_LEN * 30);
- goto error_0;
- }
-
- filename = (char*)malloc(MAX_FILE_LEN);
- if ( !filename ) {
- fprintf(stderr, MALLOC_FAILED, __XINE_FUNCTION__, MAX_FILE_LEN);
- goto error_1;
- }
-
- /*
- * read complete directory
- */
-
- lbnum = partition.Start+Dir->Location;
- lb_dir_end = partition.Start+Dir->Location+(Dir->Length-1)/DVD_VIDEO_LB_LEN;
- offset = 0;
-
- while (lbnum<=lb_dir_end) {
-
- if (!UDFReadLB(fd, lbnum++,1,&LogBlock[offset]))
- break;
-
- offset += DVD_VIDEO_LB_LEN;
- }
-
- /* Scan dir for ICB of file */
-
- p=0;
- while (p<offset) {
- UDFDescriptor (&LogBlock[p],&TagID);
-
- if (TagID==257) {
- p += UDFFileIdentifier(&LogBlock[p],&filechar,filename,FileICB);
- if (!strcasecmp (FileName,filename)) {
- retval = 1;
- goto bail;
- }
- } else
- p=offset;
- }
-
- retval = 0;
-
-bail:
- free(filename);
-
-error_1:
- free(LogBlock);
-
-error_0:
- return retval;
-}
-
-
-/*
- * looks for partition on the disc
- * partnum: number of the partition, starting at 0
- * part: structure to fill with the partition information
- * return 1 if partition found, 0 on error;
- */
-
-int UDFFindPartition (int fd, int partnum, struct Partition *part)
-{
- uint8_t *LogBlock,*Anchor;
- uint32_t lbnum,MVDS_location,MVDS_length;
- uint16_t TagID;
- uint32_t lastsector;
- int i,terminate,volvalid,retval = 0;
-
- Anchor = (uint8_t*)malloc(DVD_VIDEO_LB_LEN);
- if ( !Anchor ) {
- fprintf(stderr, MALLOC_FAILED, __XINE_FUNCTION__, DVD_VIDEO_LB_LEN);
- goto error_0;
- }
-
- LogBlock = (uint8_t*)malloc(DVD_VIDEO_LB_LEN);
- if ( !LogBlock ) {
- fprintf(stderr, MALLOC_FAILED, __XINE_FUNCTION__, DVD_VIDEO_LB_LEN);
- goto error_1;
- }
-
- /* find anchor */
- lastsector=0;
- lbnum=256; /* try #1, prime anchor */
- terminate=0;
-
- while (1) { /* loop da loop */
- if (UDFReadLB(fd, lbnum,1,Anchor)) {
- UDFDescriptor(Anchor,&TagID);
- } else
- TagID=0;
-
- if (TagID!=2) { /* not an anchor? */
- if (terminate) goto bail; /* final try failed */
- if (lastsector) { /* we already found the last sector */
- lbnum=lastsector; /* try #3, alternative backup anchor */
- terminate=1; /* but thats just about enough, then! */
- } else {
- /* TODO: find last sector of the disc (this is optional) */
- if (lastsector) lbnum=lastsector-256; /* try #2, backup anchor */
- else goto bail; /* unable to find last sector */
- }
- } else break; /* it is an anchor! continue... */
- }
-
- UDFExtentAD(&Anchor[16],&MVDS_length,&MVDS_location); /* main volume descriptor */
-
- part->valid=0;
- volvalid=0;
- part->VolumeDesc[0]='\0';
-
- i=1;
- do {
- /* Find Volume Descriptor */
- lbnum=MVDS_location;
- do {
- if (!UDFReadLB (fd, lbnum++, 1, LogBlock))
- 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)) UDFExtentAD(&Anchor[24],&MVDS_length,&MVDS_location); /* backup volume descriptor */
- } while (i-- && ((!part->valid) || (!volvalid)));
-
- retval = part->valid; /* we only care for the partition, not the volume */
-
-bail:
- free(LogBlock);
-
-error_1:
- free(Anchor);
-
-error_0:
- return retval;
-}
-
-
-/*
- * looks for a file on the UDF disc/imagefile and seeks to it's location
- * filename has to be the absolute pathname on the UDF filesystem,
- * starting with '/'
- * returns absolute LB number, or 0 on error
- */
-
-off_t UDFFindFile (int fd, char *filename, off_t *size)
-{
- uint8_t *LogBlock;
- uint8_t filetype;
- uint32_t lbnum, retval = 0;
- uint16_t TagID;
- struct AD RootICB,File,ICB;
- char *tokenline;
- char *tokenbuf;
- char *token;
- off_t lb_number;
- int Partition=0; /* this is the standard location for DVD Video */
-
- tokenline = (char*)malloc(MAX_FILE_LEN);
- if ( !tokenline ) {
- fprintf(stderr, MALLOC_FAILED, __XINE_FUNCTION__, MAX_FILE_LEN);
- goto error_0;
- }
-
- LogBlock = (uint8_t*)malloc(DVD_VIDEO_LB_LEN);
- if ( !LogBlock ) {
- fprintf(stderr, MALLOC_FAILED, __XINE_FUNCTION__, DVD_VIDEO_LB_LEN);
- goto error_1;
- }
-
- memset(tokenline, 0, MAX_FILE_LEN);
- strncat(tokenline, filename, MAX_FILE_LEN);
-
- /* Find partition */
- if (!UDFFindPartition(fd, Partition,&partition))
- goto bail;
-
- /* Find root dir ICB */
- lbnum=partition.Start;
-
- do {
- if (!UDFReadLB(fd, lbnum++, 1, LogBlock))
- TagID=0;
- else
- UDFDescriptor(LogBlock,&TagID);
-
- if (TagID==256) /* File Set Descriptor */
- UDFAD(&LogBlock[400],&RootICB,UDFADlong);
- } while ((lbnum<partition.Start+partition.Length) && (TagID!=8) && (TagID!=256));
-
- if ( (TagID!=256) || (RootICB.Partition != Partition) )
- goto bail;
-
- /* Find root dir */
- if ( !UDFMapICB(fd, &RootICB, &filetype, &File) )
- goto bail;
- if (filetype != 4) /* root dir should be dir */
- goto bail;
-
- /* Tokenize filepath */
- token = strtok_r(tokenline, "/", &tokenbuf);
- while (token) {
- if (!UDFScanDir(fd, &File, token, &ICB))
- goto bail;
- if (!UDFMapICB(fd, &ICB, &filetype, &File))
- goto bail;
- token = strtok_r(NULL, "/", &tokenbuf);
- }
-
- *size = File.Length;
-
- lb_number = partition.Start+File.Location ;
-
- retval = lb_number;
-
-bail:
- free(LogBlock);
-
-error_1:
- free(tokenline);
-
-error_0:
- return retval;
-}
-
-
-/*
- * lists contents of given directory
- */
-
-void UDFListDir(int fd, char *dirname, int nMaxFiles, char **file_list, int *nFiles) {
- uint8_t *LogBlock;
- uint32_t lbnum;
- uint16_t TagID;
- struct AD RootICB,Dir,ICB;
- char *tokenline;
- char *tokenbuf;
- char *token, *ntoken;
- uint8_t filetype;
- char *filename;
- int p;
- uint8_t filechar;
- char *dest;
- int Partition=0; /* this is the standard location for DVD Video */
-
- filename = (char*)malloc(MAX_FILE_LEN);
- if ( !filename )
- {
- fprintf(stderr, MALLOC_FAILED, __XINE_FUNCTION__, MAX_FILE_LEN);
- goto error_0;
- }
-
- tokenline = (char*)malloc(MAX_FILE_LEN);
- if ( !tokenline )
- {
- fprintf(stderr, MALLOC_FAILED, __XINE_FUNCTION__, MAX_FILE_LEN);
- goto error_1;
- }
-
- LogBlock = (uint8_t*)malloc(DVD_VIDEO_LB_LEN * 30);
- if ( !LogBlock )
- {
- fprintf(stderr, MALLOC_FAILED, __XINE_FUNCTION__, DVD_VIDEO_LB_LEN*30);
- goto error_2;
- }
-
- *nFiles = 0;
- tokenline[0]='\0';
- strncat(tokenline,dirname,MAX_FILE_LEN);
-
- /* Find partition */
- if (!UDFFindPartition(fd, Partition,&partition))
- goto bail; /* no partition found (no disc ??) */
-
- /* Find root dir ICB */
- lbnum=partition.Start;
- do {
- if (!UDFReadLB(fd, lbnum++,1,LogBlock))
- TagID=0;
- else
- UDFDescriptor(LogBlock,&TagID);
-
- if (TagID==256) { // File Set Descriptor
- UDFAD(&LogBlock[400],&RootICB,UDFADlong);
- }
- } while ((lbnum<partition.Start+partition.Length)
- && (TagID!=8) && (TagID!=256));
-
- if (TagID!=256)
- goto bail;
- if (RootICB.Partition!=Partition)
- goto bail;
-
- /* Find root dir */
- if (!UDFMapICB(fd, &RootICB, &filetype, &Dir))
- goto bail;
- if (filetype!=4)
- goto bail; /* root dir should be dir */
-
-
-
- /* Tokenize filepath */
- token = strtok_r(tokenline, "/", &tokenbuf);
- ntoken = strtok_r(NULL, "/", &tokenbuf);
- while (token != NULL) {
-
- if (!UDFScanDir(fd, &Dir, token, &ICB))
- goto bail;
- if (!UDFMapICB(fd, &ICB, &filetype, &Dir))
- goto bail;
-
- if (ntoken == NULL) {
- uint32_t lb_dir_end, offset;
-
- /*
- * read complete directory
- */
-
- lbnum = partition.Start+Dir.Location;
- lb_dir_end = partition.Start+Dir.Location+(Dir.Length-1)/DVD_VIDEO_LB_LEN;
- offset = 0;
-
- while (lbnum<=lb_dir_end) {
-
- if (!UDFReadLB(fd, lbnum++,1,&LogBlock[offset]))
- break;
-
- offset += DVD_VIDEO_LB_LEN;
- }
-
-
- p=0;
- while (p<offset) {
- UDFDescriptor(&LogBlock[p],&TagID);
- /* printf ("tagid : %d\n",TagID); */
- if (TagID==257) {
- p+=UDFFileIdentifier(&LogBlock[p],&filechar,filename,&ICB);
-
- /* printf ("file : >%s< %d (p: %d)\n", filename, *nFiles,p); */
-
- if (strcmp (filename,"")) {
-
- dest = file_list[*nFiles];
- strncpy (dest,filename,256);
- (*nFiles)++;
-
- if ((*nFiles)>=nMaxFiles)
- goto bail;
- }
- } else {
- p=offset;
- }
- }
- }
-
- token = ntoken;
- ntoken = strtok_r(NULL, "/", &tokenbuf);
- }
-
-bail:
- free(LogBlock);
-
-error_2:
- free(tokenline);
-
-error_1:
- free(filename);
-
-error_0:
- return;
-}
-
diff --git a/src/input/input_dvd.c b/src/input/input_dvd.c
index 6e87dc282..d37734d89 100644
--- a/src/input/input_dvd.c
+++ b/src/input/input_dvd.c
@@ -1,5 +1,6 @@
-/*
- * Copyright (C) 2000-2001 the xine project
+/*
+ * Copyright (C) 2000, 2001 the xine project,
+ * Rich Wareham <richwareham@users.sourceforge.net>
*
* This file is part of xine, a unix video player.
*
@@ -7,871 +8,1397 @@
* 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.
- *
+ *
* xine 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
*
- * $Id: input_dvd.c,v 1.52 2002/07/05 17:32:01 mroi Exp $
+ * $Id: input_dvd.c,v 1.53 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+/* This file was origninally part of the xine-dvdnav project
+ * at http://dvd.sf.net/.
+ */
+
+/* TODO:
+ *
+ * - Proper internationalisation of strings.
+ * - Failure dialogue.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
-#include <dlfcn.h>
+/* Standard includes */
#include <stdio.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <errno.h>
-#include <unistd.h>
#include <stdlib.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
#include <string.h>
-#include <inttypes.h>
-
-#ifdef HAVE_SYS_CDIO_H
-# include <sys/cdio.h>
-#endif
-#ifdef HAVE_LINUX_CDROM_H
-# include <linux/cdrom.h>
-#elif defined __FreeBSD__
-# include "sys/dvdio.h"
-#endif
-#if ! defined (HAVE_LINUX_CDROM_H) && ! defined (HAVE_SYS_CDIO_H)
-#error "you need to add dvd support for your platform to input_dvd.c and configure.in"
-#endif
+#include <errno.h>
-#include "xine_internal.h"
-#include "xineutils.h"
-#include "input_plugin.h"
-#include "dvd_udf.h"
-#include "read_cache.h"
+#include <sys/mount.h>
+#include <sys/wait.h>
-extern int errno;
+#include <sys/poll.h>
+#include <sys/ioctl.h>
-#ifdef __GNUC__
-#define LOG_MSG_STDERR(xine, message, args...) { \
- xine_log(xine, XINE_LOG_MSG, message, ##args); \
- fprintf(stderr, message, ##args); \
- }
-#define LOG_MSG(xine, message, args...) { \
- xine_log(xine, XINE_LOG_MSG, message, ##args); \
- printf(message, ##args); \
- }
+#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
+#include <sys/dvdio.h>
+#include <sys/cdio.h> /* CDIOCALLOW etc... */
+#elif defined(__linux__)
+#include <linux/cdrom.h>
#else
-#define LOG_MSG_STDERR(xine, ...) { \
- xine_log(xine, XINE_LOG_MSG, __VA_ARGS__); \
- fprintf(stderr, __VA_ARGS__); \
- }
-#define LOG_MSG(xine, ...) { \
- xine_log(xine, XINE_LOG_MSG, __VA_ARGS__); \
- printf(__VA_ARGS__); \
- }
+#error "Need the DVD ioctls"
#endif
+/* Xine includes */
+#include <xine/xineutils.h>
+#include <xine/buffer.h>
+#include <xine/input_plugin.h>
+#include <xine/video_out.h>
+#include <xine/events.h>
+#include <xine/metronom.h>
+#include <xine/spu_decoder_api.h>
+#include <xine/xine_internal.h>
+
+/* DVDNAV includes */
+#include <dvdnav.h>
+
+/* libdvdread includes */
+#include <dvdread/nav_read.h>
+
+/* Print debug messages? */
+/* #define INPUT_DEBUG 1 */
+
+/* Print trace messages? */
+/* #define INPUT_DEBUG_TRACE */
+
+/* Print debug of eject */
+/* #define LOG_DVD_EJECT */
+
+/* Current play mode (title only or menus?) */
+#define MODE_NAVIGATE 0
+#define MODE_TITLE 1
+
+/* Is seeking enabled? 1 - Yes, 0 - No */
+#define CAN_SEEK 1
+
+/* The default DVD device on Solaris is not /dev/dvd */
#if defined(__sun)
-#define RDVD "/vol/dev/aliases/cdrom0"
-#define DVD RDVD
+#define DVD_PATH "/vol/dev/aliases/cdrom0"
#else
-#define DVD "/dev/dvd"
-#define RDVD "/dev/rdvd"
+#define DVD_PATH "/dev/dvd"
+#endif
+
+/* Some misc. defines */
+#define DVD_BLOCK_SIZE 2048
+#ifndef BUF_DEMUX_BLOCK
+#define BUF_DEMUX_BLOCK 0x05000000
#endif
+#define VIDEO_FILL_THROTTLE 5
-typedef struct {
+/* Debugging macros */
+#if INPUT_DEBUG
+#define dprint(s, args...) fprintf(stderr, __FUNCTION__ ": " s, ##args);
+#else
+#define dprint(s, args...) /* Nowt */
+#endif
- input_plugin_t input_plugin;
+#if INPUT_DEBUG_TRACE
+#define trace_print(s, args...) fprintf(stdout, __FUNCTION__ ": " s, ##args);
+#else
+#define trace_print(s, args...) /* Nothing */
+#endif
- xine_t *xine;
-
- char *mrl;
- config_values_t *config;
-
- int dvd_fd;
- int raw_fd;
- read_cache_t *read_cache;
- off_t file_size;
- off_t file_size_left;
- int file_lbstart;
- int file_lbcur;
- int gVTSMinor;
- int gVTSMajor;
-
- const char *device;
- const char *raw_device;
+/* Globals */
+extern int errno;
- /*
- * udf dir function
- */
+/* Array to hold MRLs returned by get_autoplay_list */
#define MAX_DIR_ENTRIES 250
-
- char *filelist[MAX_DIR_ENTRIES];
- char *filelist2[MAX_DIR_ENTRIES];
-
- int mrls_allocated_entries;
- mrl_t **mrls;
+#define MAX_STR_LEN 255
+char filelist[MAX_DIR_ENTRIES][MAX_STR_LEN];
+char *filelist2[MAX_DIR_ENTRIES];
-} dvd_input_plugin_t;
+/* A Temporary string (FIXME: May cause problems if multiple
+ * dvdnavs in multiple threads). */
+char temp_str[256];
+#define TEMP_STR_LEN 255
+typedef struct {
+ input_plugin_t input_plugin; /* Parent input plugin type */
+ int pause_timer; /* Cell stil-time timer */
+ int pause_counter;
+ time_t pause_end_time;
+ int32_t buttonN;
-/* ***************************************************************** */
-/* Private functions */
-/* ***************************************************************** */
-/*
- * Callbacks for configuratoin changes.
- */
-static void device_change_cb(void *data, cfg_entry_t *cfg) {
- dvd_input_plugin_t *this = (dvd_input_plugin_t *) data;
+ /* Flags */
+ int opened; /* 1 if the DVD device is already open */
- this->device = cfg->str_value;
-}
-
-static void rawdevice_change_cb(void *data, cfg_entry_t *cfg) {
- dvd_input_plugin_t *this = (dvd_input_plugin_t *) data;
+ /* Xine specific variables */
+ config_values_t *config; /* Pointer to XineRC config file */
+ char *dvd_device; /* Default DVD device */
+ char *current_dvd_device; /* DVD device currently open */
+ char *mrl; /* Current MRL */
+ int mode;
+ dvdnav_t *dvdnav; /* Handle for libdvdnav */
+ xine_t *xine;
+ char dvd_name[128];
+ size_t dvd_name_length;
+ mrl_t **mrls;
+ int num_mrls;
- this->raw_device = cfg->str_value;
-}
-
-static int openDrive (dvd_input_plugin_t *this) {
+ /* special buffer handling for libdvdnav caching */
+ pthread_mutex_t buf_mutex;
+ void *source;
+ void (*free_buffer)(buf_element_t *);
+ int mem_stack;
+ unsigned char *mem[1024];
+} dvdnav_input_plugin_t;
+
+static void flush_buffers(dvdnav_input_plugin_t *this);
+static void xine_dvdnav_send_button_update(dvdnav_input_plugin_t *this, int mode);
+
+/* Callback on device name change */
+static void device_change_cb(void *data, cfg_entry_t *cfg) {
+ dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t *) data;
- this->dvd_fd = open(this->device, O_RDONLY /* | O_NONBLOCK */ );
-
- if (this->dvd_fd < 0) {
- LOG_MSG(this->xine, _("input_dvd: unable to open dvd drive (%s): %s\n"),
- this->device, strerror(errno));
- return -1;
- }
-
- this->raw_fd = open(this->raw_device, O_RDONLY /* | O_NONBLOCK */ );
- if (this->raw_fd < 0) {
- this->raw_fd = this->dvd_fd;
- }
+ this->dvd_device = cfg->str_value;
+}
- read_cache_set_fd (this->read_cache, this->raw_fd);
+static uint32_t dvdnav_plugin_get_capabilities (input_plugin_t *this_gen) {
+ trace_print("Called\n");
- return this->raw_fd;
+ return INPUT_CAP_AUTOPLAY | INPUT_CAP_BLOCK | INPUT_CAP_CLUT |
+#if CAN_SEEK
+ INPUT_CAP_SEEKABLE | INPUT_CAP_VARIABLE_BITRATE |
+#endif
+ INPUT_CAP_AUDIOLANG | INPUT_CAP_SPULANG | INPUT_CAP_GET_DIR | INPUT_CAP_CHAPTERS;
}
-static void closeDrive (dvd_input_plugin_t *this) {
+void read_ahead_cb(void *this_gen, cfg_entry_t *entry) {
+ dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen;
- if (this->dvd_fd < 0)
- return;
-
- close (this->dvd_fd);
- if (this->raw_fd != this->dvd_fd)
- close (this->raw_fd);
+ if(!this)
+ return;
- this->dvd_fd = -1;
+ if(!this->dvdnav)
+ return;
+ dvdnav_set_readahead_flag(this->dvdnav, entry->num_value);
}
+
+void region_changed_cb(void *this_gen, cfg_entry_t *entry) {
+ dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen;
-#ifdef __sun
-#include <sys/scsi/generic/commands.h>
-#include <sys/scsi/impl/uscsi.h>
-#include <sys/stat.h>
-
-/* SCSI mmc3 DVD Commands */
-#define GPCMD_READ_DVD_STRUCTURE 0xad
-#define GPCMD_SEND_DVD_STRUCTURE 0xad
-#define GPCMD_REPORT_KEY 0xa4
-#define GPCMD_SEND_KEY 0xa3
+ if(!this)
+ return;
-/* 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
+ if(!this->dvdnav)
+ return;
-struct dvd_copyright {
- uint8_t type;
+ if((entry->num_value >= 1) && (entry->num_value <= 8)) {
+ /* FIXME: Remove debug message */
+ dprint("Setting region code to %i (0x%x)\n",
+ entry->num_value, 1<<(entry->num_value-1));
+ dvdnav_set_region_mask(this->dvdnav, 1<<(entry->num_value-1));
+ }
+}
- uint8_t layer_num;
- uint8_t cpst;
- uint8_t rmi;
-};
+void language_changed_cb(void *this_gen, cfg_entry_t *entry) {
+ dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen;
-typedef union {
- uint8_t type;
+ if(!this)
+ return;
-/*
- struct dvd_physical physical;
-*/
- struct dvd_copyright copyright;
-/*
- struct dvd_disckey disckey;
- struct dvd_bca bca;
- struct dvd_manufact manufact;
-*/
-} dvd_struct;
+ if(!this->dvdnav)
+ return;
+ dvdnav_menu_language_select(this->dvdnav, entry->str_value);
+ dvdnav_audio_language_select(this->dvdnav, entry->str_value);
+ dvdnav_spu_language_select(this->dvdnav, entry->str_value);
+}
+
+void update_title_display(dvdnav_input_plugin_t *this) {
+ xine_ui_event_t uevent;
+ int tt=-1, pr=-1;
+ size_t temp_str_length=0;
-/*
- * Read DVD "Copyright Structure" from DVD Drive
- */
-static int
-dvd_read_copyright(dvd_input_plugin_t *this, dvd_struct *s)
-{
- struct uscsi_cmd sc;
- union scsi_cdb rs_cdb;
- uint8_t buf[8];
-
- memset(&rs_cdb, 0, sizeof(rs_cdb));
- rs_cdb.scc_cmd = GPCMD_READ_DVD_STRUCTURE;
- rs_cdb.cdb_opaque[6] = s->copyright.layer_num;
- rs_cdb.cdb_opaque[7] = s->type;
- rs_cdb.cdb_opaque[8] = (sizeof(buf) >> 8) & 0xff;
- rs_cdb.cdb_opaque[9] = sizeof(buf) & 0xff;
-
- memset(&sc, 0, sizeof(sc));
- sc.uscsi_cdb = (caddr_t)&rs_cdb;
- sc.uscsi_cdblen = 12;
- sc.uscsi_bufaddr = buf;
- sc.uscsi_buflen = sizeof(buf);
- sc.uscsi_flags = USCSI_ISOLATE|USCSI_READ;
- sc.uscsi_timeout = 15;
+ if(!this || !(this->xine))
+ return;
- memset(buf, 0, sizeof(buf));
+ /* Set title/chapter display */
+ uevent.event.type = XINE_EVENT_UI_SET_TITLE;
+ uevent.data = temp_str;
- if (ioctl(this->raw_fd, USCSICMD, &sc)) {
- LOG_MSG(this->xine, _("USCSICMD dvd_read_copyright: %s"), strerror(errno));
- return -1;
+ dvdnav_current_title_info(this->dvdnav, &tt, &pr);
+
+ if(tt != -1) {
+ int num_angle = 0, cur_angle = 0;
+ /* no menu here */
+ /* Reflect angle info if appropriate */
+ dvdnav_get_angle_info(this->dvdnav, &cur_angle, &num_angle);
+ if(num_angle > 1) {
+ snprintf(temp_str, TEMP_STR_LEN,
+ "Title %i, Chapter %i, Angle %i of %i",
+ tt,pr,cur_angle, num_angle);
+ } else {
+ snprintf(temp_str, TEMP_STR_LEN,
+ "Title %i, Chapter %i",
+ tt,pr);
+ }
+ } else {
+ strcpy(temp_str, "DVD Navigator: Menu");
}
- if (sc.uscsi_status) {
- LOG_MSG_STDERR(this->xine, _("bad status: READ DVD STRUCTURE (copyright)\n"));
- return -1;
+ temp_str_length = strlen(temp_str);
+
+ if (this->dvd_name[0] != 0 && (temp_str_length + this->dvd_name_length < TEMP_STR_LEN)) {
+ snprintf(temp_str+temp_str_length, TEMP_STR_LEN - temp_str_length,
+ ", %s",
+ &this->dvd_name[0]);
}
+
+ dprint("Changing title to read '%s'\n", temp_str);
+ xine_send_event(this->xine, &uevent.event);
+}
- s->copyright.cpst = buf[4];
- s->copyright.rmi = buf[5];
+static void dvdnav_plugin_stop (input_plugin_t *this_gen) {
+ dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*) this_gen;
+ fprintf(stderr, "dvdnav_plugin_stop called.\n");
+ if (this->dvdnav) {
+ fprintf(stderr, "Now get out of still.\n");
+ dvdnav_still_skip(this->dvdnav);
+ }
+}
- return 0;
+static void dvdnav_plugin_close (input_plugin_t *this_gen) {
+ dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen;
+
+ trace_print("Called\n");
+
+ if(this->opened)
+ dvdnav_close(this->dvdnav);
+ this->dvdnav = NULL;
+ this->opened = 0;
+ this->dvd_name[0] = 0;
+ this->dvd_name_length = 0;
}
+static void dvdnav_build_mrl_list(dvdnav_input_plugin_t *this) {
+ int num_titles, *num_parts;
-/*
- * Check the environment, if we're running under sun's
- * vold/rmmount control.
- */
-static void
-check_solaris_vold_device(dvd_input_plugin_t *this)
-{
- char *volume_device;
- char *volume_name;
- char *volume_action;
- char *device;
- struct stat stb;
+ /* skip DVD if already open */
+ if (this->opened) return;
+ if (this->mrls) {
+ free(this->mrls);
+ this->mrls = NULL;
+ this->num_mrls = 0;
+ }
- if ((volume_device = getenv("VOLUME_DEVICE")) != NULL &&
- (volume_name = getenv("VOLUME_NAME")) != NULL &&
- (volume_action = getenv("VOLUME_ACTION")) != NULL &&
- strcmp(volume_action, "insert") == 0) {
+ if (dvdnav_open(&(this->dvdnav),
+ this->current_dvd_device) == DVDNAV_STATUS_ERR)
+ return;
+
+ dvdnav_get_number_of_titles(this->dvdnav, &num_titles);
+ if ((num_parts = (int *) calloc(num_titles, sizeof(int)))) {
+ int num_mrls = 1, i;
+ /* for each title, count the number of programs */
+ for (i = 1; i <= num_titles; i++) {
+ num_parts[i-1] = 0;
+ dvdnav_title_play(this->dvdnav, i);
+ dvdnav_get_number_of_programs(this->dvdnav, &num_parts[i-1]);
+ num_mrls += num_parts[i-1]; /* num_mrls = total number of programs */
+ }
- device = malloc(strlen(volume_device) + strlen(volume_name) + 2);
- if (device == NULL)
- return;
- sprintf(device, "%s/%s", volume_device, volume_name);
- if (stat(device, &stb) != 0 || !S_ISCHR(stb.st_mode)) {
- free(device);
- return;
+ /* allocate enough memory for:
+ * - a list of pointers to mrls sizeof(mrl_t *) * num_mrls + 1
+ * - an array of mrl structures sizeof(mrl_t) * num_mrls
+ * - enough chars for every filename sizeof(char)*25 * num_mrls
+ * - "dvd://:000000.000000\0" = 25 chars
+ */
+ if ((this->mrls = (mrl_t **) malloc(sizeof(mrl_t *) + num_mrls *
+ (sizeof(mrl_t*) + sizeof(mrl_t) + 25*sizeof(char))))) {
+
+ /* the first mrl struct comes after the pointer list */
+ mrl_t *mrl = (mrl_t *) &this->mrls[num_mrls+1];
+ /* the chars for filenames come after the mrl structs */
+ char *name = (char *) &mrl[num_mrls];
+ int pos = 0, j;
+ this->num_mrls = num_mrls;
+
+ for (i = 1; i <= num_titles; i++) {
+ for (j = (i == 1 ? 0 : 1); j <= num_parts[i-1]; j++) {
+ this->mrls[pos++] = mrl;
+ mrl->origin = NULL;
+ mrl->mrl = name;
+ mrl->link = NULL;
+ mrl->type = mrl_dvd;
+ mrl->size = 0;
+ snprintf(name, 25, (j == 0) ? "dvd://" :
+ (j == 1) ? "dvd://:%d" :
+ "dvd://:%d.%d", i, j);
+ name = &name[25];
+ mrl++;
+ }
+ }
+ this->mrls[pos] = NULL; /* terminate list */
}
- this->device = this->raw_device = device;
+ free(num_parts);
}
+
+ /* Reset the VM so that we don't break anything */
+ /* dvdnav_reset(this->dvdnav); */
+
+ dvdnav_close(this->dvdnav);
}
-#endif
/*
- * try to open dvd and prepare to read >filename<
+ * Opens the DVD plugin. The MRL takes the following form:
+ *
+ * dvd://[dvd_path][:vts[.program]]
*
- * returns lbnum on success, 0 otherwise
+ * e.g.
+ * dvd:// - Play (navigate) /dev/dvd
+ * dvd:///dev/dvd2 - Play (navigate) /dev/dvd2
+ * dvd:///dev/dvd2:1 - Play Title 1 from /dev/dvd2
+ * dvd://:1.3 - Play Title 1, program 3 from /dev/dvd
*/
-static int openDVDFile (dvd_input_plugin_t *this,
- char *filename, off_t *size) {
- char str[256];
- int lbnum;
- int encrypted=0;
-
- if (openDrive(this) < 0) {
- LOG_MSG(this->xine, _("input_dvd: cannot open dvd drive >%s<\n"), this->device);
+static int dvdnav_plugin_open (input_plugin_t *this_gen, char *mrl) {
+ char *locator;
+ int colon_point;
+ dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t *) this_gen;
+ dvdnav_status_t ret;
+ char *intended_dvd_device;
+ cfg_entry_t *region_entry, *lang_entry, *cache_entry;
+
+ trace_print("Called\n");
+ /* printf("open1: dvdnav=%p opened=%d\n",this->dvdnav, this->opened); */
+
+ this->mrl = mrl;
+ this->pause_timer = 0;
+ this->dvd_name[0] = 0;
+ this->dvd_name_length = 0;
+
+ /* Check we can handle this MRL */
+ if (!strncasecmp (mrl, "dvd://",9))
+ locator = &mrl[9];
+ else {
return 0;
}
-#if defined HAVE_LINUX_CDROM_H
- {
- dvd_struct dvd;
-
- dvd.copyright.type = DVD_STRUCT_COPYRIGHT;
- dvd.copyright.layer_num = 0;
- if (ioctl (this->dvd_fd, DVD_READ_STRUCT, &dvd) < 0) {
- LOG_MSG(this->xine, _("input_dvd: Could not read Copyright Structure\n"));
- return 0;
- }
- encrypted = (dvd.copyright.cpst != 0) ;
+ /* Attempt to parse MRL */
+ colon_point=0;
+ while((locator[colon_point] != '\0') && (locator[colon_point] != ':')) {
+ colon_point++;
}
-#elif defined __FreeBSD__
- {
- struct dvd_struct dvd;
- dvd.format = DVD_STRUCT_COPYRIGHT;
- dvd.layer_num = 0;
+ if(locator[colon_point] == ':') {
+ this->mode = MODE_TITLE;
+ } else {
+ this->mode = MODE_NAVIGATE;
+ }
- if (ioctl(this->dvd_fd, DVDIOCREADSTRUCTURE, &dvd) < 0) {
- LOG_MSG(this->xine, _("input_dvd: Could not read Copyright Structure\n"));
+ locator[colon_point] = '\0';
+ ret = DVDNAV_STATUS_OK;
+ if(colon_point == 0) {
+ /* Use default device */
+ intended_dvd_device=this->dvd_device;
+ } else {
+ /* Use specified device */
+ intended_dvd_device=locator;
+ }
+
+ if(this->opened) {
+ if ( intended_dvd_device==this->current_dvd_device ) {
+ /* Already open, so skip opening */
+ } else {
+ /* Changing DVD device */
+ dvdnav_close(this->dvdnav);
+ this->dvdnav=NULL;
+ this->opened=0;
+ ret = dvdnav_open(&this->dvdnav, intended_dvd_device);
+ if(ret == DVDNAV_STATUS_ERR) {
+ fprintf(stderr, "Error opening DVD device\n");
+ return 0;
+ }
+ this->opened=1;
+ this->current_dvd_device=intended_dvd_device;
+ }
+ } else {
+ ret = dvdnav_open(&this->dvdnav, intended_dvd_device);
+ if(ret == DVDNAV_STATUS_ERR) {
+ fprintf(stderr, "Error opening DVD device\n");
return 0;
}
-
- encrypted = (dvd.cpst != 0);
+ this->opened=1;
+ this->current_dvd_device=intended_dvd_device;
}
-#elif defined __sun
- {
- dvd_struct dvd;
-
- dvd.copyright.type = DVD_STRUCT_COPYRIGHT;
- dvd.copyright.layer_num = 0;
- if (dvd_read_copyright(this, &dvd) < 0) {
- LOG_MSG(this->xine, _("input_dvd: Could not read Copyright Structure.\n"
- " Assuming disk is not encrypted.\n"));
- } else
- encrypted = (dvd.copyright.cpst != 0);
+ if (1) {
+ int fd, i;
+ off64_t off;
+ uint8_t data[DVD_VIDEO_LB_LEN];
+
+ /* Read DVD name */
+ fd=open(intended_dvd_device, O_RDONLY);
+ if (fd > 0) {
+ off = lseek64( fd, 32 * (int64_t) DVD_VIDEO_LB_LEN, SEEK_SET );
+ if( off == ( 32 * (int64_t) DVD_VIDEO_LB_LEN ) ) {
+ off = read( fd, data, DVD_VIDEO_LB_LEN );
+ close(fd);
+ if (off == ( (int64_t) DVD_VIDEO_LB_LEN )) {
+ fprintf( stderr, "DVD Title: ");
+ for(i=25; i < 73; i++ ) {
+ if((data[i] == 0)) break;
+ if((data[i] > 32) && (data[i] < 127)) {
+ fprintf(stderr, "%c", data[i]);
+ } else {
+ fprintf(stderr, " ");
+ }
+ }
+ strncpy(&this->dvd_name[0], &data[25], 48);
+ /* fprintf(stderr, "TITLE:%s\n",&this->dvd_name[0]); */
+ this->dvd_name[48]=0;
+ this->dvd_name_length=strlen(&this->dvd_name[0]);
+ fprintf( stderr, "\nDVD Serial Number: ");
+ for(i=73; i < 89; i++ ) {
+ if((data[i] == 0)) break;
+ if((data[i] > 32) && (data[i] < 127)) {
+ fprintf(stderr, "%c", data[i]);
+ } else {
+ fprintf(stderr, " ");
+ }
+ }
+ fprintf( stderr, "\nDVD Title (Alternative): ");
+ for(i=89; i < 128; i++ ) {
+ if((data[i] == 0)) break;
+ if((data[i] > 32) && (data[i] < 127)) {
+ fprintf(stderr, "%c", data[i]);
+ } else {
+ fprintf(stderr, " ");
+ }
+ }
+ fprintf( stderr, "\n");
+ } else {
+ fprintf( stderr, "libdvdread: Can't read name block. Probably not a DVD-ROM device.\n");
+ }
+ } else {
+ fprintf( stderr, "libdvdread: Can't seek to block %u\n", 32 );
+ }
+ } else {
+ fprintf(stderr,"NAME OPEN FAILED\n");
+ }
}
-#endif
- if( encrypted ) {
- LOG_MSG(this->xine,
- _("\ninput_dvd: Sorry, this plugin doesn't play encrypted DVDs. The legal status\n"
- " of CSS decryption is unclear and we can't provide such code.\n"
- " Please check http://dvd.sf.net for more information.\n"));
- return 0;
+
+ /* Set region code */
+ region_entry = this->config->lookup_entry(this->config,
+ "input.dvd_region");
+ if(region_entry) {
+ region_changed_cb(this, region_entry);
}
-
- snprintf (str, sizeof(str), "/VIDEO_TS/%s", filename);
-
- if (!(lbnum = UDFFindFile(this->dvd_fd, str, size))) {
- LOG_MSG(this->xine, _("input_dvd: cannot open file >%s<\n"), filename);
-
- closeDrive (this);
-
- return 0;
+
+ /* Set languages */
+ lang_entry = this->config->lookup_entry(this->config,
+ "input.dvdnav_language");
+ if(lang_entry) {
+ language_changed_cb(this, lang_entry);
}
+
+ /* Set cache usage */
+ cache_entry = this->config->lookup_entry(this->config,
+ "input.dvdnav_use_readahead");
+ if(cache_entry) {
+ read_ahead_cb(this, cache_entry);
+ }
+
+ if(this->mode == MODE_TITLE) {
+ int tt, i, pr, found;
+ int titles;
+
+ /* A program and/or VTS was specified */
+ locator += colon_point + 1;
- lseek (this->raw_fd, lbnum * (off_t) DVD_VIDEO_LB_LEN, SEEK_SET) ;
-
- return lbnum;
-}
-/* ***************************************************************** */
-/* END OF PRIVATES */
-/* ***************************************************************** */
-
-/*
- *
- */
-static uint32_t dvd_plugin_get_capabilities (input_plugin_t *this) {
- return INPUT_CAP_SEEKABLE | INPUT_CAP_PREVIEW | INPUT_CAP_BLOCK | INPUT_CAP_AUTOPLAY | INPUT_CAP_GET_DIR;
-}
-
-/*
- *
- */
-static int dvd_plugin_open (input_plugin_t *this_gen, char *mrl) {
- char *filename;
- dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen;
-
- this->mrl = mrl;
-
- /*
- * do we handle this kind of MRL ?
- */
- if (strncasecmp (mrl, "dvd://", 6))
- return 0;
+ if(locator[0] == '\0') {
+ /* Empty specifier */
+ fprintf(stderr, "Incorrect MRL format.\n");
+ dvdnav_close(this->dvdnav);
+ return 0;
+ }
- filename = (char *) &mrl[6];
+ /* See if there is a period. */
+ found = -1;
+ for(i=0; i<strlen(locator); i++) {
+ if(locator[i] == '.') {
+ found = i;
+ locator[i] = '\0';
+ }
+ }
+ tt = strtol(locator, NULL,10);
- sscanf (filename, "VTS_%d_%d.VOB", &this->gVTSMajor, &this->gVTSMinor);
+ dvdnav_get_number_of_titles(this->dvdnav, &titles);
+ if((tt <= 0) || (tt > titles)) {
+ fprintf(stderr, "Title %i is out of range (1 to %i).\n", tt,
+ titles);
+ dvdnav_close(this->dvdnav);
+ return 0;
+ }
- this->file_lbstart = openDVDFile (this, filename, &this->file_size) ;
- this->file_lbcur = this->file_lbstart;
+ /* If there was a program specified, get that too. */
+ pr = -1;
+ if(found != -1) {
+ pr = strtol(locator+found+1, NULL,10);
+ }
- if (!this->file_lbstart) {
- LOG_MSG(this->xine, _("input_dvd: Unable to find >%s< on dvd.\n"), filename);
- return 0;
+ dprint("Jumping to VTS >%i<, prog >%i<\n", tt, pr);
+ if(pr != -1) {
+ dvdnav_part_play(this->dvdnav, tt, pr);
+ } else {
+ dvdnav_title_play(this->dvdnav, tt);
+ }
}
-
- this->file_size_left = this->file_size;
-
- return 1 ;
-}
-
-static int dvd_plugin_is_branch_possible(input_plugin_t *this_gen, char *nextmrl ) {
- dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen;
- char *mrl;
-
- if (strncasecmp (nextmrl, "dvd://", 6))
- return 0;
-
- mrl = this->mrl;
- mrl += 6;
- nextmrl += 6;
+ dprint("DVD device successfully opened.\n");
- if( strncasecmp (mrl, "VTS_", 4) || strncasecmp (nextmrl, "VTS_", 4) )
- return 0;
-
return 1;
-}
-
-static off_t dvd_plugin_read (input_plugin_t *this_gen,
- char *buf, off_t nlen) {
-
- dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen;
- int bytes_read;
+}
- if (nlen != DVD_VIDEO_LB_LEN) {
+static void dvdnav_plugin_free_buffer(buf_element_t *buf) {
+ dvdnav_input_plugin_t *this = buf->source;
+
+ pthread_mutex_lock(&this->buf_mutex);
+ /* give this buffer back to libdvdnav */
+ dvdnav_free_cache_block(this->dvdnav, buf->mem);
+ /* reconstruct the original xine buffer */
+ buf->free_buffer = this->free_buffer;
+ buf->source = this->source;
+ buf->mem = this->mem[--this->mem_stack];
+ pthread_mutex_unlock(&this->buf_mutex);
+ /* give this buffer back to xine's pool */
+ buf->free_buffer(buf);
+}
- LOG_MSG(this->xine, _("input_dvd: error read: %Ld bytes is not a sector!\n"),
- nlen);
+static buf_element_t *dvdnav_plugin_read_block (input_plugin_t *this_gen,
+ fifo_buffer_t *fifo, off_t nlen) {
+ dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen;
+ buf_element_t *buf;
+ dvdnav_status_t result;
+ int event, len;
+ int finished = 0;
+ unsigned char *block;
- return 0;
+ if(fifo == NULL) {
+ dprint("values of \\beta will give rise to dom!\n");
+ return NULL;
}
- if (this->file_size_left < nlen)
- return 0;
+ /* Read buffer */
+ buf = fifo->buffer_pool_alloc (fifo);
+ block = buf->mem;
- bytes_read = read (this->raw_fd, buf, DVD_VIDEO_LB_LEN);
- if (bytes_read == DVD_VIDEO_LB_LEN) {
+ while(!finished) {
+ if (block != buf->mem) {
+ /* if we already have a dvdnav cache block, give it back first */
+ dvdnav_free_cache_block(this->dvdnav, block);
+ block = buf->mem;
+ }
+ result = dvdnav_get_next_cache_block (this->dvdnav, &block, &event, &len);
+ if(result == DVDNAV_STATUS_ERR) {
+ fprintf(stderr, "Error getting next block from DVD (%s)\n",
+ dvdnav_err_to_string(this->dvdnav));
+ if (block != buf->mem) dvdnav_free_cache_block(this->dvdnav, block);
+ buf->free_buffer(buf);
+ return NULL;
+ }
- this->file_lbcur++;
- this->file_size_left -= DVD_VIDEO_LB_LEN;
+ switch(event) {
+ case DVDNAV_BLOCK_OK:
+ {
+ buf->content = block;
+ buf->type = BUF_DEMUX_BLOCK;
- return DVD_VIDEO_LB_LEN;
- } else if (bytes_read < 0) {
- LOG_MSG(this->xine, _("input_dvd: read error in input_dvd plugin (%s)\n"),
- strerror (errno));
+ /* Make sure we don't think we are still paused */
+ this->pause_timer = 0;
+
+ finished = 1;
+ }
+ break;
+ case DVDNAV_NOP:
+ {
+ /* Nothing */
+ }
+ break;
+ case DVDNAV_STILL_FRAME:
+ {
+
+ /* OK, So xine no-longer accepts BUF_VIDEO_FILLs, find out
+ * how else we provide the hint
+ */
+ dvdnav_still_event_t *still_event =
+ (dvdnav_still_event_t*)(block);
+ buf->type = BUF_CONTROL_NOP;
+ finished = 1;
+
+ /* Xine's method of doing still-frames */
+ if (this->pause_timer == 0) {
+ dprint("dvd:input_dvdnav.c:Stillframe! (pause time = 0x%02x)\n",
+ still_event->length);
+ this->pause_timer = still_event->length;
+ this->pause_end_time = time(NULL) + this->pause_timer;
+ this->pause_counter = 0;
+ break;
+ }
+
+ if(this->pause_timer == 0xff) {
+ this->pause_counter++;
+ xine_usec_sleep(100000);
+ break;
+ }
+ if ( (this->pause_timer != 0xFF) &&
+ (time(NULL) >= this->pause_end_time) ){
+ this->pause_timer = 0;
+ this->pause_end_time = 0;
+ dvdnav_still_skip(this->dvdnav);
+ break;
+ }
+ if(this->pause_timer) {
+ this->pause_counter++;
+ dprint("dvd:input_dvdnav.c:Stillframe! (pause_timer = 0x%02x) counter=%d\n",
+ still_event->length, this->pause_counter);
+ xine_usec_sleep(100000);
+ break;
+ }
+ }
+ break;
+ case DVDNAV_SPU_STREAM_CHANGE:
+ {
+ dvdnav_spu_stream_change_event_t *stream_event =
+ (dvdnav_spu_stream_change_event_t*) (block);
+ buf->content = block;
+ buf->type = BUF_CONTROL_SPU_CHANNEL;
+ buf->decoder_info[0] = stream_event->physical_wide;
+ buf->decoder_info[1] = stream_event->physical_letterbox;
+ buf->decoder_info[2] = stream_event->physical_pan_scan;
+ dprint("SPU stream wide %d, letterbox %d, pan&scan %d\n",
+ stream_event->physical_wide,
+ stream_event->physical_letterbox,
+ stream_event->physical_pan_scan);
+ finished = 1;
+ }
+ break;
+ case DVDNAV_AUDIO_STREAM_CHANGE:
+ {
+ dvdnav_audio_stream_change_event_t *stream_event =
+ (dvdnav_audio_stream_change_event_t*) (block);
+ buf->content = block;
+ buf->type = BUF_CONTROL_AUDIO_CHANNEL;
+ buf->decoder_info[0] = stream_event->physical;
+ dprint("AUDIO stream %d\n", stream_event->physical);
+ finished = 1;
+ }
+ break;
+ case DVDNAV_HIGHLIGHT:
+ {
+ xine_dvdnav_send_button_update(this, 0);
+ }
+ break;
+ case DVDNAV_VTS_CHANGE:
+ {
+ int aspect, permission;
+
+ dprint("VTS change\n");
+
+ /* Check for video aspect change and scaling permissions */
+ aspect = dvdnav_get_video_aspect(this->dvdnav);
+ permission = dvdnav_get_video_scale_permission(this->dvdnav);
+
+ buf->type = BUF_VIDEO_MPEG;
+ buf->decoder_flags = BUF_FLAG_SPECIAL;
+ buf->decoder_info[1] = BUF_SPECIAL_ASPECT;
+ buf->decoder_info[2] = aspect;
+ buf->decoder_info[3] = permission;
+ finished = 1;
+ }
+ break;
+ case DVDNAV_CELL_CHANGE:
+ {
+ xine_ui_event_t uevent;
+
+ /* Tell Xine to update the UI */
+ uevent.event.type = XINE_EVENT_UI_CHANNELS_CHANGED;
+ uevent.data = NULL;
+ xine_send_event(this->xine, &uevent.event);
+
+ update_title_display(this);
+ }
+ break;
+ case DVDNAV_SEEK_DONE:
+ {
+ dprint("Seek done\n");
+ /* FIXME: This should send a message to clear all currently displaying subtitle. */
+ }
+ break;
+ case DVDNAV_HOP_CHANNEL:
+ {
+ flush_buffers(this);
+ break;
+ }
+ case DVDNAV_NAV_PACKET:
+ {
+ buf->content = block;
+ buf->type = BUF_DEMUX_BLOCK;
+ finished = 1;
+ }
+ break;
+ case DVDNAV_SPU_CLUT_CHANGE:
+ {
+ buf->content = block;
+ buf->type = BUF_SPU_CLUT;
+ finished = 1;
+ }
+ break;
+ case DVDNAV_STOP:
+ {
+ if (buf->mem != block) dvdnav_free_cache_block(this->dvdnav, block);
+ buf->free_buffer(buf);
+ /* return NULL to indicate end of stream */
+ return NULL;
+ }
+ default:
+ dprint("FIXME: Unknown event (%i)\n", event);
+ break;
+ }
}
- else {
- LOG_MSG(this->xine, _("input_dvd: short read in input_dvd (%d != %d)\n"),
- bytes_read, DVD_VIDEO_LB_LEN);
+
+ if (block != buf->mem) {
+ /* we have received a buffer from the libdvdnav cache, store all
+ * necessary values to reconstruct xine's buffer and modify it according to
+ * our needs. */
+ pthread_mutex_lock(&this->buf_mutex);
+ if (this->mem_stack < 1024) {
+ this->mem[this->mem_stack++] = buf->mem;
+ this->free_buffer = buf->free_buffer;
+ this->source = buf->source;
+ buf->mem = block;
+ buf->free_buffer = dvdnav_plugin_free_buffer;
+ buf->source = this;
+ } else {
+ /* the stack for storing the memory chunks from xine is full, we cannot
+ * modify the buffer, because we would not be able to reconstruct it.
+ * Therefore we copy the data and give the buffer back. */
+ dprint("too many buffers issued, memory stack exceeded\n");
+ memcpy(buf->mem, block, 2048);
+ dvdnav_free_cache_block(this->dvdnav, block);
+ buf->content = buf->mem;
+ }
+ pthread_mutex_unlock(&this->buf_mutex);
}
- return 0;
+ return buf;
}
+static off_t dvdnav_plugin_read (input_plugin_t *this_gen, char *ch_buf, off_t len) {
+/* dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen; */
-static buf_element_t *dvd_plugin_read_block (input_plugin_t *this_gen,
- fifo_buffer_t *fifo, off_t nlen) {
- dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen;
- buf_element_t *buf;
-
- if (nlen != DVD_VIDEO_LB_LEN || this->file_size_left < nlen) {
- /*
- * Hide the error reporting now, demuxer try to read 6 bytes
- * at STAGE_BY_CONTENT probe stage
- */
- if(nlen != DVD_VIDEO_LB_LEN)
- LOG_MSG(this->xine,
- _("input_dvd: error in input_dvd plugin read: %Ld bytes "
- "is not a sector!\n"), nlen);
- return NULL;
- }
-
- if ((buf = read_cache_read_block (this->read_cache, (off_t)this->file_lbcur*DVD_VIDEO_LB_LEN))) {
+ /* FIXME: Implement somehow */
- this->file_lbcur++;
- this->file_size_left -= DVD_VIDEO_LB_LEN;
- buf->type = BUF_DEMUX_BLOCK;
+ return 0;
+}
+
+static off_t dvdnav_plugin_seek (input_plugin_t *this_gen, off_t offset, int origin) {
+ dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen;
+
+ trace_print("Called\n");
- } else {
- LOG_MSG(this->xine, _("input_dvd: read error in input_dvd plugin\n"));
+ if(!this || !this->dvdnav) {
+ return -1;
}
+
+ return dvdnav_sector_search(this->dvdnav, offset / DVD_BLOCK_SIZE , origin) * DVD_BLOCK_SIZE;
-
- return buf;
+ return -1;
}
+static off_t dvdnav_plugin_get_current_pos (input_plugin_t *this_gen){
+ dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen;
+ uint32_t pos=0;
+ uint32_t length=1;
+ dvdnav_status_t result;
+ trace_print("Called\n");
-static off_t dvd_plugin_seek (input_plugin_t *this_gen, off_t offset, int origin) {
- dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen;
-
- offset /= DVD_VIDEO_LB_LEN;
-
- switch (origin) {
- case SEEK_END:
- offset = (this->file_size / DVD_VIDEO_LB_LEN) - offset;
-
- case SEEK_SET:
- this->file_lbcur = this->file_lbstart + offset;
- this->file_size_left = this->file_size - (offset * DVD_VIDEO_LB_LEN);
- break;
- case SEEK_CUR:
- if (offset) {
- this->file_lbcur += offset;
- this->file_size_left = this->file_size -
- ((this->file_lbcur - this->file_lbstart) * DVD_VIDEO_LB_LEN);
- } else {
- return (this->file_lbcur - this->file_lbstart) *
- (off_t) DVD_VIDEO_LB_LEN;
- }
-
- break;
- default:
- LOG_MSG(this->xine, _("input_dvd: seek: %d is an unknown origin\n"), origin);
+ if(!this || !this->dvdnav) {
+ return 0;
}
-
- return lseek (this->raw_fd,
- this->file_lbcur * (off_t) DVD_VIDEO_LB_LEN, SEEK_SET)
- - this->file_lbstart * (off_t) DVD_VIDEO_LB_LEN;
+ result = dvdnav_get_position(this->dvdnav, &pos, &length);
+ return (off_t)pos * (off_t)2048;
}
+static off_t dvdnav_plugin_get_length (input_plugin_t *this_gen) {
+ dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen;
+ uint32_t pos=0;
+ uint32_t length=1;
+ dvdnav_status_t result;
+
+ trace_print("Called\n");
-static off_t dvd_plugin_get_current_pos (input_plugin_t *this_gen){
- dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen;
+ if(!this || !this->dvdnav) {
+ return 0;
+ }
- return ((this->file_lbcur - this->file_lbstart) * DVD_VIDEO_LB_LEN);
+ result = dvdnav_get_position(this->dvdnav, &pos, &length);
+ return (off_t)length * (off_t)2048;
}
+static uint32_t dvdnav_plugin_get_blocksize (input_plugin_t *this_gen) {
+ trace_print("Called\n");
-static off_t dvd_plugin_get_length (input_plugin_t *this_gen) {
- dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen;
-
- return this->file_size;
+ return DVD_BLOCK_SIZE;
}
+static mrl_t **dvdnav_plugin_get_dir (input_plugin_t *this_gen,
+ char *filename, int *nFiles) {
+ dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen;
-static uint32_t dvd_plugin_get_blocksize (input_plugin_t *this_gen) {
+ trace_print("Called\n");
+ if (filename) { *nFiles = 0; return NULL; }
- return DVD_VIDEO_LB_LEN;
+ dvdnav_build_mrl_list((dvdnav_input_plugin_t *) this_gen);
+ *nFiles = this->num_mrls;
+ return this->mrls;
}
+static int dvdnav_umount_media(char *device)
+{
+ char *argv[10];
+ int i;
+ pid_t pid;
+ int status;
+ argv[0]="umount";
+ argv[1]=device;
+ argv[2]=0;
+ pid=fork();
+ if (pid == 0) {
+ i= execv("/bin/umount", argv);
+ exit(127);
+ }
+ do {
+ if(waitpid(pid, &status, 0) == -1) {
+ if (errno != EINTR)
+ return -1;
+ }
+ else {
+ return WEXITSTATUS(status);
+ }
+ } while(1);
+
+ return -1;
+}
+
-static int dvd_plugin_eject_media (input_plugin_t *this_gen) {
- dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen;
+static int dvdnav_plugin_eject_media (input_plugin_t *this_gen) {
+ dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t *) this_gen;
int ret, status;
int fd;
- if((fd = open(this->device, O_RDONLY|O_NONBLOCK)) > -1) {
+ /* printf("dvd:Eject Device %s current device %s opened=%d handle=%p trying...\n",this->dvd_device, this->current_dvd_device, this->opened, this->dvdnav); */
+ dvdnav_plugin_close (this_gen) ;
+ ret=dvdnav_umount_media(this->current_dvd_device);
+ /**********
+ printf ("umount result: %s\n",
+ strerror(errno));
+ ***********/
+ if ((fd = open (this->current_dvd_device, O_RDONLY|O_NONBLOCK)) > -1) {
-#if defined (HAVE_LINUX_CDROM_H)
+#if defined (__linux__)
if((status = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)) > 0) {
switch(status) {
case CDS_TRAY_OPEN:
- if((ret = ioctl(fd, CDROMCLOSETRAY)) != 0) {
- LOG_MSG(this->xine, _("input_dvd: CDROMCLOSETRAY failed: %s\n"),
- strerror(errno));
- }
- break;
+ if((ret = ioctl(fd, CDROMCLOSETRAY)) != 0) {
+#ifdef LOG_DVD_EJECT
+ dprint ("CDROMCLOSETRAY failed: %s\n",
+ strerror(errno));
+#endif
+ }
+ break;
case CDS_DISC_OK:
- if((ret = ioctl(fd, CDROMEJECT)) != 0) {
- LOG_MSG(this->xine, _("input_dvd: CDROMEJECT failed: %s\n"), strerror(errno));
- }
- break;
+ if((ret = ioctl(fd, CDROMEJECT)) != 0) {
+#ifdef LOG_DVD_EJECT
+ dprint ("CDROMEJECT failed: %s\n", strerror(errno));
+#endif
+ }
+ break;
}
}
else {
- LOG_MSG(this->xine, _("input_dvd: CDROM_DRIVE_STATUS failed: %s\n"),
- strerror(errno));
+#ifdef LOG_DVD_EJECT
+ dprint ("CDROM_DRIVE_STATUS failed: %s\n",
+ strerror(errno));
+#endif
close(fd);
return 0;
}
+#elif defined (__NetBSD__) || defined (__OpenBSD__) || defined (__FreeBSD__)
-#elif defined (HAVE_CDIO_H)
-
-# if defined (__sun)
- status = 0;
- if ((ret = ioctl(fd, CDROMEJECT)) != 0) {
- LOG_MSG(this->xine, _("input_dvd: CDROMEJECT failed: %s\n"), strerror(errno));
- }
-
-# else
if (ioctl(fd, CDIOCALLOW) == -1) {
- LOG_MSG(this->xine, _("ioctl(cdromallow): %s"), strerror(errno));
+ perror("ioctl(cdromallow)");
} else {
if (ioctl(fd, CDIOCEJECT) == -1) {
- LOG_MSG(this->xine, _("ioctl(cdromeject): %s"), strerror(errno));
+ perror("ioctl(cdromeject)");
}
}
-# endif
#endif
close(fd);
+ } else {
+ dprint("Device %s failed to open during eject calls\n",this->current_dvd_device);
}
return 1;
}
+static char* dvdnav_plugin_get_mrl (input_plugin_t *this_gen) {
+ dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen;
+
+ trace_print("Called\n");
-static void dvd_plugin_close (input_plugin_t *this_gen) {
- dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen;
-
- closeDrive (this);
+ return this->mrl;
}
+static char *dvdnav_plugin_get_description (input_plugin_t *this_gen) {
+ trace_print("Called\n");
-static void dvd_plugin_stop (input_plugin_t *this_gen) {
- dvd_plugin_close(this_gen);
+ return "DVD Navigator";
}
+static char *dvdnav_plugin_get_identifier (input_plugin_t *this_gen) {
+ trace_print("Called\n");
-static char *dvd_plugin_get_description (input_plugin_t *this_gen) {
-
- return _("dvd device input plugin as shipped with xine");
+ return "DVD";
}
+static void flush_buffers(dvdnav_input_plugin_t *this) {
+ /* Small hack for still menus with audio. Thanks to
+ * the Captain for doing this in d5d. The changes are necessary to
+ * stop some audio problems (esp. with R2 'Dalekmania').
+ */
-static char *dvd_plugin_get_identifier (input_plugin_t *this_gen) {
+ if (this->xine->audio_fifo)
+ this->xine->audio_fifo->clear (this->xine->audio_fifo);
+
+ if (this->xine->video_fifo)
+ this->xine->video_fifo->clear (this->xine->video_fifo);
- return "DVD";
+
+ if (this->xine->cur_audio_decoder_plugin)
+ this->xine->cur_audio_decoder_plugin->reset(this->xine->cur_audio_decoder_plugin);
+ if (this->xine->cur_video_decoder_plugin)
+ this->xine->cur_video_decoder_plugin->flush(this->xine->cur_video_decoder_plugin);
}
+static void xine_dvdnav_send_button_update(dvdnav_input_plugin_t *this, int mode) {
+ int button;
+ spu_button_t spu_button;
+ xine_spu_event_t spu_event;
+ dvdnav_get_current_highlight(this->dvdnav, &button);
+ if (button == this->buttonN && (mode ==0) ) return;
+ this->buttonN = button; /* Avoid duplicate sending of button info */
+ dprint("sending_button_update button=%d mode=%d\n", button, mode);
+ /* Do we want to show or hide the button? */
+ /* libspudec will control hiding */
+ spu_event.event.type = XINE_EVENT_SPU_BUTTON;
+ spu_event.data = &spu_button;
+ spu_button.show = mode + 1; /* mode=0 select, 1 activate. */
+ spu_button.buttonN = button;
+ xine_send_event(this->xine, &spu_event.event);
+}
-static mrl_t **dvd_plugin_get_dir (input_plugin_t *this_gen,
- char *filename, int *nEntries) {
- dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen;
- int i, fd;
+static void dvdnav_event_listener (void *this_gen, xine_event_t *event) {
- *nEntries = 0;
-
- if (filename)
- return NULL;
-
- if((fd = open(this->device, O_RDONLY /* | O_NONBLOCK */ )) > -1) {
- int nFiles, nFiles2;
-
- UDFListDir (fd, "/VIDEO_TS", MAX_DIR_ENTRIES, this->filelist, &nFiles);
+ dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t *) this_gen;
- nFiles2 = 0;
- for (i=0; i<nFiles; i++) {
- int nLen;
+ if(!this->dvdnav) {
+ return;
+ }
- nLen = strlen (this->filelist[i]);
+ switch(event->type) {
+ case XINE_EVENT_INPUT_MENU1:
+ dvdnav_menu_call(this->dvdnav, DVD_MENU_Root);
+ break;
+ case XINE_EVENT_INPUT_MENU2:
+ dvdnav_menu_call(this->dvdnav, DVD_MENU_Title);
+ break;
+ case XINE_EVENT_INPUT_MENU3:
+ dvdnav_menu_call(this->dvdnav, DVD_MENU_Audio);
+ break;
+ case XINE_EVENT_INPUT_NEXT:
+ dvdnav_next_pg_search(this->dvdnav);
+ break;
+ case XINE_EVENT_INPUT_PREVIOUS:
+ dvdnav_prev_pg_search(this->dvdnav);
+ break;
+ case XINE_EVENT_INPUT_ANGLE_NEXT:
+ {
+ int num = 0, current = 0;
+ dvdnav_get_angle_info(this->dvdnav, &current, &num);
+
+ if(num != 0) {
+ current ++;
+ if(current > num)
+ current = 1;
+ }
+ dvdnav_angle_change(this->dvdnav, current);
+ dprint("Changing to angle %i\n", current);
+
+ update_title_display(this);
+ }
+ break;
+ case XINE_EVENT_INPUT_ANGLE_PREVIOUS:
+ {
+ int num = 0, current = 0;
+ dvdnav_get_angle_info(this->dvdnav, &current, &num);
+
+ if(num != 0) {
+ current --;
+ if(current <= 0)
+ current = num;
+ }
+ dvdnav_angle_change(this->dvdnav, current);
+ dprint("Changing to angle %i\n", current);
+
+ update_title_display(this);
+ }
+ break;
+ case XINE_EVENT_INPUT_SELECT:
+ {
+ xine_dvdnav_send_button_update(this, 1);
+ dvdnav_button_activate(this->dvdnav);
+ }
+ break;
+ case XINE_EVENT_MOUSE_BUTTON:
+ {
+ xine_input_event_t *input_event = (xine_input_event_t*) event;
+ xine_dvdnav_send_button_update(this, 1);
+ dvdnav_mouse_activate(this->dvdnav, input_event->x,
+ input_event->y);
+ }
+ break;
+ case XINE_EVENT_INPUT_BUTTON_FORCE: /* For libspudec to feedback forced button select from NAV PCI packets. */
+ {
+ xine_spu_event_t *spu_event = (xine_spu_event_t *) event;
+ spu_button_t *but = spu_event->data;
+ fprintf(stderr, "xine_dvd:BUTTON_FORCE %d\n", but->buttonN);
+ dvdnav_button_select(this->dvdnav, but->buttonN);
+ }
+ break;
+ case XINE_EVENT_MOUSE_MOVE:
+ {
+ xine_input_event_t *input_event = (xine_input_event_t*) event;
+ /* printf("Mouse move (x,y) = (%i,%i)\n", input_event->x,
+ input_event->y); */
+ dvdnav_mouse_select(this->dvdnav, input_event->x, input_event->y);
+ xine_dvdnav_send_button_update(this, 0);
+ }
+ break;
+ case XINE_EVENT_INPUT_UP:
+ dvdnav_upper_button_select(this->dvdnav);
+ xine_dvdnav_send_button_update(this, 0);
+ break;
+ case XINE_EVENT_INPUT_DOWN:
+ dvdnav_lower_button_select(this->dvdnav);
+ xine_dvdnav_send_button_update(this, 0);
+ break;
+ case XINE_EVENT_INPUT_LEFT:
+ dvdnav_left_button_select(this->dvdnav);
+ xine_dvdnav_send_button_update(this, 0);
+ break;
+ case XINE_EVENT_INPUT_RIGHT:
+ dvdnav_right_button_select(this->dvdnav);
+ xine_dvdnav_send_button_update(this, 0);
+ break;
+ }
+
+ return;
+}
- if (nLen<4)
- continue;
+static int dvdnav_plugin_get_optional_data (input_plugin_t *this_gen,
+ void *data, int data_type) {
+ dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t *) this_gen;
+
+ switch(data_type) {
- if (!strcasecmp (&this->filelist[i][nLen-4], ".VOB")) {
- char str[1024];
+ case INPUT_OPTIONAL_DATA_AUDIOLANG: {
+ uint16_t lang;
+ int8_t channel;
+
+ /* Be paranoid */
+ if(this && this->xine && this->dvdnav) {
- if(nFiles2 >= this->mrls_allocated_entries) {
- ++this->mrls_allocated_entries;
- /* note: 1 extra pointer for terminating NULL */
- this->mrls = realloc(this->mrls, (this->mrls_allocated_entries+1) * sizeof(mrl_t*));
- this->mrls[nFiles2] = (mrl_t *) xine_xmalloc(sizeof(mrl_t));
- }
+ if(!(dvdnav_is_domain_vts(this->dvdnav))) {
+ sprintf(data, "%s", "nav");
+ goto __audio_success;
+ }
+
+ channel = (int8_t) xine_get_audio_channel(this->xine);
+ /* printf("********* AUDIO CHANNEL = %d\n", channel); */
+ channel = dvdnav_get_audio_logical_stream(this->dvdnav, channel);
+ if(channel != -1) {
+ lang = dvdnav_audio_stream_to_lang(this->dvdnav, channel);
- if(this->mrls[nFiles2]->mrl) {
- this->mrls[nFiles2]->mrl = (char *)
- realloc(this->mrls[nFiles2]->mrl, strlen(this->filelist[i]) + 7);
- }
+ if(lang != 0xffff) {
+ sprintf(data, " %c%c", lang >> 8, lang & 0xff);
+ }
else {
- this->mrls[nFiles2]->mrl = (char *)
- xine_xmalloc(strlen(this->filelist[i]) + 7);
+ sprintf(data, "%3i", xine_get_audio_channel(this->xine));
}
+ }
+ else {
+ channel = xine_get_audio_channel(this->xine);
+ sprintf(data, "%3i", channel);
+ }
+
+ __audio_success:
+ /* printf("********** RETURNING '%s'\n", (char *)data); */
+ return INPUT_OPTIONAL_SUCCESS;
+ }
+ return INPUT_OPTIONAL_UNSUPPORTED;
+ }
+ break;
- this->mrls[nFiles2]->origin = NULL;
- sprintf(this->mrls[nFiles2]->mrl, "dvd://%s", this->filelist[i]);
- this->mrls[nFiles2]->link = NULL;
- this->mrls[nFiles2]->type = (0 | mrl_dvd);
- /* determine size */
- memset(&str, 0, sizeof(str));
- sprintf (str, "/VIDEO_TS/%s", this->filelist[i]);
- UDFFindFile(fd, str, &this->mrls[nFiles2]->size);
+ case INPUT_OPTIONAL_DATA_SPULANG: {
+ uint16_t lang;
+ int8_t channel;
+
+ /* Be paranoid */
+ if(this && this->xine && this->dvdnav) {
- nFiles2++;
+ if(!(dvdnav_is_domain_vts(this->dvdnav))) {
+ sprintf(data, "%3s", "off");
+ goto __spu_success;
}
+ channel = (int8_t) xine_get_spu_channel(this->xine);
+ /* printf("********* SPU CHANNEL = %i\n", channel); */
+ if(channel == -1)
+ channel = dvdnav_get_spu_logical_stream(this->dvdnav, this->xine->spu_channel);
+ else
+ channel = dvdnav_get_spu_logical_stream(this->dvdnav, channel);
+
+ if(channel != -1) {
+ lang = dvdnav_spu_stream_to_lang(this->dvdnav, channel);
+
+ if(lang != 0xffff) {
+ sprintf(data, " %c%c", lang >> 8, lang & 0xff);
+ }
+ else {
+ sprintf(data, "%3i", xine_get_spu_channel(this->xine));
+ }
+ }
+ else {
+ channel = xine_get_spu_channel(this->xine);
+ if(channel == -1)
+ sprintf(data, "%3s", "off");
+ else
+ sprintf(data, "%3i", channel);
+ }
+
+ __spu_success:
+ /* printf("********** RETURNING '%s'\n", (char *)data); */
+ return INPUT_OPTIONAL_SUCCESS;
}
-
- *nEntries = nFiles2;
-
- close (fd);
-
- }
- else {
- LOG_MSG(this->xine, _("input_dvd: unable to open dvd drive (%s): %s\n"),
- this->device, strerror(errno));
- return NULL;
+ return INPUT_OPTIONAL_UNSUPPORTED;
}
- /*
- * Freeing exceeded mrls if exists.
- */
- while(this->mrls_allocated_entries > *nEntries) {
- MRL_ZERO(this->mrls[this->mrls_allocated_entries - 1]);
- free(this->mrls[this->mrls_allocated_entries--]);
+ break;
+
}
- /*
- * This is useful to let UI know where it should stops ;-).
- */
- this->mrls[*nEntries] = NULL;
-
- return this->mrls;
+ return INPUT_OPTIONAL_UNSUPPORTED;
}
+static char **dvdnav_plugin_get_autoplay_list (input_plugin_t *this_gen,
+ int *nFiles) {
+ dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t *) this_gen;
+ dvdnav_status_t res;
+ int titles, i;
+ trace_print("get_autoplay_list entered\n");
+ /* Close the plugin is opened */
+ if(this->opened) {
+ dvdnav_close(this->dvdnav);
+ this->opened = 0;
+ }
-static char **dvd_plugin_get_autoplay_list (input_plugin_t *this_gen,
- int *nFiles) {
- dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen;
- int i, fd;
-
- if((fd = open(this->device, O_RDONLY /* | O_NONBLOCK */ )) > -1) {
- int nFiles3, nFiles2;
-
- UDFListDir (fd, "/VIDEO_TS", MAX_DIR_ENTRIES, this->filelist, &nFiles3);
-
- nFiles2 = 0;
- for (i=0; i<nFiles3; i++) {
- int nLen;
-
- nLen = strlen (this->filelist[i]);
-
- if (nLen<4)
- continue;
-
- if (!strcasecmp (&this->filelist[i][nLen-4], ".VOB")) {
-
- if(this->filelist2[nFiles2] == NULL)
- this->filelist2[nFiles2] = (char *) realloc(this->filelist2[nFiles2],
- sizeof(char *) * 256);
-
- sprintf (this->filelist2[nFiles2], "dvd://%s", this->filelist[i]);
-
- nFiles2++;
- }
-
- }
-
- *nFiles = nFiles2;
+ /* rebuild thie MRL browser list */
+ dvdnav_build_mrl_list(this);
- this->filelist2[*nFiles] = (char *) realloc(this->filelist2[*nFiles], sizeof(char *));
- this->filelist2[*nFiles] = NULL;
- close (fd);
-
- } else {
- LOG_MSG(this->xine, _("input_dvd: unable to open dvd drive (%s): %s\n"),
- this->device, strerror(errno));
- *nFiles = 0;
+ /* Use default path */
+ res = dvdnav_open(&(this->dvdnav), this->current_dvd_device);
+ if(res == DVDNAV_STATUS_ERR) {
return NULL;
}
- return this->filelist2;
-}
-
+ this->opened = 1;
+
+ /* Return a list of all titles */
+ snprintf (&(filelist[0][0]), MAX_STR_LEN, "dvd://");
+ filelist2[0] = &(filelist[0][0]);
-static char* dvd_plugin_get_mrl (input_plugin_t *this_gen) {
- dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen;
+ dvdnav_get_number_of_titles(this->dvdnav, &titles);
+ for(i=1; i<=titles; i++) {
+ snprintf (&(filelist[i][0]), MAX_STR_LEN, "dvd://:%i", i);
+ filelist2[i] = &(filelist[i][0]);
+ }
+ *nFiles=titles+1;
+ filelist2[*nFiles] = NULL;
+ dprint("get_autoplay_list exiting opened=%d dvdnav=%p\n",this->opened, this->dvdnav);
- return this->mrl;
+ return filelist2;
}
-
-static int dvd_plugin_get_optional_data (input_plugin_t *this_gen,
- void *data, int data_type) {
- /*
- switch(data_type) {
-
- case INPUT_OPTIONAL_DATA_CLUT:
- ...
- return INPUT_OPTIONAL_SUCCESS;
- break;
-
- case INPUT_OPTIONAL_DATA_AUDIOLANG:
- ...
- return INPUT_OPTIONAL_SUCCESS;
- break;
-
- }
- */
- return INPUT_OPTIONAL_UNSUPPORTED;
+void dvdnav_plugin_dispose(input_plugin_t *this_gen) {
+ dvdnav_input_plugin_t *this = (dvdnav_input_plugin_t*)this_gen;
+ pthread_mutex_destroy(&this->buf_mutex);
+ free(this->mrls); this->mrls = NULL;
}
-static void dvd_plugin_dispose (input_plugin_t *this_gen ) {
- dvd_input_plugin_t *this = (dvd_input_plugin_t *) this_gen;
- int i;
+#ifdef __sun
+/*
+ * Check the environment, if we're running under sun's
+ * vold/rmmount control.
+ */
+static void
+check_solaris_vold_device(dvdnav_input_plugin_t *this)
+{
+ char *volume_device;
+ char *volume_name;
+ char *volume_action;
+ char *device;
+ struct stat stb;
- read_cache_free (this->read_cache);
+ if ((volume_device = getenv("VOLUME_DEVICE")) != NULL &&
+ (volume_name = getenv("VOLUME_NAME")) != NULL &&
+ (volume_action = getenv("VOLUME_ACTION")) != NULL &&
+ strcmp(volume_action, "insert") == 0) {
- for (i = 0; i < MAX_DIR_ENTRIES; i++) {
- free (this->filelist[i]);
- free (this->filelist2[i]);
+ device = malloc(strlen(volume_device) + strlen(volume_name) + 2);
+ if (device == NULL)
+ return;
+ sprintf(device, "%s/%s", volume_device, volume_name);
+ if (stat(device, &stb) != 0 || !S_ISCHR(stb.st_mode)) {
+ free(device);
+ return;
+ }
+ this->dvd_device = device;
}
-
- free (this->mrls);
- free (this);
}
-
+#endif
input_plugin_t *init_input_plugin (int iface, xine_t *xine) {
+ dvdnav_input_plugin_t *this;
+ config_values_t *config = xine->config;
+
+ trace_print("Called\n");
+
+ switch (iface) {
+ case 8:
+ this = (dvdnav_input_plugin_t *) malloc (sizeof (dvdnav_input_plugin_t));
+
+ this->input_plugin.interface_version = INPUT_PLUGIN_IFACE_VERSION;
+ this->input_plugin.get_capabilities = dvdnav_plugin_get_capabilities;
+ this->input_plugin.open = dvdnav_plugin_open;
+ this->input_plugin.read = dvdnav_plugin_read;
+ this->input_plugin.read_block = dvdnav_plugin_read_block;
+ this->input_plugin.seek = dvdnav_plugin_seek;
+ this->input_plugin.get_current_pos = dvdnav_plugin_get_current_pos;
+ this->input_plugin.get_length = dvdnav_plugin_get_length;
+ this->input_plugin.get_blocksize = dvdnav_plugin_get_blocksize;
+ this->input_plugin.get_dir = dvdnav_plugin_get_dir;
+ this->input_plugin.eject_media = dvdnav_plugin_eject_media;
+ this->input_plugin.get_mrl = dvdnav_plugin_get_mrl;
+ this->input_plugin.stop = dvdnav_plugin_stop;
+ this->input_plugin.close = dvdnav_plugin_close;
+ this->input_plugin.get_description = dvdnav_plugin_get_description;
+ this->input_plugin.get_identifier = dvdnav_plugin_get_identifier;
+ this->input_plugin.get_autoplay_list = dvdnav_plugin_get_autoplay_list;
+ this->input_plugin.get_optional_data = dvdnav_plugin_get_optional_data;
+ this->input_plugin.is_branch_possible = NULL;
+ this->input_plugin.dispose = dvdnav_plugin_dispose;
+
+ this->config = config;
+ this->xine = xine;
+ this->dvdnav = NULL;
+ this->opened = 0;
+ this->buttonN = 0;
+ this->dvd_name[0] = 0;
+ this->dvd_name_length = 0;
+ this->mrls = NULL;
+ this->num_mrls = 0;
+
+ pthread_mutex_init(&this->buf_mutex, NULL);
+ this->mem_stack = 0;
+
+ xine_register_event_listener(this->xine, dvdnav_event_listener, this);
+ this->dvd_device = config->register_string(config,
+ "input.dvd_device",
+ DVD_PATH,
+ "device used for dvd drive",
+ NULL,
+ device_change_cb, (void *)this);
+ this->current_dvd_device = this->dvd_device;
+
+ config->register_num(config, "input.dvd_region",
+ 1,
+ "Region that DVD player claims "
+ "to be (1 -> 8)",
+ "This only needs to be changed "
+ "if your DVD jumps to a screen "
+ "complaining about region code ",
+ region_changed_cb,
+ this);
+ config->register_string(config, "input.dvdnav_language",
+ "en",
+ "The default language for dvd",
+ "The dvdnav plugin tries to use this "
+ "language as a default. This must be a"
+ "two character ISO country code.",
+ language_changed_cb, this);
+ config->register_bool(config, "input.dvdnav_use_readahead",
+ 1,
+ "Do we use read-ahead caching?",
+ "This "
+ "may lead to jerky playback on low-end "
+ "machines.",
+ read_ahead_cb, this);
+
+#ifdef __sun
+ check_solaris_vold_device(this);
+#endif
- dvd_input_plugin_t *this;
- config_values_t *config;
- int i;
-
- if (iface != 8) {
- LOG_MSG(xine,
- _("dvd input plugin doesn't support plugin API version %d.\n"
- "PLUGIN DISABLED.\n"
- "This means there's a version mismatch between xine and this input"
- "plugin.\nInstalling current input plugins should help.\n"),
+ return (input_plugin_t *) this;
+ break;
+ default:
+ fprintf(stderr,
+ "DVD Navigator input plugin doesn't support plugin API version %d.\n"
+ "PLUGIN DISABLED.\n"
+ "This means there's a version mismatch between xine and this input"
+ "plugin.\nInstalling current input plugins should help.\n",
iface);
return NULL;
}
-
- this = (dvd_input_plugin_t *) xine_xmalloc (sizeof (dvd_input_plugin_t));
- config = xine->config;
- this->xine = xine;
-
- for (i = 0; i < MAX_DIR_ENTRIES; i++) {
- this->filelist[i] = (char *) xine_xmalloc(sizeof(char *) * 256);
- this->filelist2[i] = (char *) xine_xmalloc(sizeof(char *) * 256);
- }
-
- this->input_plugin.interface_version = INPUT_PLUGIN_IFACE_VERSION;
- this->input_plugin.get_capabilities = dvd_plugin_get_capabilities;
- this->input_plugin.open = dvd_plugin_open;
- this->input_plugin.read = dvd_plugin_read;
- this->input_plugin.read_block = dvd_plugin_read_block;
- this->input_plugin.seek = dvd_plugin_seek;
- this->input_plugin.get_current_pos = dvd_plugin_get_current_pos;
- this->input_plugin.get_length = dvd_plugin_get_length;
- this->input_plugin.get_blocksize = dvd_plugin_get_blocksize;
- this->input_plugin.eject_media = dvd_plugin_eject_media;
- this->input_plugin.close = dvd_plugin_close;
- this->input_plugin.stop = dvd_plugin_stop;
- this->input_plugin.get_identifier = dvd_plugin_get_identifier;
- this->input_plugin.get_description = dvd_plugin_get_description;
- this->input_plugin.get_dir = dvd_plugin_get_dir;
- this->input_plugin.get_mrl = dvd_plugin_get_mrl;
- this->input_plugin.get_autoplay_list = dvd_plugin_get_autoplay_list;
- this->input_plugin.get_optional_data = dvd_plugin_get_optional_data;
- this->input_plugin.dispose = dvd_plugin_dispose;
- this->input_plugin.is_branch_possible= NULL;
- /* disable branch until we fix the problems branching from
- menu vob to video vob
- this->input_plugin.is_branch_possible= dvd_plugin_is_branch_possible;
- */
-
- this->device = config->register_string(config, "input.dvd_device", DVD,
- _("path to your local dvd device file"),
- NULL, device_change_cb, (void *)this);
- this->raw_device = config->register_string(config, "input.dvd_raw_device", RDVD,
- _("path to a raw device set up for dvd access"),
- NULL, rawdevice_change_cb, (void*)this);
-#ifdef __sun
- check_solaris_vold_device(this);
-#endif
-
- this->mrls_allocated_entries = 0;
- this->mrls = xine_xmalloc(sizeof(mrl_t*));
-
- this->mrl = NULL;
- this->config = config;
- this->dvd_fd = -1;
- this->raw_fd = -1;
-
- this->read_cache = read_cache_new ();
-
- return (input_plugin_t *) this;
}
+
+/*
+ * $Log: input_dvd.c,v $
+ * Revision 1.53 2002/08/08 17:49:21 richwareham
+ * First stage of DVD plugin -> dvdnav conversion
+ *
+ */
diff --git a/src/input/libdvdnav/Makefile.am b/src/input/libdvdnav/Makefile.am
new file mode 100644
index 000000000..cf30e12e4
--- /dev/null
+++ b/src/input/libdvdnav/Makefile.am
@@ -0,0 +1,30 @@
+DVD_CFLAGS = -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE \
+ -Ilibdvdnav/ \
+ -Ilibdvdread/
+
+CFLAGS = @CFLAGS@ $(DVD_CFLAGS)
+
+noinst_LTLIBRARIES = libdvdnav.la
+
+libdvdnav_la_SOURCES = decoder.c dvdnav.c vm.c vmcmd.c \
+ read_cache.c navigation.c highlight.c \
+ searching.c settings.c
+libdvdnav_la_LDFLAGS = $(THREAD_LIBS) \
+ -avoid-version -module
+
+noinst_HEADERS = decoder.h dvdnav.h dvdnav_events.h \
+ dvdnav_internal.h vm.h vmcmd.h read_cache.h dvd_types.h
+
+debug:
+ @$(MAKE) CFLAGS="$(DEBUG_CFLAGS) $(OGG_CFLAGS) $(VORBIS_CFLAGS)"
+
+install-debug: debug
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+mostlyclean-generic:
+ -rm -f *~ \#* .*~ .\#*
+
+maintainer-clean-generic:
+ -@echo "This command is intended for maintainers to use;"
+ -@echo "it deletes files that may require special tools to rebuild."
+ -rm -f Makefile.in
diff --git a/src/input/libdvdnav/decoder.c b/src/input/libdvdnav/decoder.c
new file mode 100644
index 000000000..8e177b49d
--- /dev/null
+++ b/src/input/libdvdnav/decoder.c
@@ -0,0 +1,768 @@
+/*
+ * Copyright (C) 2000, 2001 Martin Norbäck, Håkan Hjort
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav 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.
+ *
+ * libdvdnav 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
+ *
+ * $Id: decoder.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h> /* For memset */
+#include <dvdread/ifo_types.h> /* vm_cmd_t */
+#include <assert.h>
+#include "vmcmd.h"
+#include "decoder.h"
+
+uint32_t vm_getbits(command_t *command, int start, int count) {
+ uint64_t result = 0;
+ uint64_t bit_mask=0xffffffffffffffff; /* I could put -1 instead */
+ uint64_t examining = 0;
+ int32_t bits;
+ if (count == 0) return 0;
+
+ if ( ((count+start) > 64) ||
+ (count > 32) ||
+ (start > 63) ||
+ (count < 0) ||
+ (start < 0) ){
+ fprintf(stderr, "Bad call to vm_getbits. Parameter out of range\n");
+ assert(0);
+ }
+ bit_mask >>= start;
+ bits = 64-count-start;
+ examining = ((bit_mask >> bits) << bits );
+ command->examined |= examining;
+ result = (command->instruction & bit_mask) >> bits;
+ return (uint32_t) result;
+}
+
+static uint16_t get_GPRM(registers_t* registers, uint8_t reg) {
+ if (registers->GPRM_mode[reg] & 0x01) {
+ struct timeval current_time, time_offset;
+ uint16_t result;
+ /* Counter mode */
+ /* fprintf(stderr, "Getting counter %d\n",reg);*/
+ gettimeofday(&current_time, NULL);
+ time_offset.tv_sec = current_time.tv_sec - registers->GPRM_time[reg].tv_sec;
+ time_offset.tv_usec = current_time.tv_usec - registers->GPRM_time[reg].tv_usec;
+ if (time_offset.tv_usec < 0) {
+ time_offset.tv_sec--;
+ time_offset.tv_usec += 1000000;
+ }
+ result = (uint16_t) (time_offset.tv_sec & 0xffff);
+ registers->GPRM[reg]=result;
+ return result;
+
+ } else {
+ /* Register mode */
+ return registers->GPRM[reg];
+ }
+
+}
+
+static void set_GPRM(registers_t* registers, uint8_t reg, uint16_t value) {
+ if (registers->GPRM_mode[reg] & 0x01) {
+ struct timeval current_time;
+ /* Counter mode */
+ /* fprintf(stderr, "Setting counter %d\n",reg); */
+ gettimeofday(&current_time, NULL);
+ registers->GPRM_time[reg] = current_time;
+ registers->GPRM_time[reg].tv_sec -= value;
+ }
+ registers->GPRM[reg] = value;
+}
+
+/* Eval register code, can either be system or general register.
+ SXXX_XXXX, where S is 1 if it is system register. */
+static uint16_t eval_reg(command_t* command, uint8_t reg) {
+ if(reg & 0x80) {
+ if ((reg & 0x1f) == 20) {
+ fprintf(stderr, "Suspected RCE Region Protection!!!");
+ }
+ return command->registers->SPRM[reg & 0x1f]; /* FIXME max 24 not 32 */
+ } else {
+ return get_GPRM(command->registers, reg & 0x0f) ;
+ }
+}
+
+/* Eval register or immediate data.
+ AAAA_AAAA BBBB_BBBB, if immediate use all 16 bits for data else use
+ lower eight bits for the system or general purpose register. */
+static uint16_t eval_reg_or_data(command_t* command, int32_t imm, int32_t byte) {
+ if(imm) { /* immediate */
+ return vm_getbits(command, (byte*8), 16);
+ } else {
+ return eval_reg(command, vm_getbits(command, ((byte + 1)*8), 8));
+ }
+}
+
+/* Eval register or immediate data.
+ xBBB_BBBB, if immediate use all 7 bits for data else use
+ lower four bits for the general purpose register number. */
+/* Evaluates gprm or data depending on bit, data is in byte n */
+uint16_t eval_reg_or_data_2(command_t* command, int32_t imm, int32_t byte) {
+ if(imm) /* immediate */
+ return vm_getbits(command, ((byte*8)+1), 7);
+ else
+ return get_GPRM(command->registers, (vm_getbits(command, ((byte*8)+4), 4)) );
+}
+
+
+/* Compare data using operation, return result from comparison.
+ Helper function for the different if functions. */
+static int32_t eval_compare(uint8_t operation, uint16_t data1, uint16_t data2) {
+ switch(operation) {
+ case 1:
+ return data1 & data2;
+ case 2:
+ return data1 == data2;
+ case 3:
+ return data1 != data2;
+ case 4:
+ return data1 >= data2;
+ case 5:
+ return data1 > data2;
+ case 6:
+ return data1 <= data2;
+ case 7:
+ return data1 < data2;
+ }
+ fprintf(stderr,"eval_compare: Invalid comparison code\n");
+ return 0;
+}
+
+
+/* Evaluate if version 1.
+ Has comparison data in byte 3 and 4-5 (immediate or register) */
+static int32_t eval_if_version_1(command_t* command) {
+ uint8_t op = vm_getbits(command, 9, 3);
+ if(op) {
+ return eval_compare(op, eval_reg(command, vm_getbits(command, 24, 8)),
+ eval_reg_or_data(command, vm_getbits(command, 8, 1), 4));
+ }
+ return 1;
+}
+
+/* Evaluate if version 2.
+ This version only compares register which are in byte 6 and 7 */
+static int32_t eval_if_version_2(command_t* command) {
+ uint8_t op = vm_getbits(command, 9, 3);
+ if(op) {
+ return eval_compare(op, eval_reg(command, vm_getbits(command, 48, 8)),
+ eval_reg(command, vm_getbits(command, 56, 8)));
+ }
+ return 1;
+}
+
+/* Evaluate if version 3.
+ Has comparison data in byte 2 and 6-7 (immediate or register) */
+static int32_t eval_if_version_3(command_t* command) {
+ uint8_t op = vm_getbits(command, 9, 3);
+ if(op) {
+ return eval_compare(op, eval_reg(command, vm_getbits(command, 16, 8)),
+ eval_reg_or_data(command, vm_getbits(command, 8, 1), 6));
+ }
+ return 1;
+}
+
+/* Evaluate if version 4.
+ Has comparison data in byte 1 and 4-5 (immediate or register)
+ The register in byte 1 is only the lowe nibble (4 bits) */
+static int32_t eval_if_version_4(command_t* command) {
+ uint8_t op = vm_getbits(command, 9, 3);
+ if(op) {
+ return eval_compare(op, eval_reg(command, vm_getbits(command, 12, 4)),
+ eval_reg_or_data(command, vm_getbits(command, 8, 1), 4));
+ }
+ return 1;
+}
+
+/* Evaluate special instruction.... returns the new row/line number,
+ 0 if no new row and 256 if Break. */
+static int32_t eval_special_instruction(command_t* command, int32_t cond) {
+ int32_t line, level;
+
+ switch(vm_getbits(command, 12, 4)) {
+ case 0: /* NOP */
+ line = 0;
+ return cond ? line : 0;
+ case 1: /* Goto line */
+ line = vm_getbits(command, 56, 8);
+ return cond ? line : 0;
+ case 2: /* Break */
+ /* max number of rows < 256, so we will end this set */
+ line = 256;
+ return cond ? 256 : 0;
+ case 3: /* Set temporary parental level and goto */
+ line = vm_getbits(command, 56, 8);
+ level = vm_getbits(command, 52, 4);
+ if(cond) {
+ /* This always succeeds now, if we want real parental protection */
+ /* we need to ask the user and have passwords and stuff. */
+ command->registers->SPRM[13] = level;
+ }
+ return cond ? line : 0;
+ }
+ return 0;
+}
+
+/* Evaluate link by subinstruction.
+ Return 1 if link, or 0 if no link
+ Actual link instruction is in return_values parameter */
+static int32_t eval_link_subins(command_t* command, int32_t cond, link_t *return_values) {
+ uint16_t button = vm_getbits(command, 48, 6);
+ uint8_t linkop = vm_getbits(command, 59, 5);
+
+ if(linkop > 0x10)
+ return 0; /* Unknown Link by Sub-Instruction command */
+
+ /* Assumes that the link_cmd_t enum has the same values as the LinkSIns codes */
+ return_values->command = linkop;
+ return_values->data1 = button;
+ return cond;
+}
+
+
+/* Evaluate link instruction.
+ Return 1 if link, or 0 if no link
+ Actual link instruction is in return_values parameter */
+static int32_t eval_link_instruction(command_t* command, int32_t cond, link_t *return_values) {
+ uint8_t op = vm_getbits(command, 12, 4);
+
+ switch(op) {
+ case 1:
+ return eval_link_subins(command, cond, return_values);
+ case 4:
+ return_values->command = LinkPGCN;
+ return_values->data1 = vm_getbits(command, 49, 15);
+ return cond;
+ case 5:
+ return_values->command = LinkPTTN;
+ return_values->data1 = vm_getbits(command, 54, 10);
+ return_values->data2 = vm_getbits(command, 48, 6);
+ return cond;
+ case 6:
+ return_values->command = LinkPGN;
+ return_values->data1 = vm_getbits(command, 57, 7);
+ return_values->data2 = vm_getbits(command, 48, 6);
+ return cond;
+ case 7:
+ return_values->command = LinkCN;
+ return_values->data1 = vm_getbits(command, 56, 8);
+ return_values->data2 = vm_getbits(command, 48, 6);
+ return cond;
+ }
+ return 0;
+}
+
+
+/* Evaluate a jump instruction.
+ returns 1 if jump or 0 if no jump
+ actual jump instruction is in return_values parameter */
+static int32_t eval_jump_instruction(command_t* command, int32_t cond, link_t *return_values) {
+
+ switch(vm_getbits(command, 12, 4)) {
+ case 1:
+ return_values->command = Exit;
+ return cond;
+ case 2:
+ return_values->command = JumpTT;
+ return_values->data1 = vm_getbits(command, 41, 7);
+ return cond;
+ case 3:
+ return_values->command = JumpVTS_TT;
+ return_values->data1 = vm_getbits(command, 41, 7);
+ return cond;
+ case 5:
+ return_values->command = JumpVTS_PTT;
+ return_values->data1 = vm_getbits(command, 41, 7);
+ return_values->data2 = vm_getbits(command, 22, 10);
+ return cond;
+ case 6:
+ switch(vm_getbits(command, 40, 2)) {
+ case 0:
+ return_values->command = JumpSS_FP;
+ return cond;
+ case 1:
+ return_values->command = JumpSS_VMGM_MENU;
+ return_values->data1 = vm_getbits(command, 44, 4);
+ return cond;
+ case 2:
+ return_values->command = JumpSS_VTSM;
+ return_values->data1 = vm_getbits(command, 32, 8);
+ return_values->data2 = vm_getbits(command, 24, 8);
+ return_values->data3 = vm_getbits(command, 44, 4);
+ return cond;
+ case 3:
+ return_values->command = JumpSS_VMGM_PGC;
+ return_values->data1 = vm_getbits(command, 17, 15);
+ return cond;
+ }
+ break;
+ case 8:
+ switch(vm_getbits(command, 40, 2)) {
+ case 0:
+ return_values->command = CallSS_FP;
+ return_values->data1 = vm_getbits(command, 32, 8);
+ return cond;
+ case 1:
+ return_values->command = CallSS_VMGM_MENU;
+ return_values->data1 = vm_getbits(command, 44, 4);
+ return_values->data2 = vm_getbits(command, 32, 8);
+ return cond;
+ case 2:
+ return_values->command = CallSS_VTSM;
+ return_values->data1 = vm_getbits(command, 44, 4);
+ return_values->data2 = vm_getbits(command, 32, 8);
+ return cond;
+ case 3:
+ return_values->command = CallSS_VMGM_PGC;
+ return_values->data1 = vm_getbits(command, 17, 15);
+ return_values->data2 = vm_getbits(command, 32, 8);
+ return cond;
+ }
+ break;
+ }
+ return 0;
+}
+
+/* Evaluate a set sytem register instruction
+ May contain a link so return the same as eval_link */
+static int32_t eval_system_set(command_t* command, int32_t cond, link_t *return_values) {
+ int32_t i;
+ uint16_t data, data2;
+
+ switch(vm_getbits(command, 4, 4)) {
+ case 1: /* Set system reg 1 &| 2 &| 3 (Audio, Subp. Angle) */
+ for(i = 1; i <= 3; i++) {
+ if(vm_getbits(command, ((2 + i)*8), 1)) {
+ data = eval_reg_or_data_2(command, vm_getbits(command, 3, 1), 2 + i);
+ if(cond) {
+ command->registers->SPRM[i] = data;
+ }
+ }
+ }
+ break;
+ case 2: /* Set system reg 9 & 10 (Navigation timer, Title PGC number) */
+ data = eval_reg_or_data(command, vm_getbits(command, 3, 1), 2);
+ data2 = vm_getbits(command, 40, 8); /* ?? size */
+ if(cond) {
+ command->registers->SPRM[9] = data; /* time */
+ command->registers->SPRM[10] = data2; /* pgcN */
+ }
+ break;
+ case 3: /* Mode: Counter / Register + Set */
+ data = eval_reg_or_data(command, vm_getbits(command, 3, 1), 2);
+ data2 = vm_getbits(command, 44, 4);
+ if(vm_getbits(command, 40, 1)) {
+ command->registers->GPRM_mode[data2] |= 1; /* Set bit 0 */
+ } else {
+ command->registers->GPRM_mode[data2] &= ~ 0x01; /* Reset bit 0 */
+ }
+ if(cond) {
+ set_GPRM(command->registers, data2, data);
+ }
+ break;
+ case 6: /* Set system reg 8 (Highlighted button) */
+ data = eval_reg_or_data(command, vm_getbits(command, 3, 1), 4); /* Not system reg!! */
+ if(cond) {
+ command->registers->SPRM[8] = data;
+ }
+ break;
+ }
+ if(vm_getbits(command, 12, 4)) {
+ return eval_link_instruction(command, cond, return_values);
+ }
+ return 0;
+}
+
+
+/* Evaluate set operation
+ Sets the register given to the value indicated by op and data.
+ For the swap case the contents of reg is stored in reg2.
+*/
+static void eval_set_op(command_t* command, int32_t op, int32_t reg, int32_t reg2, int32_t data) {
+ const int32_t shortmax = 0xffff;
+ int32_t tmp;
+ switch(op) {
+ case 1:
+ set_GPRM(command->registers, reg, data);
+ break;
+ case 2: /* SPECIAL CASE - SWAP! */
+ set_GPRM(command->registers, reg2, get_GPRM(command->registers, reg));
+ set_GPRM(command->registers, reg, data);
+ break;
+ case 3:
+ tmp = get_GPRM(command->registers, reg) + data;
+ if(tmp > shortmax) tmp = shortmax;
+ set_GPRM(command->registers, reg, (uint16_t)tmp);
+ break;
+ case 4:
+ tmp = get_GPRM(command->registers, reg) - data;
+ if(tmp < 0) tmp = 0;
+ set_GPRM(command->registers, reg, (uint16_t)tmp);
+ break;
+ case 5:
+ tmp = get_GPRM(command->registers, reg) * data;
+ if(tmp >= shortmax) tmp = shortmax;
+ set_GPRM(command->registers, reg, (uint16_t)tmp);
+ break;
+ case 6:
+ if (data != 0) {
+ set_GPRM(command->registers, reg, (get_GPRM(command->registers, reg) / data) );
+ } else {
+ set_GPRM(command->registers, reg, 0); /* Avoid that divide by zero! */
+ }
+ break;
+ case 7:
+ set_GPRM(command->registers, reg, (get_GPRM(command->registers, reg) % data) );
+ break;
+ case 8: /* SPECIAL CASE - RND! */
+ set_GPRM(command->registers, reg, ((uint16_t) ((float) data * rand()/(RAND_MAX+1.0))) );
+ break;
+ case 9:
+ set_GPRM(command->registers, reg, (get_GPRM(command->registers, reg) & data) );
+ break;
+ case 10:
+ set_GPRM(command->registers, reg, (get_GPRM(command->registers, reg) | data) );
+ break;
+ case 11:
+ set_GPRM(command->registers, reg, (get_GPRM(command->registers, reg) ^ data) );
+ break;
+ }
+}
+
+/* Evaluate set instruction, combined with either Link or Compare. */
+static void eval_set_version_1(command_t* command, int32_t cond) {
+ uint8_t op = vm_getbits(command, 4, 4);
+ uint8_t reg = vm_getbits(command, 28, 4); /* FIXME: This is different from vmcmd.c!!! */
+ uint8_t reg2 = vm_getbits(command, 44, 4);
+ uint16_t data = eval_reg_or_data(command, vm_getbits(command, 3, 1), 4);
+
+ if(cond) {
+ eval_set_op(command, op, reg, reg2, data);
+ }
+}
+
+
+/* Evaluate set instruction, combined with both Link and Compare. */
+static void eval_set_version_2(command_t* command, int32_t cond) {
+ uint8_t op = vm_getbits(command, 4, 4);
+ uint8_t reg = vm_getbits(command, 12, 4);
+ uint8_t reg2 = vm_getbits(command, 28, 4); /* FIXME: This is different from vmcmd.c!!! */
+ uint16_t data = eval_reg_or_data(command, vm_getbits(command, 3, 1), 2);
+
+ if(cond) {
+ eval_set_op(command, op, reg, reg2, data);
+ }
+}
+
+
+/* Evaluate a command
+ returns row number of goto, 0 if no goto, -1 if link.
+ Link command in return_values */
+static int32_t eval_command(uint8_t *bytes, registers_t* registers, link_t *return_values) {
+ int32_t cond, res = 0;
+ command_t command;
+ command.instruction =( (uint64_t) bytes[0] << 56 ) |
+ ( (uint64_t) bytes[1] << 48 ) |
+ ( (uint64_t) bytes[2] << 40 ) |
+ ( (uint64_t) bytes[3] << 32 ) |
+ ( (uint64_t) bytes[4] << 24 ) |
+ ( (uint64_t) bytes[5] << 16 ) |
+ ( (uint64_t) bytes[6] << 8 ) |
+ (uint64_t) bytes[7] ;
+ command.examined = 0;
+ command.registers = registers;
+ memset(return_values, 0, sizeof(link_t));
+
+ switch(vm_getbits(&command, 0, 3)) { /* three first old_bits */
+ case 0: /* Special instructions */
+ cond = eval_if_version_1(&command);
+ res = eval_special_instruction(&command, cond);
+ if(res == -1) {
+ fprintf(stderr, "Unknown Instruction!\n");
+ assert(0);
+ }
+ break;
+ case 1: /* Link/jump instructions */
+ if(vm_getbits(&command, 3, 1)) {
+ cond = eval_if_version_2(&command);
+ res = eval_jump_instruction(&command, cond, return_values);
+ } else {
+ cond = eval_if_version_1(&command);
+ res = eval_link_instruction(&command, cond, return_values);
+ }
+ if(res)
+ res = -1;
+ break;
+ case 2: /* System set instructions */
+ cond = eval_if_version_2(&command);
+ res = eval_system_set(&command, cond, return_values);
+ if(res)
+ res = -1;
+ break;
+ case 3: /* Set instructions, either Compare or Link may be used */
+ cond = eval_if_version_3(&command);
+ eval_set_version_1(&command, cond);
+ if(vm_getbits(&command, 12, 4)) {
+ res = eval_link_instruction(&command, cond, return_values);
+ }
+ if(res)
+ res = -1;
+ break;
+ case 4: /* Set, Compare -> Link Sub-Instruction */
+ eval_set_version_2(&command, /*True*/ 1);
+ cond = eval_if_version_4(&command);
+ res = eval_link_subins(&command, cond, return_values);
+ if(res)
+ res = -1;
+ break;
+ case 5: /* Compare -> (Set and Link Sub-Instruction) */
+ cond = eval_if_version_4(&command);
+ eval_set_version_2(&command, cond);
+ res = eval_link_subins(&command, cond, return_values);
+ if(res)
+ res = -1;
+ break;
+ case 6: /* Compare -> Set, allways Link Sub-Instruction */
+ cond = eval_if_version_4(&command);
+ eval_set_version_2(&command, cond);
+ res = eval_link_subins(&command, /*True*/ 1, return_values);
+ if(res)
+ res = -1;
+ break;
+ default: /* Unknown command */
+ fprintf(stderr, "WARNING: Unknown Command=%x\n", vm_getbits(&command, 0, 3));
+ assert(0);
+ }
+ /* Check if there are bits not yet examined */
+
+ if(command.instruction & ~ command.examined) {
+ fprintf(stderr, " libdvdnav: decoder.c: [WARNING, unknown bits:");
+ fprintf(stderr, " %08llx", (command.instruction & ~ command.examined) );
+ fprintf(stderr, "]");
+ }
+
+ return res;
+}
+
+/* Evaluate a set of commands in the given register set (which is
+ * modified */
+int32_t vmEval_CMD(vm_cmd_t commands[], int32_t num_commands,
+ registers_t *registers, link_t *return_values) {
+ int32_t i = 0;
+ int32_t total = 0;
+
+#ifdef TRACE
+ /* DEBUG */
+ fprintf(stderr, "libdvdnav: Registers before transaction\n");
+ vmPrint_registers( registers );
+ if(1) {
+ int32_t i;
+ fprintf(stderr, "libdvdnav: Full list of commands to execute\n");
+ for(i = 0; i < num_commands; i++)
+ vmPrint_CMD(i, &commands[i]);
+ fprintf(stderr, "--------------------------------------------\n");
+ } /* end DEBUG */
+ if (1) {
+ fprintf(stderr, "libdvdnav: Single stepping commands\n");
+ }
+#endif
+
+ while(i < num_commands && total < 100000) {
+ int32_t line;
+
+#ifdef TRACE
+ if(1) vmPrint_CMD(i, &commands[i]);
+#endif
+ line = eval_command(&commands[i].bytes[0], registers, return_values);
+
+ if (line < 0) { /* Link command */
+#ifdef TRACE
+ fprintf(stderr, "libdvdnav: Registers after transaction\n");
+ vmPrint_registers( registers );
+ fprintf(stderr, "eval: Doing Link/Jump/Call\n");
+#endif
+ return 1;
+ }
+
+ if (line > 0) /* Goto command */
+ i = line - 1;
+ else /* Just continue on the next line */
+ i++;
+
+ total++;
+ }
+
+ memset(return_values, 0, sizeof(link_t));
+#ifdef TRACE
+ fprintf(stderr, "libdvdnav: Registers after transaction\n");
+ vmPrint_registers( registers );
+#endif
+ return 0;
+}
+
+static char *linkcmd2str(link_cmd_t cmd) {
+ switch(cmd) {
+ case LinkNoLink:
+ return "LinkNoLink";
+ case LinkTopC:
+ return "LinkTopC";
+ case LinkNextC:
+ return "LinkNextC";
+ case LinkPrevC:
+ return "LinkPrevC";
+ case LinkTopPG:
+ return "LinkTopPG";
+ case LinkNextPG:
+ return "LinkNextPG";
+ case LinkPrevPG:
+ return "LinkPrevPG";
+ case LinkTopPGC:
+ return "LinkTopPGC";
+ case LinkNextPGC:
+ return "LinkNextPGC";
+ case LinkPrevPGC:
+ return "LinkPrevPGC";
+ case LinkGoUpPGC:
+ return "LinkGoUpPGC";
+ case LinkTailPGC:
+ return "LinkTailPGC";
+ case LinkRSM:
+ return "LinkRSM";
+ case LinkPGCN:
+ return "LinkPGCN";
+ case LinkPTTN:
+ return "LinkPTTN";
+ case LinkPGN:
+ return "LinkPGN";
+ case LinkCN:
+ return "LinkCN";
+ case Exit:
+ return "Exit";
+ case JumpTT:
+ return "JumpTT";
+ case JumpVTS_TT:
+ return "JumpVTS_TT";
+ case JumpVTS_PTT:
+ return "JumpVTS_PTT";
+ case JumpSS_FP:
+ return "JumpSS_FP";
+ case JumpSS_VMGM_MENU:
+ return "JumpSS_VMGM_MENU";
+ case JumpSS_VTSM:
+ return "JumpSS_VTSM";
+ case JumpSS_VMGM_PGC:
+ return "JumpSS_VMGM_PGC";
+ case CallSS_FP:
+ return "CallSS_FP";
+ case CallSS_VMGM_MENU:
+ return "CallSS_VMGM_MENU";
+ case CallSS_VTSM:
+ return "CallSS_VTSM";
+ case CallSS_VMGM_PGC:
+ return "CallSS_VMGM_PGC";
+ case PlayThis:
+ return "PlayThis";
+ }
+ return "*** (bug)";
+}
+
+void vmPrint_LINK(link_t value) {
+ char *cmd = linkcmd2str(value.command);
+
+ switch(value.command) {
+ case LinkNoLink:
+ case LinkTopC:
+ case LinkNextC:
+ case LinkPrevC:
+ case LinkTopPG:
+ case LinkNextPG:
+ case LinkPrevPG:
+ case LinkTopPGC:
+ case LinkNextPGC:
+ case LinkPrevPGC:
+ case LinkGoUpPGC:
+ case LinkTailPGC:
+ case LinkRSM:
+ fprintf(stderr, "%s (button %d)\n", cmd, value.data1);
+ break;
+ case LinkPGCN:
+ case JumpTT:
+ case JumpVTS_TT:
+ case JumpSS_VMGM_MENU: /* == 2 -> Title Menu */
+ case JumpSS_VMGM_PGC:
+ fprintf(stderr, "%s %d\n", cmd, value.data1);
+ break;
+ case LinkPTTN:
+ case LinkPGN:
+ case LinkCN:
+ fprintf(stderr, "%s %d (button %d)\n", cmd, value.data1, value.data2);
+ break;
+ case Exit:
+ case JumpSS_FP:
+ case PlayThis: /* Humm.. should we have this at all.. */
+ fprintf(stderr, "%s\n", cmd);
+ break;
+ case JumpVTS_PTT:
+ fprintf(stderr, "%s %d:%d\n", cmd, value.data1, value.data2);
+ break;
+ case JumpSS_VTSM:
+ fprintf(stderr, "%s vts %d title %d menu %d\n",
+ cmd, value.data1, value.data2, value.data3);
+ break;
+ case CallSS_FP:
+ fprintf(stderr, "%s resume cell %d\n", cmd, value.data1);
+ break;
+ case CallSS_VMGM_MENU: /* == 2 -> Title Menu */
+ case CallSS_VTSM:
+ fprintf(stderr, "%s %d resume cell %d\n", cmd, value.data1, value.data2);
+ break;
+ case CallSS_VMGM_PGC:
+ fprintf(stderr, "%s %d resume cell %d\n", cmd, value.data1, value.data2);
+ break;
+ }
+ }
+
+void vmPrint_registers( registers_t *registers ) {
+ int32_t i;
+ fprintf(stderr, " # ");
+ for(i = 0; i < 24; i++)
+ fprintf(stderr, " %2d |", i);
+ fprintf(stderr, "\nSRPMS: ");
+ for(i = 0; i < 24; i++)
+ fprintf(stderr, "%04x|", registers->SPRM[i]);
+ fprintf(stderr, "\nGRPMS: ");
+ for(i = 0; i < 16; i++)
+ fprintf(stderr, "%04x|", get_GPRM(registers, i) );
+ fprintf(stderr, "\nGmode: ");
+ for(i = 0; i < 16; i++)
+ fprintf(stderr, "%04x|", registers->GPRM_mode[i]);
+ fprintf(stderr, "\nGtime: ");
+ for(i = 0; i < 16; i++)
+ fprintf(stderr, "%04lx|", registers->GPRM_time[i].tv_sec & 0xffff);
+ fprintf(stderr, "\n");
+}
diff --git a/src/input/libdvdnav/decoder.h b/src/input/libdvdnav/decoder.h
new file mode 100644
index 000000000..e977ac90f
--- /dev/null
+++ b/src/input/libdvdnav/decoder.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2000, 2001 Martin Norbäck, Håkan Hjort
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav 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.
+ *
+ * libdvdnav 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
+ *
+ * $Id: decoder.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifndef DECODER_H_INCLUDED
+#define DECODER_H_INCLUDED
+
+#include <inttypes.h>
+#include <sys/time.h>
+
+#include <dvdread/ifo_types.h> /* vm_cmd_t */
+
+/* Uncomment for tracing */
+/* #define TRACE */
+
+typedef enum {
+ LinkNoLink = 0,
+
+ LinkTopC = 1,
+ LinkNextC = 2,
+ LinkPrevC = 3,
+
+ LinkTopPG = 5,
+ LinkNextPG = 6,
+ LinkPrevPG = 7,
+
+ LinkTopPGC = 9,
+ LinkNextPGC = 10,
+ LinkPrevPGC = 11,
+ LinkGoUpPGC = 12,
+ LinkTailPGC = 13,
+
+ LinkRSM = 16,
+
+ LinkPGCN,
+ LinkPTTN,
+ LinkPGN,
+ LinkCN,
+
+ Exit,
+
+ JumpTT, /* 22 */
+ JumpVTS_TT,
+ JumpVTS_PTT,
+
+ JumpSS_FP,
+ JumpSS_VMGM_MENU,
+ JumpSS_VTSM,
+ JumpSS_VMGM_PGC,
+
+ CallSS_FP, /* 29 */
+ CallSS_VMGM_MENU,
+ CallSS_VTSM,
+ CallSS_VMGM_PGC,
+
+ PlayThis
+} link_cmd_t;
+
+typedef struct {
+ link_cmd_t command;
+ uint16_t data1;
+ uint16_t data2;
+ uint16_t data3;
+} link_t;
+
+typedef struct {
+ uint16_t SPRM[24];
+ uint16_t GPRM[16];
+ uint8_t GPRM_mode[16]; /* Need to have some thing to indicate normal/counter mode for every GPRM */
+ struct timeval GPRM_time[16]; /* For counter mode */
+} registers_t;
+
+typedef struct
+{
+ uint64_t instruction;
+ uint64_t examined;
+ registers_t *registers;
+} command_t;
+
+int vmEval_CMD(vm_cmd_t commands[], int num_commands,
+ registers_t *registers, link_t *return_values);
+
+void vmPrint_LINK(link_t value);
+void vmPrint_registers( registers_t *registers );
+uint32_t vm_getbits(command_t* command, int start, int count);
+
+#endif /* DECODER_H_INCLUDED */
diff --git a/src/input/libdvdnav/dvd_types.h b/src/input/libdvdnav/dvd_types.h
new file mode 100644
index 000000000..d19bce84a
--- /dev/null
+++ b/src/input/libdvdnav/dvd_types.h
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2000, 2001 Björn Englund, Håkan Hjort
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is a modified
+ * file originally part of the Ogle DVD player project.
+ *
+ * libdvdnav 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.
+ *
+ * libdvdnav 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
+ *
+ * $Id: dvd_types.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifndef DVD_H_INCLUDED
+#define DVD_H_INCLUDED
+
+#include <inttypes.h>
+
+/**
+ * \file dvd_types.h
+ *
+ * Various useful structs and enums for DVDs.
+ */
+
+/**
+ * DVD Domain
+ */
+typedef enum {
+ DVD_DOMAIN_FirstPlay, /**< First Play Domain */
+ DVD_DOMAIN_VMG, /**< Video Manager Domain */
+ DVD_DOMAIN_VTSMenu, /**< Video Title Set Menu Domain */
+ DVD_DOMAIN_VTSTitle, /**< Video Title Set Domain */
+ DVD_DOMAIN_Stop /**< Stop Domain */
+} DVDDomain_t;
+
+/**
+ * DVD Menu
+ */
+typedef enum {
+ DVD_MENU_Title = 2, /**< TBD */
+ DVD_MENU_Root = 3, /**< TBD */
+ DVD_MENU_Subpicture = 4, /**< TBD */
+ DVD_MENU_Audio = 5, /**< TBD */
+ DVD_MENU_Angle = 6, /**< TBD */
+ DVD_MENU_Part = 7 /**< TBD */
+} DVDMenuID_t;
+
+/**
+ * User operations
+ */
+typedef enum {
+ UOP_FLAG_TitleOrTimePlay = 0x00000001,
+ UOP_FLAG_ChapterSearchOrPlay = 0x00000002,
+ UOP_FLAG_TitlePlay = 0x00000004,
+ UOP_FLAG_Stop = 0x00000008,
+ UOP_FLAG_GoUp = 0x00000010,
+ UOP_FLAG_TimeOrChapterSearch = 0x00000020,
+ UOP_FLAG_PrevOrTopPGSearch = 0x00000040,
+ UOP_FLAG_NextPGSearch = 0x00000080,
+ UOP_FLAG_ForwardScan = 0x00000100,
+ UOP_FLAG_BackwardScan = 0x00000200,
+ UOP_FLAG_TitleMenuCall = 0x00000400,
+ UOP_FLAG_RootMenuCall = 0x00000800,
+ UOP_FLAG_SubPicMenuCall = 0x00001000,
+ UOP_FLAG_AudioMenuCall = 0x00002000,
+ UOP_FLAG_AngleMenuCall = 0x00004000,
+ UOP_FLAG_ChapterMenuCall = 0x00008000,
+ UOP_FLAG_Resume = 0x00010000,
+ UOP_FLAG_ButtonSelectOrActivate = 0x00020000,
+ UOP_FLAG_StillOff = 0x00040000,
+ UOP_FLAG_PauseOn = 0x00080000,
+ UOP_FLAG_AudioStreamChange = 0x00100000,
+ UOP_FLAG_SubPicStreamChange = 0x00200000,
+ UOP_FLAG_AngleChange = 0x00400000,
+ UOP_FLAG_KaraokeAudioPresModeChange = 0x00800000,
+ UOP_FLAG_VideoPresModeChange = 0x01000000
+} DVDUOP_t;
+
+
+/**
+ * Parental Level
+ */
+typedef enum {
+ DVD_PARENTAL_LEVEL_1 = 1,
+ DVD_PARENTAL_LEVEL_2 = 2,
+ DVD_PARENTAL_LEVEL_3 = 3,
+ DVD_PARENTAL_LEVEL_4 = 4,
+ DVD_PARENTAL_LEVEL_5 = 5,
+ DVD_PARENTAL_LEVEL_6 = 6,
+ DVD_PARENTAL_LEVEL_7 = 7,
+ DVD_PARENTAL_LEVEL_8 = 8,
+ DVD_PARENTAL_LEVEL_None = 15
+} DVDParentalLevel_t;
+
+/**
+ * Language ID (ISO-639 language code)
+ */
+typedef uint16_t DVDLangID_t;
+
+/**
+ * Country ID (ISO-3166 country code)
+ */
+typedef uint16_t DVDCountryID_t;
+
+/**
+ * Register
+ */
+typedef uint16_t DVDRegister_t;
+
+typedef enum {
+ DVDFalse = 0,
+ DVDTrue = 1
+} DVDBool_t;
+
+typedef DVDRegister_t DVDGPRMArray_t[16];
+typedef DVDRegister_t DVDSPRMArray_t[24];
+
+typedef int DVDStream_t;
+
+/**
+ * Angle number (1-9 or default?)
+ */
+typedef int DVDAngle_t;
+
+typedef int DVDPTT_t;
+typedef int DVDTitle_t;
+typedef struct {
+ uint8_t Hours;
+ uint8_t Minutes;
+ uint8_t Seconds;
+ uint8_t Frames;
+} DVDTimecode_t;
+
+/**
+ * Subpicture stream number (0-31,62,63)
+ */
+typedef int DVDSubpictureStream_t;
+
+/**
+ * Audio stream number (0-7, 15(none))
+ */
+typedef int DVDAudioStream_t;
+
+
+/**
+ * The audio application mode
+ */
+typedef enum {
+ DVD_AUDIO_APP_MODE_None = 0, /**< app mode none */
+ DVD_AUDIO_APP_MODE_Karaoke = 1, /**< app mode karaoke */
+ DVD_AUDIO_APP_MODE_Surround = 2, /**< app mode surround */
+ DVD_AUDIO_APP_MODE_Other = 3 /**< app mode other */
+} DVDAudioAppMode_t;
+
+/**
+ * The audio format
+ */
+typedef enum {
+ DVD_AUDIO_FORMAT_AC3 = 0, /**< Dolby AC-3 */
+ DVD_AUDIO_FORMAT_MPEG1 = 1, /**< MPEG-1 */
+ DVD_AUDIO_FORMAT_MPEG1_DRC = 2, /**< MPEG-1 with dynamic range control */
+ DVD_AUDIO_FORMAT_MPEG2 = 3, /**< MPEG-2 */
+ DVD_AUDIO_FORMAT_MPEG2_DRC = 4, /**< MPEG-2 with dynamic range control */
+ DVD_AUDIO_FORMAT_LPCM = 5, /**< Linear Pulse Code Modulation */
+ DVD_AUDIO_FORMAT_DTS = 6, /**< Digital Theater Systems */
+ DVD_AUDIO_FORMAT_SDDS = 7, /**< Sony Dynamic Digital Sound */
+ DVD_AUDIO_FORMAT_Other = 8 /**< Other format*/
+} DVDAudioFormat_t;
+
+/**
+ * Audio language extension
+ */
+typedef enum {
+ DVD_AUDIO_LANG_EXT_NotSpecified = 0, /**< TBD */
+ DVD_AUDIO_LANG_EXT_NormalCaptions = 1, /**< TBD */
+ DVD_AUDIO_LANG_EXT_VisuallyImpaired = 2, /**< TBD */
+ DVD_AUDIO_LANG_EXT_DirectorsComments1 = 3, /**< TBD */
+ DVD_AUDIO_LANG_EXT_DirectorsComments2 = 4 /**< TBD */
+} DVDAudioLangExt_t;
+
+/**
+ * Subpicture language extension
+ */
+typedef enum {
+ DVD_SUBPICTURE_LANG_EXT_NotSpecified = 0,
+ DVD_SUBPICTURE_LANG_EXT_NormalCaptions = 1,
+ DVD_SUBPICTURE_LANG_EXT_BigCaptions = 2,
+ DVD_SUBPICTURE_LANG_EXT_ChildrensCaptions = 3,
+ DVD_SUBPICTURE_LANG_EXT_NormalCC = 5,
+ DVD_SUBPICTURE_LANG_EXT_BigCC = 6,
+ DVD_SUBPICTURE_LANG_EXT_ChildrensCC = 7,
+ DVD_SUBPICTURE_LANG_EXT_Forced = 9,
+ DVD_SUBPICTURE_LANG_EXT_NormalDirectorsComments = 13,
+ DVD_SUBPICTURE_LANG_EXT_BigDirectorsComments = 14,
+ DVD_SUBPICTURE_LANG_EXT_ChildrensDirectorsComments = 15,
+} DVDSubpictureLangExt_t;
+
+/**
+ * Karaoke Downmix mode
+ */
+typedef enum {
+ DVD_KARAOKE_DOWNMIX_0to0 = 0x0001,
+ DVD_KARAOKE_DOWNMIX_1to0 = 0x0002,
+ DVD_KARAOKE_DOWNMIX_2to0 = 0x0004,
+ DVD_KARAOKE_DOWNMIX_3to0 = 0x0008,
+ DVD_KARAOKE_DOWNMIX_4to0 = 0x0010,
+ DVD_KARAOKE_DOWNMIX_Lto0 = 0x0020,
+ DVD_KARAOKE_DOWNMIX_Rto0 = 0x0040,
+ DVD_KARAOKE_DOWNMIX_0to1 = 0x0100,
+ DVD_KARAOKE_DOWNMIX_1to1 = 0x0200,
+ DVD_KARAOKE_DOWNMIX_2to1 = 0x0400,
+ DVD_KARAOKE_DOWNMIX_3to1 = 0x0800,
+ DVD_KARAOKE_DOWNMIX_4to1 = 0x1000,
+ DVD_KARAOKE_DOWNMIX_Lto1 = 0x2000,
+ DVD_KARAOKE_DOWNMIX_Rto1 = 0x4000
+} DVDKaraokeDownmix_t;
+
+typedef int DVDKaraokeDownmixMask_t;
+
+typedef enum {
+ DVD_DISPLAY_MODE_ContentDefault = 0,
+ DVD_DISPLAY_MODE_16x9 = 1,
+ DVD_DISPLAY_MODE_4x3PanScan = 2,
+ DVD_DISPLAY_MODE_4x3Letterboxed = 3
+} DVDDisplayMode_t;
+
+typedef int DVDAudioSampleFreq_t; /**< TBD */
+typedef int DVDAudioSampleQuant_t; /**< TBD */
+typedef int DVDChannelNumber_t; /**< TBD */
+
+
+typedef struct {
+ DVDAudioAppMode_t AppMode;
+ DVDAudioFormat_t AudioFormat;
+ DVDLangID_t Language;
+ DVDAudioLangExt_t LanguageExtension;
+ DVDBool_t HasMultichannelInfo;
+ DVDAudioSampleFreq_t SampleFrequency;
+ DVDAudioSampleQuant_t SampleQuantization;
+ DVDChannelNumber_t NumberOfChannels;
+} DVDAudioAttributes_t;
+
+typedef enum {
+ DVD_SUBPICTURE_TYPE_NotSpecified = 0,
+ DVD_SUBPICTURE_TYPE_Language = 1,
+ DVD_SUBPICTURE_TYPE_Other = 2
+} DVDSubpictureType_t;
+
+typedef enum {
+ DVD_SUBPICTURE_CODING_RunLength = 0,
+ DVD_SUBPICTURE_CODING_Extended = 1,
+ DVD_SUBPICTURE_CODING_Other = 2
+} DVDSubpictureCoding_t;
+
+typedef struct {
+ DVDSubpictureType_t Type;
+ DVDSubpictureCoding_t CodingMode;
+ DVDLangID_t Language;
+ DVDSubpictureLangExt_t LanguageExtension;
+} DVDSubpictureAttributes_t;
+
+typedef int DVDVideoCompression_t; /**< TBD */
+
+typedef struct {
+ DVDBool_t PanscanPermitted;
+ DVDBool_t LetterboxPermitted;
+ int AspectX;
+ int AspectY;
+ int FrameRate;
+ int FrameHeight;
+ DVDVideoCompression_t Compression;
+ DVDBool_t Line21Field1InGop;
+ DVDBool_t Line21Field2InGop;
+ int more_to_come;
+} DVDVideoAttributes_t;
+
+/**
+ * Atructure containing info on highlight areas.
+ */
+typedef struct {
+ uint32_t palette; /*!< The CLUT entries for the highlight palette
+ (4-bits per entry -> 4 entries) */
+ uint16_t sx,sy,ex,ey; /*!< The start/end x,y positions */
+ uint32_t pts; /*!< Highlight PTS to match with SPU */
+ uint32_t buttonN; /*!< Button number for the SPU decoder. */
+} dvdnav_highlight_area_t;
+
+
+#endif /* DVD_H_INCLUDED */
diff --git a/src/input/libdvdnav/dvdnav.c b/src/input/libdvdnav/dvdnav.c
new file mode 100644
index 000000000..63282db69
--- /dev/null
+++ b/src/input/libdvdnav/dvdnav.c
@@ -0,0 +1,1136 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library.
+ *
+ * libdvdnav 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.
+ *
+ * libdvdnav 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
+ *
+ * $Id: dvdnav.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/*
+#define LOG_DEBUG
+*/
+
+#include <pthread.h>
+#include <dvdnav.h>
+#include "dvdnav_internal.h"
+#include "read_cache.h"
+
+#include <dvdread/nav_read.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * NOTE:
+ * All NLCK_*() function are not mutex locked, this made them reusable in
+ * a locked context. Take care.
+ *
+ */
+
+/* Current domain (backend to dvdnav_is_domain_() funcs) */
+static int8_t NLCK_dvdnav_is_domain(dvdnav_t *this, domain_t domain) {
+ dvd_state_t *state;
+
+ if((!this) || (!this->started) || (!this->vm))
+ return -1;
+
+ state = &(this->vm->state);
+
+ if(!state)
+ return -1;
+
+ return (state->domain == domain) ? 1 : 0;
+}
+
+static int8_t _dvdnav_is_domain(dvdnav_t *this, domain_t domain) {
+ int8_t retval;
+
+ pthread_mutex_lock(&this->vm_lock);
+ retval = NLCK_dvdnav_is_domain(this, domain);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return retval;
+}
+
+static int8_t NCLK_dvdnav_get_audio_logical_stream(dvdnav_t *this, uint8_t audio_num) {
+ dvd_state_t *state;
+ int8_t logical = -1;
+
+ if(!NLCK_dvdnav_is_domain(this, VTS_DOMAIN))
+ audio_num = 0;
+
+ state = &(this->vm->state);
+
+ if(audio_num < 8) {
+ if(state->pgc->audio_control[audio_num] & (1 << 15)) {
+ logical = (state->pgc->audio_control[audio_num] >> 8) & 0x07;
+ }
+ }
+
+ return logical;
+}
+
+static int8_t NCLK_dvdnav_get_spu_logical_stream(dvdnav_t *this, uint8_t subp_num) {
+ dvd_state_t *state;
+ ifo_handle_t *vtsi;
+
+ if(!this)
+ return -1;
+
+ state = &(this->vm->state);
+ vtsi = this->vm->vtsi;
+
+ if(subp_num >= vtsi->vtsi_mat->nr_of_vts_subp_streams)
+ return -1;
+
+ return vm_get_subp_stream(this->vm, subp_num, 0);
+}
+
+static int8_t NLCK_dvdnav_get_active_spu_stream(dvdnav_t *this) {
+ dvd_state_t *state;
+ int8_t subp_num;
+ int stream_num;
+
+ state = &(this->vm->state);
+ subp_num = state->SPST_REG & ~0x40;
+ stream_num = NCLK_dvdnav_get_spu_logical_stream(this, subp_num);
+
+ if(stream_num == -1)
+ for(subp_num = 0; subp_num < 32; subp_num++)
+ if(state->pgc->subp_control[subp_num] & (1 << 31)) {
+ stream_num = NCLK_dvdnav_get_spu_logical_stream(this, subp_num);
+ break;
+ }
+
+ return stream_num;
+}
+
+uint8_t dvdnav_get_video_aspect(dvdnav_t *this) {
+ uint8_t retval;
+
+ pthread_mutex_lock(&this->vm_lock);
+ retval = (uint8_t) vm_get_video_aspect(this->vm);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return retval;
+}
+
+uint8_t dvdnav_get_video_scale_permission(dvdnav_t *this) {
+ uint8_t retval;
+
+ pthread_mutex_lock(&this->vm_lock);
+ retval = (uint8_t) vm_get_video_scale_permission(this->vm);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return retval;
+}
+
+dvdnav_status_t dvdnav_clear(dvdnav_t * this) {
+ if (!this) {
+ printerr("Passed a NULL pointer");
+ return S_ERR;
+ }
+ /* clear everything except path, file, vm, mutex, readahead */
+
+ /* path */
+ if (this->file) DVDCloseFile(this->file);
+ this->file = NULL;
+ this->open_vtsN = -1;
+ this->open_domain = -1;
+
+ memset(&this->pci,0,sizeof(this->pci));
+ memset(&this->dsi,0,sizeof(this->dsi));
+
+ /* Set initial values of flags */
+ this->position_current.still = 0;
+ this->skip_still = 0;
+ this->stop = 0;
+ this->spu_clut_changed = 0;
+ this->started=0;
+ /* this->use_read_ahead */
+
+ dvdnav_read_cache_clear(this->cache);
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_open(dvdnav_t** dest, char *path) {
+ dvdnav_t *this;
+
+ /* Create a new structure */
+ (*dest) = NULL;
+ this = (dvdnav_t*)malloc(sizeof(dvdnav_t));
+ if(!this)
+ return S_ERR;
+ memset(this, 0, (sizeof(dvdnav_t) ) ); /* Make sure this structure is clean */
+
+ pthread_mutex_init(&this->vm_lock, NULL);
+ /* Initialise the error string */
+ printerr("");
+
+ /* Initialise the VM */
+ this->vm = vm_new_vm();
+ if(!this->vm) {
+ printerr("Error initialising the DVD VM");
+ return S_ERR;
+ }
+ if(vm_reset(this->vm, path) == -1) {
+ printerr("Error starting the VM / opening the DVD device");
+ return S_ERR;
+ }
+
+ /* Set the path. FIXME: Is a deep copy 'right' */
+ strncpy(this->path, path, MAX_PATH_LEN);
+
+ dvdnav_clear(this);
+
+ /* Pre-open and close a file so that the CSS-keys are cached. */
+ this->file = DVDOpenFile(vm_get_dvd_reader(this->vm), 0, DVD_READ_MENU_VOBS);
+ if (this->file) DVDCloseFile(this->file);
+ this->file = NULL;
+
+ //if(!this->started) {
+ // /* Start the VM */
+ // vm_start(this->vm);
+ // this->started = 1;
+ //}
+
+ /* Start the read-ahead cache. */
+ this->cache = dvdnav_read_cache_new(this);
+
+ (*dest) = this;
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_close(dvdnav_t *this) {
+ if(!this) {
+ printerr("Passed a NULL pointer");
+ return S_ERR;
+ }
+#ifdef LOG_DEBUG
+ fprintf(stderr,"dvdnav:close:called\n");
+#endif
+
+ if (this->file) {
+ DVDCloseFile(this->file);
+#ifdef LOG_DEBUG
+ fprintf(stderr,"dvdnav:close:file closing\n");
+#endif
+ this->file = NULL;
+ }
+
+ /* Free the VM */
+ if(this->vm) {
+ vm_free_vm(this->vm);
+ }
+ if (this->file) {
+ DVDCloseFile(this->file);
+#ifdef LOG_DEBUG
+ fprintf(stderr,"dvdnav:close2:file closing\n");
+#endif
+ this->file = NULL;
+ }
+ pthread_mutex_destroy(&this->vm_lock);
+
+ /* We leave the final freeing of the entire structure to the cache,
+ * because we don't know, if there are still buffers out in the wild,
+ * that must return first. */
+ if(this->cache) {
+ dvdnav_read_cache_free(this->cache);
+ } else free(this);
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_reset(dvdnav_t *this) {
+ dvdnav_status_t result;
+
+#ifdef LOG_DEBUG
+ printf("dvdnav:reset:called\n");
+#endif
+ if(!this) {
+ printerr("Passed a NULL pointer");
+ return S_ERR;
+ }
+#ifdef LOG_DEBUG
+ printf("getting lock\n");
+#endif
+ pthread_mutex_lock(&this->vm_lock);
+#ifdef LOG_DEBUG
+ printf("reseting vm\n");
+#endif
+ if(vm_reset(this->vm, NULL) == -1) {
+ printerr("Error restarting the VM");
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_ERR;
+ }
+#ifdef LOG_DEBUG
+ printf("clearing dvdnav\n");
+#endif
+ result=dvdnav_clear(this);
+#ifdef LOG_DEBUG
+ printf("starting vm\n");
+#endif
+// if(!this->started) {
+// /* Start the VM */
+// vm_start(this->vm);
+// this->started = 1;
+// }
+#ifdef LOG_DEBUG
+ printf("unlocking\n");
+#endif
+ pthread_mutex_unlock(&this->vm_lock);
+ return result;
+}
+
+dvdnav_status_t dvdnav_path(dvdnav_t *this, char** path) {
+ if(!this || !path || !(*path)) {
+ return S_ERR;
+ }
+
+ /* FIXME: Is shallow copy 'right'? */
+ (*path) = this->path;
+
+ return S_OK;
+}
+
+char* dvdnav_err_to_string(dvdnav_t *this) {
+ if(!this) {
+ /* Shold this be "passed a NULL pointer?" */
+ return "Hey! You gave me a NULL pointer you naughty person!";
+ }
+
+ return this->err_str;
+}
+
+/**
+ * Returns 1 if block contains NAV packet, 0 otherwise.
+ * Precesses said NAV packet if present.
+ *
+ * Most of the code in here is copied from xine's MPEG demuxer
+ * so any bugs which are found in that should be corrected here also.
+ */
+int dvdnav_decode_packet(dvdnav_t *this, uint8_t *p, dsi_t* nav_dsi, pci_t* nav_pci) {
+ int bMpeg1=0;
+ uint32_t nHeaderLen;
+ uint32_t nPacketLen;
+ uint32_t nStreamID;
+/* uint8_t *p_start=p; */
+
+
+ if (p==NULL) {
+ fprintf(stderr,"Passed a NULL pointer.\n");
+ return 0;
+ }
+
+ /* dprint("Checking packet...\n"); */
+
+ if (p[3] == 0xBA) { /* program stream pack header */
+
+ int nStuffingBytes;
+
+ /* xprintf (VERBOSE|DEMUX, "program stream pack header\n"); */
+
+ bMpeg1 = (p[4] & 0x40) == 0;
+
+ if (bMpeg1) {
+ p += 12;
+ } else { /* mpeg2 */
+ nStuffingBytes = p[0xD] & 0x07;
+ p += 14 + nStuffingBytes;
+ }
+ }
+
+
+ if (p[3] == 0xbb) { /* program stream system header */
+ int nHeaderLen;
+
+ nHeaderLen = (p[4] << 8) | p[5];
+ p += 6 + nHeaderLen;
+ }
+
+ /* we should now have a PES packet here */
+
+ if (p[0] || p[1] || (p[2] != 1)) {
+ fprintf(stderr,"demux error! %02x %02x %02x (should be 0x000001) \n",p[0],p[1],p[2]);
+ return 0;
+ }
+
+ nPacketLen = p[4] << 8 | p[5];
+ nStreamID = p[3];
+
+ nHeaderLen = 6;
+ p += nHeaderLen;
+
+ if (nStreamID == 0xbf) { /* Private stream 2 */
+/*
+ * int i;
+ * printf("dvdnav:nav packet=%u\n",p-p_start-6);
+ * for(i=0;i<80;i++) {
+ * printf("%02x ",p[i-6]);
+ * }
+ * printf("\n");
+ */
+ if(p[0] == 0x00) {
+ navRead_PCI(nav_pci, p+1);
+ }
+
+ p += nPacketLen;
+
+ /* We should now have a DSI packet. */
+ if(p[6] == 0x01) {
+ nPacketLen = p[4] << 8 | p[5];
+ p += 6;
+ /* dprint("NAV DSI packet\n"); */
+ navRead_DSI(nav_dsi, p+1);
+
+ }
+ return 1;
+ }
+ return 0;
+}
+
+/* DSI is used for most angle stuff.
+ * PCI is used for only non-seemless angle stuff
+ */
+int dvdnav_get_vobu(dvdnav_t *self, dsi_t* nav_dsi, pci_t* nav_pci, dvdnav_vobu_t* vobu) {
+ uint32_t next;
+ int angle, num_angle;
+
+ vobu->vobu_start = nav_dsi->dsi_gi.nv_pck_lbn; /* Absolute offset from start of disk */
+ vobu->vobu_length = nav_dsi->dsi_gi.vobu_ea; /* Relative offset from vobu_start */
+
+ /*
+ * If we're not at the end of this cell, we can determine the next
+ * VOBU to display using the VOBU_SRI information section of the
+ * DSI. Using this value correctly follows the current angle,
+ * avoiding the doubled scenes in The Matrix, and makes our life
+ * really happy.
+ *
+ * vobu_next is an offset value, 0x3fffffff = SRI_END_OF_CELL
+ * DVDs are about 6 Gigs, which is only up to 0x300000 blocks
+ * Should really assert if bit 31 != 1
+ */
+
+ /* Relative offset from vobu_start */
+ vobu->vobu_next = ( nav_dsi->vobu_sri.next_vobu & 0x3fffffff );
+
+ /* Old code -- may still be sueful one day
+ if(nav_dsi->vobu_sri.next_vobu != SRI_END_OF_CELL ) {
+ vobu->vobu_next = ( nav_dsi->vobu_sri.next_vobu & 0x3fffffff );
+ } else {
+ vobu->vobu_next = vobu->vobu_length;
+ } */
+
+ dvdnav_get_angle_info(self, &angle, &num_angle);
+#if 0
+ /* FIMXE: The angle reset doesn't work for some reason for the moment */
+
+ if((num_angle < angle) && (angle != 1)) {
+ printf("OOOOOOO angle ends!\n");
+
+ /* This is to switch back to angle one when we
+ * finish with angles. */
+ dvdnav_angle_change(self, 1);
+ }
+#endif
+
+ if(num_angle != 0) {
+ next = nav_pci->nsml_agli.nsml_agl_dsta[angle-1];
+
+ if(next != 0) {
+ if((next & 0x3fffffff) != 0) {
+ if(next & 0x80000000) {
+ vobu->vobu_next = - (int32_t)(next & 0x3fffffff);
+ } else {
+ vobu->vobu_next = + (int32_t)(next & 0x3fffffff);
+ }
+ }
+
+ } else if( nav_dsi->sml_agli.data[angle-1].address != 0 ) {
+ next = nav_dsi->sml_agli.data[angle-1].address;
+ vobu->vobu_length = nav_dsi->sml_pbi.ilvu_ea;
+
+ if((next & 0x80000000) && (next != 0x7fffffff)) {
+ vobu->vobu_next = - (int32_t)(next & 0x3fffffff);
+ } else {
+ vobu->vobu_next = + (int32_t)(next & 0x3fffffff);
+ }
+ }
+ }
+
+ return 1;
+}
+
+/* This is the main get_next_block function which actually gets the media stream video and audio etc.
+ * The use of this function is optional, with the application programmer
+ * free to implement their own version of this function
+ * FIXME: Make the function calls from here public API calls.
+ */
+
+dvdnav_status_t dvdnav_get_next_block(dvdnav_t *this, unsigned char *buf,
+ int *event, int *len) {
+ unsigned char *block;
+ dvdnav_status_t status;
+
+ block = buf;
+ status = dvdnav_get_next_cache_block(this, &block, event, len);
+ if (block != buf) {
+ /* we received a block from the cache, copy it, so we can give it back */
+ memcpy(buf, block, DVD_VIDEO_LB_LEN);
+ dvdnav_free_cache_block(this, block);
+ }
+ return status;
+}
+
+dvdnav_status_t dvdnav_get_next_cache_block(dvdnav_t *this, unsigned char **buf,
+ int *event, int *len) {
+ dvd_state_t *state;
+ int result;
+ if(!this || !event || !len || !buf || !*buf) {
+ printerr("Passed a NULL pointer");
+ return S_ERR;
+ }
+ pthread_mutex_lock(&this->vm_lock);
+
+ if(!this->started) {
+ /* Start the VM */
+ vm_start(this->vm);
+ this->started = 1;
+ }
+
+ state = &(this->vm->state);
+ (*event) = DVDNAV_NOP;
+ (*len) = 0;
+
+ /* Check the STOP flag */
+ if(this->stop) {
+ (*event) = DVDNAV_STOP;
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+
+ /* Check the STILLFRAME flag */
+ /* FIXME: Still cell, not still frame */
+ if(this->position_current.still != 0) {
+ dvdnav_still_event_t still_event;
+
+ still_event.length = this->position_current.still;
+
+ (*event) = DVDNAV_STILL_FRAME;
+ (*len) = sizeof(dvdnav_still_event_t);
+ memcpy(*buf, &(still_event), sizeof(dvdnav_still_event_t));
+
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+
+ vm_position_get(this->vm,&this->position_next);
+ /**********
+ fprintf(stderr, "POS-NEXT ");
+ vm_position_print(this->vm, &this->position_next);
+ fprintf(stderr, "POS-CUR ");
+ vm_position_print(this->vm, &this->position_current);
+ **********/
+
+ if(this->position_current.hop_channel != this->position_next.hop_channel) {
+ this->position_current.hop_channel = this->position_next.hop_channel;
+ (*event) = DVDNAV_HOP_CHANNEL;
+ (*len) = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+
+
+
+ if(this->spu_clut_changed) {
+ (*event) = DVDNAV_SPU_CLUT_CHANGE;
+#ifdef LOG_DEBUG
+ fprintf(stderr,"libdvdnav:SPU_CLUT_CHANGE\n");
+#endif
+ (*len) = sizeof(dvdnav_still_event_t);
+ memcpy(*buf, &(state->pgc->palette), 16 * sizeof(uint32_t));
+ this->spu_clut_changed = 0;
+#ifdef LOG_DEBUG
+ fprintf(stderr,"libdvdnav:SPU_CLUT_CHANGE returning S_OK\n");
+#endif
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+
+ if(this->position_current.spu_channel != this->position_next.spu_channel) {
+ dvdnav_spu_stream_change_event_t stream_change;
+ (*event) = DVDNAV_SPU_STREAM_CHANGE;
+#ifdef LOG_DEBUG
+ fprintf(stderr,"libdvdnav:SPU_STREAM_CHANGE\n");
+#endif
+ (*len) = sizeof(dvdnav_spu_stream_change_event_t);
+ stream_change.physical_wide = vm_get_subp_active_stream(this->vm, 0);
+ stream_change.physical_letterbox = vm_get_subp_active_stream(this->vm, 1);
+ stream_change.physical_pan_scan = vm_get_subp_active_stream(this->vm, 2);
+ memcpy(*buf, &(stream_change), sizeof( dvdnav_spu_stream_change_event_t));
+ this->position_current.spu_channel = this->position_next.spu_channel;
+#ifdef LOG_DEBUG
+ fprintf(stderr,"libdvdnav:SPU_STREAM_CHANGE stream_id_wide=%d\n",stream_change.physical_wide);
+ fprintf(stderr,"libdvdnav:SPU_STREAM_CHANGE stream_id_letterbox=%d\n",stream_change.physical_letterbox);
+ fprintf(stderr,"libdvdnav:SPU_STREAM_CHANGE stream_id_pan_scan=%d\n",stream_change.physical_pan_scan);
+#endif
+ pthread_mutex_unlock(&this->vm_lock);
+ if (stream_change.physical_wide != -1 &&
+ stream_change.physical_letterbox != -1 &&
+ stream_change.physical_pan_scan != -1) {
+#ifdef LOG_DEBUG
+ fprintf(stderr,"libdvdnav:SPU_STREAM_CHANGE returning S_OK\n");
+#endif
+ return S_OK;
+ }
+ }
+
+ if(this->position_current.audio_channel != this->position_next.audio_channel) {
+ dvdnav_audio_stream_change_event_t stream_change;
+ (*event) = DVDNAV_AUDIO_STREAM_CHANGE;
+#ifdef LOG_DEBUG
+ fprintf(stderr,"libdvdnav:AUDIO_STREAM_CHANGE\n");
+#endif
+ (*len) = sizeof(dvdnav_audio_stream_change_event_t);
+ stream_change.physical= vm_get_audio_active_stream( this->vm );
+ memcpy(*buf, &(stream_change), sizeof( dvdnav_audio_stream_change_event_t));
+ this->position_current.audio_channel = this->position_next.audio_channel;
+#ifdef LOG_DEBUG
+ fprintf(stderr,"libdvdnav:AUDIO_STREAM_CHANGE stream_id=%d returning S_OK\n",stream_change.physical);
+#endif
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+
+ /* Check the HIGHLIGHT flag */
+ /* FIXME: Use BUTTON instead of HIGHLIGHT. */
+ if(this->position_current.button != this->position_next.button) {
+ dvdnav_highlight_event_t hevent;
+
+ hevent.display = 1;
+ hevent.buttonN = this->position_next.button;
+
+ this->position_current.button = this->position_next.button;
+
+ (*event) = DVDNAV_HIGHLIGHT;
+ (*len) = sizeof(hevent);
+ memcpy(*buf, &(hevent), sizeof(hevent));
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+
+ /* Check to see if we need to change the currently opened VOB */
+ if((this->position_current.vts != this->position_next.vts) ||
+ (this->position_current.domain != this->position_next.domain)) {
+ dvd_read_domain_t domain;
+ int vtsN;
+ dvdnav_vts_change_event_t vts_event;
+
+ if(this->file) {
+ dvdnav_read_cache_clear(this->cache);
+ DVDCloseFile(this->file);
+ this->file = NULL;
+ }
+
+ vts_event.old_vtsN = this->open_vtsN;
+ vts_event.old_domain = this->open_domain;
+
+ /* Use the current DOMAIN to find whether to open menu or title VOBs */
+ switch(this->position_next.domain) {
+ case FP_DOMAIN:
+ case VMGM_DOMAIN:
+ domain = DVD_READ_MENU_VOBS;
+ vtsN = 0;
+ break;
+ case VTSM_DOMAIN:
+ domain = DVD_READ_MENU_VOBS;
+ vtsN = this->position_next.vts;
+ break;
+ case VTS_DOMAIN:
+ domain = DVD_READ_TITLE_VOBS;
+ vtsN = this->position_next.vts;
+ break;
+ default:
+ printerr("Unknown domain when changing VTS.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_ERR;
+ }
+
+ this->position_current.vts = this->position_next.vts;
+ this->position_current.domain = this->position_next.domain;
+ dvdnav_read_cache_clear(this->cache);
+ this->file = DVDOpenFile(vm_get_dvd_reader(this->vm), vtsN, domain);
+ vts_event.new_vtsN = this->position_next.vts;
+ vts_event.new_domain = this->position_next.domain;
+
+ /* If couldn't open the file for some reason, moan */
+ if(this->file == NULL) {
+ printerrf("Error opening vtsN=%i, domain=%i.", vtsN, domain);
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_ERR;
+ }
+
+ /* File opened successfully so return a VTS change event */
+ (*event) = DVDNAV_VTS_CHANGE;
+ memcpy(*buf, &(vts_event), sizeof(vts_event));
+ (*len) = sizeof(vts_event);
+
+ /* On a VTS change, we want to disable any highlights which
+ * may have been shown (FIXME: is this valid?) */
+ this->spu_clut_changed = 1;
+ this->position_current.cell = -1; /* Force an update */
+ this->position_current.spu_channel = -1; /* Force an update */
+ this->position_current.audio_channel = -1; /* Force an update */;
+
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+ /* FIXME: Don't really need "cell", we only need vobu_start */
+ if( (this->position_current.cell != this->position_next.cell) ||
+ (this->position_current.cell_restart != this->position_next.cell_restart) ||
+ (this->position_current.vobu_start != this->position_next.vobu_start) ||
+ (this->position_current.vobu_next != this->position_next.vobu_next) ) {
+ this->position_current.cell = this->position_next.cell;
+ this->position_current.cell_restart = this->position_next.cell_restart;
+ /* vobu_start changes when PGC or PG changes. */
+ this->position_current.vobu_start = this->position_next.vobu_start;
+ this->position_current.vobu_next = this->position_next.vobu_next;
+ /* FIXME: Need to set vobu_start, vobu_next */
+ this->vobu.vobu_start = this->position_next.vobu_start;
+ /* vobu_next is use for mid cell resumes */
+ this->vobu.vobu_next = this->position_next.vobu_next;
+ this->vobu.vobu_length = 0;
+ this->vobu.blockN = this->vobu.vobu_length + 1;
+ /* Make blockN > vobu_lenght to do expected_nav */
+ (*event) = DVDNAV_CELL_CHANGE;
+ (*len) = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+
+
+ if (this->vobu.blockN > this->vobu.vobu_length) {
+ /* End of VOBU */
+
+ if(this->vobu.vobu_next == SRI_END_OF_CELL) {
+ /* End of Cell from NAV DSI info */
+#ifdef LOG_DEBUG
+ fprintf(stderr, "Still set to %x\n", this->position_next.still);
+#endif
+ this->position_current.still = this->position_next.still;
+
+ if( this->position_current.still == 0 || this->skip_still ) {
+ vm_get_next_cell(this->vm);
+ vm_position_get(this->vm,&this->position_next);
+ /* FIXME: Need to set vobu_start, vobu_next */
+ this->position_current.still = 0; /* still gets activated at end of cell */
+ this->skip_still = 0;
+ this->position_current.cell = this->position_next.cell;
+ this->position_current.vobu_start = this->position_next.vobu_start;
+ this->position_current.vobu_next = this->position_next.vobu_next;
+ this->vobu.vobu_start = this->position_next.vobu_start;
+ /* vobu_next is use for mid cell resumes */
+ this->vobu.vobu_next = this->position_next.vobu_next;
+ this->vobu.vobu_length = 0;
+ this->vobu.blockN = this->vobu.vobu_length + 1;
+ /* Make blockN > vobu_next to do expected_nav */
+ (*event) = DVDNAV_CELL_CHANGE;
+ (*len) = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ } else {
+ dvdnav_still_event_t still_event;
+ still_event.length = this->position_current.still;
+ (*event) = DVDNAV_STILL_FRAME;
+ (*len) = sizeof(dvdnav_still_event_t);
+ memcpy(*buf, &(still_event), sizeof(dvdnav_still_event_t));
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+
+ /* Only set still after whole VOBU has been output. */
+ /*
+ if(this->position_next.still != 0) {
+ this->position_current.still = this->position_next.still;
+ }
+ */
+
+ }
+ /* Perform the jump if necessary (this is always a
+ * VOBU boundary). */
+
+ //result = DVDReadBlocks(this->file, this->vobu.vobu_start + this->vobu.vobu_next, 1, buf);
+ result = dvdnav_read_cache_block(this->cache, this->vobu.vobu_start + this->vobu.vobu_next, 1, buf);
+
+ if(result <= 0) {
+ printerr("Error reading NAV packet.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_ERR;
+ }
+ /* Decode nav into pci and dsi. */
+ /* Then get next VOBU info. */
+ if(dvdnav_decode_packet(this, *buf, &this->dsi, &this->pci) == 0) {
+ printerr("Expected NAV packet but none found.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_ERR;
+ }
+ dvdnav_get_vobu(this, &this->dsi,&this->pci, &this->vobu);
+ this->vobu.blockN=1;
+ /* FIXME: We need to update the vm state->blockN with which VOBU we are in.
+ * This is so RSM resumes to the VOBU level and not just the CELL level.
+ * This should be implemented with a new Public API call.
+ */
+ /* We cache one past the end of the VOBU,
+ * in the hope it might catch the next NAV packet as well.
+ * This reduces the amount of read commands sent to the DVD device.
+ * A cache miss will only happen for 3 reasons.
+ * 1) Seeking
+ * 2) Menu change
+ * 3) The next VOBU does not immeadiately follow the current one. E.g. Multi Angles, ILVU.
+ */
+ dvdnav_pre_cache_blocks(this->cache, this->vobu.vobu_start+1, this->vobu.vobu_length+1);
+
+ /* Successfully got a NAV packet */
+ (*event) = DVDNAV_NAV_PACKET;
+ (*len) = 2048;
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+
+ /* If we've got here, it must just be a normal block. */
+ if(!this->file) {
+ printerr("Attempting to read without opening file");
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_ERR;
+ }
+
+ result = dvdnav_read_cache_block(this->cache, this->vobu.vobu_start + this->vobu.blockN, 1, buf);
+ if(result <= 0) {
+ printerr("Error reading from DVD.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_ERR;
+ }
+ this->vobu.blockN++;
+ (*len) = 2048;
+ (*event) = DVDNAV_BLOCK_OK;
+
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+}
+
+uint16_t dvdnav_audio_stream_to_lang(dvdnav_t *this, uint8_t stream) {
+ audio_attr_t attr;
+
+ if(!this)
+ return -1;
+
+ pthread_mutex_lock(&this->vm_lock);
+ attr = vm_get_audio_attr(this->vm, stream);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ if(attr.lang_type != 1)
+ return 0xffff;
+
+ return attr.lang_code;
+}
+
+int8_t dvdnav_get_audio_logical_stream(dvdnav_t *this, uint8_t audio_num) {
+ int8_t retval;
+
+ if(!this)
+ return -1;
+
+ pthread_mutex_lock(&this->vm_lock);
+ retval = NCLK_dvdnav_get_audio_logical_stream(this, audio_num);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return retval;
+}
+
+uint16_t dvdnav_spu_stream_to_lang(dvdnav_t *this, uint8_t stream) {
+ subp_attr_t attr;
+
+ if(!this)
+ return -1;
+
+ pthread_mutex_lock(&this->vm_lock);
+ attr = vm_get_subp_attr(this->vm, stream);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ if(attr.type != 1)
+ return 0xffff;
+
+ return attr.lang_code;
+}
+
+int8_t dvdnav_get_spu_logical_stream(dvdnav_t *this, uint8_t subp_num) {
+ int8_t retval;
+
+ if(!this)
+ return -1;
+
+ pthread_mutex_lock(&this->vm_lock);
+ retval = NCLK_dvdnav_get_spu_logical_stream(this, subp_num);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return retval;
+}
+
+int8_t dvdnav_get_active_spu_stream(dvdnav_t *this) {
+ int8_t retval;
+
+ if(!this)
+ return -1;
+
+ pthread_mutex_lock(&this->vm_lock);
+ retval = NLCK_dvdnav_get_active_spu_stream(this);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return retval;
+}
+
+/* First Play domain. (Menu) */
+int8_t dvdnav_is_domain_fp(dvdnav_t *this) {
+ return _dvdnav_is_domain(this, FP_DOMAIN);
+}
+/* Video management Menu domain. (Menu) */
+int8_t dvdnav_is_domain_vmgm(dvdnav_t *this) {
+ return _dvdnav_is_domain(this, VMGM_DOMAIN);
+}
+/* Video Title Menu domain (Menu) */
+int8_t dvdnav_is_domain_vtsm(dvdnav_t *this) {
+ return _dvdnav_is_domain(this, VTSM_DOMAIN);
+}
+/* Video Title domain (playing movie). */
+int8_t dvdnav_is_domain_vts(dvdnav_t *this) {
+ return _dvdnav_is_domain(this, VTS_DOMAIN);
+}
+
+/* Generally delegate angle information handling to
+ * VM */
+dvdnav_status_t dvdnav_angle_change(dvdnav_t *this, int angle) {
+ int num, current;
+
+ if(!this) {
+ return S_ERR;
+ }
+
+ if(dvdnav_get_angle_info(this, &current, &num) != S_OK) {
+ printerr("Error getting angle info");
+ return S_ERR;
+ }
+
+ /* Set angle SPRM if valid */
+ if((angle > 0) && (angle <= num)) {
+ this->vm->state.AGL_REG = angle;
+ } else {
+ printerr("Passed an invalid angle number");
+ return S_ERR;
+ }
+
+ return S_OK;
+}
+/* FIXME: change order of current_angle, number_of_angles */
+dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *this, int* current_angle,
+ int *number_of_angles) {
+ if(!this || !this->vm) {
+ return S_ERR;
+ }
+
+ if(!current_angle || !number_of_angles) {
+ printerr("Passed a NULL pointer");
+ return S_ERR;
+ }
+
+ vm_get_angle_info(this->vm, number_of_angles, current_angle);
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_get_cell_info(dvdnav_t *this, int* current_angle,
+ int *number_of_angles) {
+ if(!this || !this->vm) {
+ return S_ERR;
+ }
+ *current_angle=this->position_next.cell;
+ return S_OK;
+}
+
+pci_t* dvdnav_get_current_nav_pci(dvdnav_t *this) {
+ if(!this || !this->vm) return 0;
+ return &this->pci;
+}
+
+dsi_t* dvdnav_get_current_nav_dsi(dvdnav_t *this) {
+ if(!this || !this->vm) return 0;
+ return &this->dsi;
+}
+
+uint32_t dvdnav_get_next_still_flag(dvdnav_t *this) {
+ if(!this || !this->vm) {
+ return S_ERR;
+ }
+ return this->position_next.still;
+}
+
+/*
+ * $Log: dvdnav.c,v $
+ * Revision 1.1 2002/08/08 17:49:21 richwareham
+ * First stage of DVD plugin -> dvdnav conversion
+ *
+ * Revision 1.29 2002/07/25 14:51:40 richwareham
+ * Moved get_current_nav_pci into dvdnac.c, changed example to use it instead of 'home-rolled'
+ * check_packet.
+ *
+ * Revision 1.28 2002/07/25 14:42:33 richwareham
+ * Patch from aschultz@cs.uni-magdeburg.de to allow for still-frame 'peek-ahead'
+ *
+ * Revision 1.27 2002/07/12 15:46:44 mroi
+ * use new memcopy'less read ahead cache
+ *
+ * Revision 1.26 2002/07/06 16:24:54 mroi
+ * * fix debug messages
+ * * send spu stream change event only, when there are new streams
+ * (should fix problems with Terminator disk 2)
+ *
+ * Revision 1.25 2002/07/05 14:18:54 mroi
+ * report all spu types (widescreen, letterbox and pan&scan), not widescreen
+ * only and report the stream's scale permissions to detect pan&scan material
+ *
+ * Revision 1.24 2002/07/05 01:42:30 jcdutton
+ * Add more debug info for Menu language selection.
+ * Only do vm_start when we have to.
+ *
+ * Revision 1.23 2002/07/02 22:57:09 jcdutton
+ * Rename some of the functions in vm.c to help readability.
+ * Hopefully fix __FUNCTION__ problem. Use __func_ as recommended in C99.
+ * Fix bug where libdvdnav would not immeadiately replay the same cell due to menu buttons.
+ *
+ * Revision 1.22 2002/06/25 13:37:11 jcdutton
+ * Revert back to old read_cache method.
+ * Some new optimizations added to the old read_cache method, thus reducing the amount of calls to read blocks from the DVD device.
+ *
+ * Revision 1.21 2002/06/06 15:03:09 richwareham
+ * Biiiiiiig change to let doxygen generate some docs for the library. Note that I'm in no way sure that the autoconf stuff plays nice.
+ *
+ * Revision 1.20 2002/06/04 13:35:16 richwareham
+ * Removed more C++ style comments
+ *
+ * Revision 1.19 2002/05/30 23:15:14 richwareham
+ * First draft of removing HAVE_DVDREAD9
+ *
+ * Revision 1.18 2002/05/30 09:52:29 richwareham
+ * 'Objectified' the read-ahead cache in preparation to implement a 'proper' threaded cache a-la that recommended in the DVD Demystified book.
+ *
+ * Revision 1.17 2002/05/09 11:57:24 richwareham
+ * Angles now work (still a few wrinkles though -- e.g. angle does not reset to '1' when returning to menus)
+ *
+ * Revision 1.16 2002/04/24 21:15:25 jcdutton
+ * Quiet please!!!
+ *
+ * Revision 1.15 2002/04/24 00:47:46 jcdutton
+ * Some more cleanups.
+ * Improve button passing.
+ *
+ * Revision 1.14 2002/04/23 13:26:08 jcdutton
+ * Add some comments, FIXMEs.
+ * The main point being that dvdnav_get_next_block is almost in a state where it can be optional whether the application programmer uses it, or implements their own version of the function. That is been the main reason for the re-write of this function recently.
+ *
+ * Revision 1.13 2002/04/23 12:55:40 jcdutton
+ * Removed un-needed variables.
+ * General Clean up.
+ *
+ * Revision 1.12 2002/04/23 12:34:39 f1rmb
+ * Why rewrite vm function, use it instead (this remark is for me, of course ;-) ).
+ * Comment unused var, shut compiler warnings.
+ *
+ * Revision 1.11 2002/04/23 02:12:27 jcdutton
+ * Re-implemented seeking.
+ *
+ * Revision 1.10 2002/04/23 00:07:16 jcdutton
+ * Name stills work better.
+ *
+ * Revision 1.9 2002/04/22 22:00:48 jcdutton
+ * Start of rewrite of libdvdnav. Still need to re-implement seeking.
+ *
+ * Revision 1.8 2002/04/22 20:57:14 f1rmb
+ * Change/fix SPU active stream id. Same for audio. Few new functions, largely
+ * inspired from libogle ;-).
+ *
+ * Revision 1.7 2002/04/10 16:45:57 jcdutton
+ * Actually fix the const this time!
+ *
+ * Revision 1.6 2002/04/07 14:10:11 richwareham
+ * Stop C++ bitching about some things and extend the menus example
+ *
+ * Revision 1.5 2002/04/06 18:42:05 jcdutton
+ * Slight correction to handle quicker menu transitions.
+ *
+ * Revision 1.4 2002/04/06 18:31:50 jcdutton
+ * Some cleaning up.
+ * changed exit(1) to assert(0) so they actually get seen by the user so that it helps developers more.
+ *
+ * Revision 1.3 2002/04/02 18:22:27 richwareham
+ * Added reset patch from Kees Cook <kees@outflux.net>
+ *
+ * Revision 1.2 2002/04/01 18:56:28 richwareham
+ * Added initial example programs directory and make sure all debug/error output goes to stderr.
+ *
+ * Revision 1.1.1.1 2002/03/12 19:45:57 richwareham
+ * Initial import
+ *
+ * Revision 1.28 2002/02/02 23:26:20 richwareham
+ * Restored title selection
+ *
+ * Revision 1.27 2002/02/01 15:48:10 richwareham
+ * Re-implemented angle selection and title/chapter display
+ *
+ * Revision 1.26 2002/01/31 16:53:49 richwareham
+ * Big patch from Daniel Caujolle-Bert to (re)implement SPU/Audio language display
+ *
+ * Revision 1.25 2002/01/24 20:53:50 richwareham
+ * Added option to _not_ use DVD read-ahead to options
+ *
+ * Revision 1.24 2002/01/20 15:54:59 jcdutton
+ * Implement seeking.
+ * It is still a bit buggy, but works sometimes.
+ * I need to find out how to make the jump clean.
+ * At the moment, some corruption of the mpeg2 stream occurs,
+ * which causes libmpeg2 to crash.
+ *
+ * Revision 1.23 2002/01/18 00:23:52 jcdutton
+ * Support Ejecting of DVD.
+ * It will first un-mount the DVD, then eject it.
+ *
+ * Revision 1.22 2002/01/17 14:50:32 jcdutton
+ * Fix corruption of stream during menu transitions.
+ * Menu transitions are now clean.
+ *
+ * Revision 1.21 2002/01/15 00:37:03 jcdutton
+ * Just a few cleanups, and a assert fix. (memset fixed it)
+ *
+ * Revision 1.20 2002/01/13 22:17:57 jcdutton
+ * Change logging.
+ *
+ *
+ */
diff --git a/src/input/libdvdnav/dvdnav.h b/src/input/libdvdnav/dvdnav.h
new file mode 100644
index 000000000..4d03341cc
--- /dev/null
+++ b/src/input/libdvdnav/dvdnav.h
@@ -0,0 +1,879 @@
+/*
+ * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library.
+ *
+ * libdvdnav 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.
+ *
+ * libdvdnav 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
+ *
+ * $Id: dvdnav.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+/**
+ * \file dvdnav.h
+ * The main file you should include if you want to access dvdnav
+ * functionality.
+ */
+
+#ifndef DVDNAV_H_INCLUDED
+#define DVDNAV_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Defines the various events and dvdnav_event_t */
+#include "dvdnav_events.h"
+
+/* Various useful types */
+#include "dvd_types.h"
+
+#include <dvdread/dvd_reader.h>
+
+/**
+ * Opaque data-type can be viewed as a 'DVD handle'. You should get
+ * a pointer to a dvdnav_t from the dvdnav_open() function.
+ * \sa dvdnav_open()
+ */
+typedef struct dvdnav_s dvdnav_t;
+
+/* Status */
+typedef int dvdnav_status_t;
+
+#define DVDNAV_STATUS_ERR 0
+#define DVDNAV_STATUS_OK 1
+
+/**
+ * NOTE: */
+
+/**
+ * \defgroup init Initialisation & housekeeping functions
+ * These functions allow you to open a DVD device and associate it
+ * with a dvdnav_t.
+ *
+ * Unless otherwise stated, all functions return DVDNAV_STATUS_OK if
+ * they succeeded, otherwise DVDNAV_STATUS_ERR is returned and the error may
+ * be obtained by calling dvdnav_err_to_string().
+ *
+ * A minimal <tt>libdvdnav</tt> program should always call dvdnav_open()
+ * and dvdnav_close().
+ *
+ * \par Example:
+ * \include minimal.c
+ *
+ * @{
+ */
+
+/**
+ * Attempts to open the DVD drive at the specifiec path and pre-cache
+ * any CSS-keys that your hacked libdvdread may use.
+ *
+ * \param dest Pointer to a dvdnav_t pointer to fill in.
+ * \param path Any libdvdread acceptable path
+ */
+dvdnav_status_t dvdnav_open(dvdnav_t** dest, char *path);
+
+/**
+ * Closes a dvdnav_t previously opened with dvdnav_open(), freeing any
+ * memory associated with it.
+ *
+ * \param self dvdnav_t to close.
+ */
+dvdnav_status_t dvdnav_close(dvdnav_t *self);
+
+/**
+ * Resets the virtual machine and buffers in a previously opened dvdnav
+ *
+ * \param self dvdnav_t to reset.
+ */
+dvdnav_status_t dvdnav_reset(dvdnav_t *self);
+
+/**
+ * Fills a pointer with a value pointing to a string describing
+ * the path associated with an open dvdnav_t. It assigns it NULL
+ * on error.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param path Pointer to char* to fill in.
+ */
+dvdnav_status_t dvdnav_path(dvdnav_t *self, char** path);
+
+/**
+ * Returns a human-readable string describing the last error.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \returns A pointer to said string.
+ */
+char* dvdnav_err_to_string(dvdnav_t *self);
+
+/** \@} */
+
+/**
+ * \defgroup char Changing and Reading DVD Player characteristics
+ *
+ * These functions allow you to manipulate the various global characteristics
+ * of the DVD playback engine.
+ *
+ * @{
+ */
+
+/**
+ * Returns the region mask (bit 0 set implies region 1, bit 1 set implies
+ * region 2, etc) of the virtual machine. Generally you will only need to set
+ * this if you are playing RCE discs which query the virtual machine as to its
+ * region setting.
+ *
+ * \par Note:
+ * This has <b>nothing</b> to do with the region setting of the DVD drive.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param region Pointer to an int which will receive the region code mask.
+ */
+dvdnav_status_t dvdnav_get_region_mask(dvdnav_t *self, int *region);
+
+/**
+ * Sets the region mask of the virtual machine.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param mask 0x00..0xff -- the desired region mask.
+ *
+ * \sa dvdnav_get_region_mask()
+ */
+dvdnav_status_t dvdnav_set_region_mask(dvdnav_t *self, int mask);
+
+/**
+ * Specify whether read-ahead caching should be used. You may not want this if your
+ * decoding engine does its own buffering or if you don't like the fact that this is
+ * implemented in a multithreaded manner.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param use_readahead 0 - no, 1 - yes
+ */
+dvdnav_status_t dvdnav_set_readahead_flag(dvdnav_t *self, int use_readahead);
+
+/**
+ * Query whether readahead caching/buffering will be used.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param flag Pointer to int to recieve flag value.
+ *
+ * \sa dvdnav_get_readahead_flag()
+ */
+dvdnav_status_t dvdnav_get_readahead_flag(dvdnav_t *self, int* flag);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup data Reading Data
+ *
+ * These functions are used to poll the playback enginge and actually get data
+ * off the DVD.
+ *
+ * @{
+ */
+
+/**
+ * Attempts to get the next block off the DVD and copies it into the buffer 'buf'.
+ * If there is any special actions that may need to be performed, the value
+ * pointed to by 'event' gets set
+ * accordingly.
+ *
+ * If 'event' is DVDNAV_BLOCK_OK then 'buf' is filled with the next block
+ * (note that means it has to be at /least/ 2048 bytes big). 'len' is
+ * then set to 2048.
+ *
+ * Otherwise, buf is filled with an appropriate event structure and
+ * len is set to the length of that structure.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param buf Buffer (at least 2048 octets) to fill with next block/event structure.
+ * \param event Pointer to int to get event type.
+ * \param len Pointer to int to get the number of octets written into buf.
+ */
+dvdnav_status_t dvdnav_get_next_block(dvdnav_t *self, unsigned char *buf,
+ int *event, int *len);
+
+/**
+ * This basically does the same as dvdnav_get_next_block. The only difference is
+ * that it avoids a memcopy, when the requested block was found in the cache.
+ * I such a case (cache hit) this function will return a different pointer than
+ * the one handed in, pointing directly into the relevant block in the cache.
+ * Those pointer must _never_ be freed but instead returned to the library via
+ + dvdnav_free_cache_block.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param buf Buffer (at least 2048 octets) to fill with next block/event structure.
+ * A different buffer might be returned, if the block was found in the internal cache.
+ * \param event Pointer to int to get event type.
+ * \param len Pointer to int to get the number of octets written into buf.
+ */
+dvdnav_status_t dvdnav_get_next_cache_block(dvdnav_t *self, unsigned char **buf,
+ int *event, int *len);
+
+/**
+ * All buffers which came from the internal cache (when dvdnav_get_next_cache_block
+ * returned a buffer different from the one handed in) have to be freed with this
+ * function. Although handing in other buffers not from the cache doesn't cause any harm.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param buf Buffer received from internal cache.
+ */
+dvdnav_status_t dvdnav_free_cache_block(dvdnav_t *self, unsigned char *buf);
+
+/**
+ * Get video aspect code.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ *
+ * \returns Video aspect ratio code, 0 -- 4:3, 2 -- 16:9
+ */
+uint8_t dvdnav_get_video_aspect(dvdnav_t *self);
+
+/**
+ * Get video scaling permissions.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ *
+ * \returns Video scaling permissions, bit0 - deny letterboxing, bit1 - deny pan&scan
+ */
+uint8_t dvdnav_get_video_scale_permission(dvdnav_t *self);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup nav Navigation Commands
+ *
+ * @{
+ */
+
+/**
+ * Returns the number of titles on the disk.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param titles Pointer to int to receive number of titles.
+ */
+dvdnav_status_t dvdnav_get_number_of_titles(dvdnav_t *self, int *titles);
+
+/**
+ * Returns the number of programs within the current title.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param programs Pointer to int to receive number of programs.
+ */
+dvdnav_status_t dvdnav_get_number_of_programs(dvdnav_t *self, int *programs);
+
+/**
+ * If we are currently in a still-frame this function skips it (or attempts to).
+ * This might fail if this still-frame is of infinite duration as most DVD
+ * authors wouldn't expect you to be able to do this <tt>:)</tt>
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+dvdnav_status_t dvdnav_still_skip(dvdnav_t *self);
+
+/**
+ * Plays a specified title of the DVD.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param title 1..99 -- Title number to play.
+ */
+dvdnav_status_t dvdnav_title_play(dvdnav_t *self, int title);
+
+/**
+ * Plays the specifiec title, starting from the specified
+ * part (chapter).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param title 1..99 -- Title number to play.
+ * \param part 1..999 -- Part to start from.
+ */
+dvdnav_status_t dvdnav_part_play(dvdnav_t *self, int title, int part);
+
+/**
+ * Play the specified amount of parts of the specified title of
+ * the DVD then STOP.
+ *
+ * \par Note:
+ * Currently unimplemented!
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param title 1..99 -- Title number to play.
+ * \param part 1..999 -- Part to start from.
+ * \param parts_to_play 1..999 -- Number of parts to play.
+ */
+dvdnav_status_t dvdnav_part_play_auto_stop(dvdnav_t *self, int title,
+ int part, int parts_to_play);
+
+/**
+ * Play the specified title starting from the specified time
+ *
+ * \par Note:
+ * Currently unimplemented!
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param title 1..99 -- Title number to play.
+ * \param time Timecode to start from (hours, minutes, seconds + frames).
+ */
+dvdnav_status_t dvdnav_time_play(dvdnav_t *self, int title,
+ unsigned long int time);
+
+/**
+ * Stops playing the current title (causes a STOP action in
+ * dvdnav_get_next_block()).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ *
+ * \sa dvdnav_get_next_block()
+ */
+dvdnav_status_t dvdnav_stop(dvdnav_t *self);
+
+/**
+ * Stop playing current title and play the "GoUp"-program chain
+ * (which generally leads to the title menu or a higer-level menu).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+dvdnav_status_t dvdnav_go_up(dvdnav_t *self);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup search Searching
+ *
+ * @{
+ */
+
+/**
+ * Stop playing the current title and start playback of the title
+ * from the specified timecode.
+ *
+ * \par Note:
+ * Currently unimplemented!
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param time Timecode to start from.
+ */
+dvdnav_status_t dvdnav_time_search(dvdnav_t *self,
+ unsigned long int time);
+
+/**
+ * Stop playing the current title and start playback of the title
+ * from the specified sector offset.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param offset Sector offset to start from.
+ * \param origin Start from here, start or end.
+ */
+dvdnav_status_t dvdnav_sector_search(dvdnav_t *self,
+ unsigned long int offset, int origin);
+
+/**
+ * Stop playing the current title and start playback of the title
+ * from the specified part (chapter).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param part 1..999 -- Part to start from.
+ */
+dvdnav_status_t dvdnav_part_search(dvdnav_t *self, int part);
+
+/**
+ * Stop playing the current title and start playback of the title
+ * from the previous program (if it exists).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+dvdnav_status_t dvdnav_prev_pg_search(dvdnav_t *self);
+
+/**
+ * Stop playing the current title and start playback of the title
+ * from the first program.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+dvdnav_status_t dvdnav_top_pg_search(dvdnav_t *self);
+
+/**
+ * Stop playing the current title and start playback of the title
+ * from the next program (if it exists).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *self);
+
+/**
+ * Stop playing the current title and jump to the specified menu.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param menu Which menu to call (see DVDMenuID_t).
+ *
+ * \sa DVDMenuID_t (from <tt>libdvdread</tt>)
+ */
+dvdnav_status_t dvdnav_menu_call(dvdnav_t *self, DVDMenuID_t menu);
+
+/**
+ * Return the title number and chapter currently being played or
+ * -1 if in a menu.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param title Pointer to into which will receive the current title number.
+ * \param part Pointer to into which will receive the current part number.
+ */
+dvdnav_status_t dvdnav_current_title_info(dvdnav_t *self, int *title,
+ int *part);
+
+/**
+ * Return a string describing the title. This is an ID string encoded on the
+ * disc byt the author. In many cases this is a descriptive string such as
+ * `<tt>THE_MATRIX</tt>' but sometimes is sigularly uninformative such as
+ * `<tt>PDVD-011421</tt>'.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param title_str Pointer to C-style string to receive a string describing the title.
+ */
+dvdnav_status_t dvdnav_get_title_string(dvdnav_t *self, char **title_str);
+
+/**
+ * Return the current position (in blocks) within the current
+ * part and the length (in blocks) of said part.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param pos Pointer to unsigned int to get the current position.
+ * \param len Pointer to unsinged int to hold the length of the current part.
+ */
+dvdnav_status_t dvdnav_get_position(dvdnav_t *self, unsigned int* pos,
+ unsigned int *len);
+
+/**
+ * Return the current position (in blocks) within the current
+ * title and the length (in blocks) of said title.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param pos Pointer to unsigned int to get the current position.
+ * \param len Pointer to unsinged int to hold the length of the current title.
+ */
+dvdnav_status_t dvdnav_get_position_in_title(dvdnav_t *self,
+ unsigned int* pos,
+ unsigned int *len);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup highlight Highlights
+ *
+ * @{
+ */
+
+/**
+ * Get the currently highlighted button
+ * number (1..36) or 0 if no button is highlighed.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param button Pointer to the value to fill in.
+ */
+dvdnav_status_t dvdnav_get_current_highlight(dvdnav_t *self, int* button);
+
+/**
+ * Returns the Presentation Control Information (PCI) structure associated
+ * with the current position.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ *
+ * \sa pci_t (in <tt>libdvdread</tt>)
+ */
+pci_t* dvdnav_get_current_nav_pci(dvdnav_t *self);
+
+/**
+ * Returns the DSI (data seach information) structure associated
+ * with the current position.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ *
+ * \sa pci_t (in <tt>libdvdread</tt>)
+ */
+dsi_t* dvdnav_get_current_nav_dsi(dvdnav_t *self);
+
+/**
+ * Get the area associated with a certain button.
+ *
+ * \param nav_pci Pointer to the PCI structure you may have got via
+ * dvdnav_get_current_nav_pci().
+ *
+ * \param button Button number to query.
+ * \param mode 0..3 -- Button mode to query.
+ * \param highlight Pointer to dvdnav_highlight_area_t to fill in.
+ *
+ * \sa dvdnav_highlight_area_t
+ */
+dvdnav_status_t dvdnav_get_highlight_area(pci_t* nav_pci , int32_t button, int32_t mode,
+ dvdnav_highlight_area_t* highlight);
+
+/**
+ * Move button highlight around as suggested by function name (e.g. with arrow keys).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+dvdnav_status_t dvdnav_upper_button_select(dvdnav_t *self);
+/**
+ * Move button highlight around as suggested by function name (e.g. with arrow keys).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+dvdnav_status_t dvdnav_lower_button_select(dvdnav_t *self);
+/**
+ * Move button highlight around as suggested by function name (e.g. with arrow keys).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+dvdnav_status_t dvdnav_right_button_select(dvdnav_t *self);
+/**
+ * Move button highlight around as suggested by function name (e.g. with arrow keys).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+dvdnav_status_t dvdnav_left_button_select(dvdnav_t *self);
+
+/**
+ * Activate (press) the currently highlighted button.
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+dvdnav_status_t dvdnav_button_activate(dvdnav_t *self);
+
+/**
+ * Highlight a specific button.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param button 1..39 -- Button number to activate.
+ */
+dvdnav_status_t dvdnav_button_select(dvdnav_t *self, int button);
+
+/**
+ * Activate (press) specified button.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param button 1..39 -- Button number to activate.
+ */
+dvdnav_status_t dvdnav_button_select_and_activate(dvdnav_t *self, int button);
+
+/**
+ * Select button at specified (image) co-ordinates.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param x X co-ordinate in image.
+ * \param y Y xo-ordinate in image.
+ */
+dvdnav_status_t dvdnav_mouse_select(dvdnav_t *self, int x, int y);
+
+/**
+ * Activate (press) button at specified co-ordinates.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param x X co-ordinate in image.
+ * \param y Y xo-ordinate in image.
+ */
+dvdnav_status_t dvdnav_mouse_activate(dvdnav_t *self, int x, int y);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup languages Languages
+ *
+ * @{
+ */
+
+/**
+ * Set which menu language we should use.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param code 2 char ISO639 Language code in a C-style string.
+ */
+dvdnav_status_t dvdnav_menu_language_select(dvdnav_t *self,
+ char *code);
+
+/**
+ * Set which audio language we should use.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param code 2 char ISO639 Language code in a C-style string.
+ */
+dvdnav_status_t dvdnav_audio_language_select(dvdnav_t *self,
+ char *code);
+
+/**
+ * Set which spu language we should use.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param code 2 char ISO639 Language code in a C-style string.
+ */
+dvdnav_status_t dvdnav_spu_language_select(dvdnav_t *self,
+ char *code);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup streams Sub-Picture Unit (Subtitles) and Audio Streams
+ *
+ * All these commands manipulate the audio/subtitle stream numbers that the
+ * player engine thinks is playing. Note that all the streams are still returned
+ * multiplexed by dvdnav_get_next_block(). You should try to make sure that the
+ * MPEG demuxer and the player engine both have the same thoughts on what stream the
+ * user is currently listening to.
+ *
+ * \sa dvdnav_get_next_block()
+ *
+ * @{
+ */
+
+/**
+ * Set a specific PHYSICAL MPEG stream.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param audio 0..7 -- Stream number.
+ */
+dvdnav_status_t dvdnav_physical_audio_stream_change(dvdnav_t *self,
+ int audio);
+
+/**
+ * Set a specific logical audio stream.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param audio 0..7 -- Stream number.
+ */
+dvdnav_status_t dvdnav_logical_audio_stream_change(dvdnav_t *self,
+ int audio);
+
+/**
+ * Set the int pointed to to the current PHYSICAL audio
+ * stream.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param audio Pointer to int which will receive value.
+ */
+dvdnav_status_t dvdnav_get_physical_audio_stream(dvdnav_t *self, int* audio);
+
+/**
+ * Set the int pointed to to the current LOGICAL audio
+ * stream.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param audio Pointer to int which will receive value.
+ */
+dvdnav_status_t dvdnav_get_logical_audio_stream(dvdnav_t *self, int* audio);
+
+/**
+ * Set a specific PHYSICAL MPEG SPU stream and whether it should be
+ * displayed.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param stream 0..31 or 63 (dummy) -- Stram number.
+ * \param display: 0..1 -- Is this actually being displayed?
+ */
+dvdnav_status_t dvdnav_physical_spu_stream_change(dvdnav_t *self,
+ int stream, int display);
+
+/**
+ * Set a specific LOGICAL SPU stream and whether it should be
+ * displayed.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param stream 0..31 or 63 (dummy) -- Stram number.
+ * \param display: 0..1 -- Is this actually being displayed?
+ */
+dvdnav_status_t dvdnav_logical_spu_stream_change(dvdnav_t *self,
+ int stream, int display);
+
+/**
+ * Set the ints pointed to to the current PHYSICAL SPU
+ * stream & display flag.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param stream Pointer which will receive value.
+ * \param display Pointer which will receive value.
+ */
+dvdnav_status_t dvdnav_get_physical_spu_stream(dvdnav_t *self,
+ int* stream, int* display);
+
+/**
+ * Set the ints pointed to to the current LOGICAL SPU
+ * stream & display flag.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param stream Pointer which will receive value.
+ * \param display Pointer which will receive value.
+ */
+dvdnav_status_t dvdnav_get_logical_spu_stream(dvdnav_t *self,
+ int* stream, int* disply);
+
+/**
+ * Converts a *logical* audio stream id into country code
+ * (returns <tt>0xffff</tt> if no such stream).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param stream Stream number
+ */
+uint16_t dvdnav_audio_stream_to_lang(dvdnav_t *self, uint8_t stream);
+
+/**
+ * Converts a *logical* subpicture stream id into country code
+ * (returns <tt>0xffff</tt> if no such stream).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param stream Stream number
+ */
+uint16_t dvdnav_spu_stream_to_lang(dvdnav_t *self, uint8_t stream);
+
+/**
+ * Converts a *physical* audio stream id into a logical stream number
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param audio_num Stream number
+ */
+int8_t dvdnav_get_audio_logical_stream(dvdnav_t *self, uint8_t audio_num);
+
+/**
+ * Converts a *physical* subpicture stream id into a logical stream number
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param subp_num Stream number
+ */
+int8_t dvdnav_get_spu_logical_stream(dvdnav_t *self, uint8_t subp_num);
+
+/**
+ * Get active spu stream.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+int8_t dvdnav_get_active_spu_stream(dvdnav_t *self);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup angles Multiple angles
+ *
+ * The <tt>libdvdnav</tt> library abstracts away the difference between seamless and
+ * non-seamless angles. From the point of view of the programmer you just set the
+ * angle number and all is well in the world.
+ *
+ * \par Note:
+ * It is quite possible that some tremendously strange DVD feature might change the
+ * angle number from under you. Generally you should always view the results from
+ * dvdnav_get_angle_info() as definitive only up to the next time you call
+ * dvdnav_get_next_block().
+ *
+ * @{
+ */
+
+/**
+ * Sets the current angle. If you try to follow a non existant angle
+ * the call fails.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param angle 1..9 -- Currentl angle to follow.
+ */
+dvdnav_status_t dvdnav_angle_change(dvdnav_t *self, int angle);
+
+/**
+ * Returns the current angle and number of angles present.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param current_angle Pointer to int which will get the current angle.
+ * \param number_of_angles Pointer to int which will get the number of angles.
+ */
+dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *self, int* current_angle,
+ int *number_of_angles);
+
+/**
+ * FIXME: WTF does this do? -- High qulaity documentation huh?
+ */
+dvdnav_status_t dvdnav_get_cell_info(dvdnav_t *self, int* current_angle,
+ int *number_of_angles);
+
+/**
+ * Returns the still time status from the next cell
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+uint32_t dvdnav_get_next_still_flag(dvdnav_t *self);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup domain Domain Queries
+ *
+ * The following functions can be used to query whether we are in
+ * particular domains or not.
+ *
+ * @{
+ */
+
+/**
+ * Are we in the First Play domain. (Menu)
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \returns -1 on failure, 1 if condition is true, 0 if condition is false
+ */
+int8_t dvdnav_is_domain_fp(dvdnav_t *self);
+/**
+ * Are we in the Video management Menu domain. (Menu)
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \returns -1 on failure, 1 if condition is true, 0 if condition is false
+ */
+int8_t dvdnav_is_domain_vmgm(dvdnav_t *self);
+/**
+ * Are we in the Video Title Menu domain (Menu)
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \returns -1 on failure, 1 if condition is true, 0 if condition is false
+ */
+int8_t dvdnav_is_domain_vtsm(dvdnav_t *self);
+/**
+ * Are we in the Video Title domain (playing movie)?
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \returns -1 on failure, 1 if condition is true, 0 if condition is false
+ */
+int8_t dvdnav_is_domain_vts(dvdnav_t *self);
+
+/**
+ * @}
+ */
+
+/*
+ * The following info appears on the front page of the reference manual. It is
+ * included here to keep it with the main dvdnav source files. The copyright notice
+ * refers to the comments _only_. All machine-readable source code is covered by
+ * the copyright notice at the top of this file.
+ *
+ * Oh, and the official language of the documentation is English --
+ * English English (as in the English spoken in England) not US English.
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DVDNAV_H_INCLUDED */
diff --git a/src/input/libdvdnav/dvdnav_events.h b/src/input/libdvdnav/dvdnav_events.h
new file mode 100644
index 000000000..427c7896c
--- /dev/null
+++ b/src/input/libdvdnav/dvdnav_events.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library.
+ *
+ * libdvdnav 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.
+ *
+ * libdvdnav 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
+ *
+ * $Id: dvdnav_events.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifndef DVDNAV_EVENTS_H_INCLUDED
+#define DVDNAV_EVENTS_H_INCLUDED
+
+#include <dvdread/ifo_types.h>
+#include <dvdread/nav_types.h>
+#include <dvdread/dvd_reader.h>
+
+/**
+ * \file dvdnav_events.h
+ * This header defines events and event types
+ */
+
+/*** EVENTS ***/
+
+#define DVDNAV_BLOCK_OK 0 /*!< The next black was returned */
+#define DVDNAV_NOP 1 /*!< No action should be taken */
+#define DVDNAV_STILL_FRAME 2 /*!< The preceeding block was the last in a
+ still frame. */
+#define DVDNAV_SPU_STREAM_CHANGE 3 /*!< The SPU stream was changed */
+#define DVDNAV_AUDIO_STREAM_CHANGE 4 /*!< The Audio stream was changed */
+#define DVDNAV_VTS_CHANGE 5 /*!< We have changed VTS */
+#define DVDNAV_CELL_CHANGE 6 /*!< We have jumped to a new cell */
+#define DVDNAV_NAV_PACKET 7 /*!< The packet just passed was the NAV packet */
+#define DVDNAV_STOP 8 /*!< The last block was final, no more are coming */
+#define DVDNAV_HIGHLIGHT 9 /*!< Change highlight region */
+#define DVDNAV_SPU_CLUT_CHANGE 10 /*!< SPU CLUT */
+#define DVDNAV_SEEK_DONE 11 /*!< Seek done, subtitles should be reset */
+#define DVDNAV_HOP_CHANNEL 12 /*!< Sent when non-seemless stream change has happed
+ E.g. Menu button pressed causing change in menu */
+
+/*** EVENT TYPES ***/
+
+/**
+ * Structure providing information on DVDNAV_STILL_FRAME events.
+ */
+typedef struct {
+ int length; /*!<
+ The length (in seconds) the still frame
+ should be displayed for, or 0xff if
+ indefinate. */
+} dvdnav_still_event_t;
+
+/**
+ * Structure providing information on DVDNAV_SPU_STREAM_CHANGE events.
+ */
+typedef struct {
+ int physical_wide; /*!< The physical (MPEG) stream number for widescreen display. */
+ int physical_letterbox; /*!< The physical (MPEG) stream number for letterboxed display. */
+ int physical_pan_scan; /*!< The physical (MPEG) stream number for pan&scan display. */
+ int logical; /*!< The logical (DVD) stream number. */
+} dvdnav_spu_stream_change_event_t;
+
+/**
+ * Structure providing information on DVDNAV_AUDIO_STREAM_CHANGE events.
+ */
+typedef struct {
+ int physical; /*!< The physical (MPEG) stream number. */
+ int logical; /*!< The logical (DVD) stream number. */
+} dvdnav_audio_stream_change_event_t;
+
+/**
+ * Structure providing information on DVDNAV_VTS_CHANGE events.
+ */
+typedef struct {
+ int old_vtsN; /*!< The old VTS number */
+ dvd_read_domain_t old_domain; /*!< The old domain */
+ int new_vtsN; /*!< The new VTS number */
+ dvd_read_domain_t new_domain; /*!< The new domain */
+} dvdnav_vts_change_event_t;
+
+/**
+ * Structure providing information on DVDNAV_CELL_CHANGE events.
+ */
+typedef struct {
+ cell_playback_t *old_cell; /*!< The old cell (or NULL if this is
+ the first cell) */
+ cell_playback_t *new_cell; /*!< The cell_playback_t for the new cell */
+} dvdnav_cell_change_event_t;
+
+/**
+ * Structure providing information on DVDNAV_NAV_PACKET events.
+ */
+typedef struct {
+ pci_t *pci;
+ dsi_t *dsi;
+} dvdnav_nav_packet_event_t;
+
+/**
+ * Structure providing information on DVDNAV_HIGHLIGHT events.
+ */
+typedef struct {
+ int display; /*!< 0 - hide, 1 - show, entries below only guaranteed useful
+ if this is '1' */
+ uint32_t palette; /*!< The CLUT entries for the highlight palette
+ (4-bits per entry -> 4 entries) */
+ uint16_t sx,sy,ex,ey; /*!< The start/end x,y positions */
+ uint32_t pts; /*!< Highlight PTS to match with SPU */
+ uint32_t buttonN; /*!< Button number for the SPU decoder. */
+} dvdnav_highlight_event_t;
+
+#endif /* DVDNAV_EVENTS_H_INCLUDED */
diff --git a/src/input/libdvdnav/dvdnav_internal.h b/src/input/libdvdnav/dvdnav_internal.h
new file mode 100644
index 000000000..8b589af77
--- /dev/null
+++ b/src/input/libdvdnav/dvdnav_internal.h
@@ -0,0 +1,168 @@
+/* !! DO NO EDIT THIS FILE, it is automatically generated */
+/*
+ * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library.
+ *
+ * libdvdnav 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.
+ *
+ * libdvdnav 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
+ *
+ * $Id: dvdnav_internal.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifndef DVDNAV_INTERNAL_H_INCLUDED
+#define DVDNAV_INTERNAL_H_INCLUDED
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "dvdnav.h"
+#include "vm.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include <string.h>
+#include <pthread.h>
+
+#undef WORDS_BIGENDIAN
+
+#include <dvdread/dvd_reader.h>
+#include <dvdread/ifo_read.h>
+#include <dvdread/ifo_types.h>
+
+
+/* Maximum length of an error string */
+#define MAX_ERR_LEN 255
+
+/* Use the POSIX PATH_MAX if available */
+#ifdef PATH_MAX
+#define MAX_PATH_LEN PATH_MAX
+#else
+#define MAX_PATH_LEN 255 /* Arbitary */
+#endif
+
+#ifndef DVD_VIDEO_LB_LEN
+#define DVD_VIDEO_LB_LEN 2048
+#endif
+
+typedef struct read_cache_s read_cache_t;
+
+/*
+ * These are defined here because they are
+ * not in ifo_types.h, they maybe one day
+ */
+
+#ifndef audio_status_t
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+ unsigned int available : 1;
+ unsigned int zero1 : 4;
+ unsigned int stream_number : 3;
+ uint8_t zero2;
+#else
+ uint8_t zero2;
+ unsigned int stream_number : 3;
+ unsigned int zero1 : 4;
+ unsigned int available : 1;
+#endif
+} ATTRIBUTE_PACKED audio_status_t;
+#endif
+
+#ifndef spu_status_t
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+ unsigned int available : 1;
+ unsigned int zero1 : 2;
+ unsigned int stream_number_4_3 : 5;
+ unsigned int zero2 : 3;
+ unsigned int stream_number_wide : 5;
+ unsigned int zero3 : 3;
+ unsigned int stream_number_letterbox : 5;
+ unsigned int zero4 : 3;
+ unsigned int stream_number_pan_scan : 5;
+#else
+ unsigned int stream_number_pan_scan : 5;
+ unsigned int zero4 : 3;
+ unsigned int stream_number_letterbox : 5;
+ unsigned int zero3 : 3;
+ unsigned int stream_number_wide : 5;
+ unsigned int zero2 : 3;
+ unsigned int stream_number_4_3 : 5;
+ unsigned int zero1 : 2;
+ unsigned int available : 1;
+#endif
+} ATTRIBUTE_PACKED spu_status_t;
+#endif
+
+typedef struct dvdnav_vobu_s {
+ int32_t vobu_start; /* Logical Absolute. MAX needed is 0x300000 */
+ int32_t vobu_length; /* Relative offset */
+ int32_t blockN; /* Relative offset */
+ int32_t vobu_next; /* Relative offset */
+} dvdnav_vobu_t;
+
+/* The main DVDNAV type */
+
+struct dvdnav_s {
+ /* General data */
+ char path[MAX_PATH_LEN]; /* Path to DVD device/dir */
+ dvd_file_t *file; /* Currently opened file */
+ int open_vtsN; /* The domain and number of the... */
+ int open_domain; /* ..currently opened VOB */
+
+ /* Position data */
+ vm_position_t position_next;
+ vm_position_t position_current;
+ dvdnav_vobu_t vobu;
+
+ /* NAV data */
+ pci_t pci;
+ dsi_t dsi;
+
+ /* Flags */
+ int skip_still; /* Set when skipping a still */
+ int stop; /* Are we stopped? (note not paused, actually stopped) */
+ int spu_clut_changed; /* The SPU CLUT changed */
+ int started; /* vm_start has been called? */
+ int use_read_ahead; /* 1 - use read-ahead cache, 0 - don't */
+ /* VM */
+ vm_t* vm;
+ pthread_mutex_t vm_lock;
+
+ /* Highlight */
+ int hli_state; /* State of highlight: 0 - disabled,
+ 1 - selected,
+ 2 - activated */
+
+ /* Read-ahead cache */
+ read_cache_t *cache;
+
+ /* Errors */
+ char err_str[MAX_ERR_LEN];
+};
+
+/** USEFUL MACROS **/
+
+#define printerrf(format, args...) snprintf(this->err_str, MAX_ERR_LEN, format, ## args);
+#define printerr(str) strncpy(this->err_str, str, MAX_ERR_LEN);
+/* Save my typing */
+
+#define S_ERR DVDNAV_STATUS_ERR
+#define S_OK DVDNAV_STATUS_OK
+
+#endif /* DVDNAV_INTERNAL_H_INCLUDED */
diff --git a/src/input/libdvdnav/highlight.c b/src/input/libdvdnav/highlight.c
new file mode 100644
index 000000000..dc4f3877a
--- /dev/null
+++ b/src/input/libdvdnav/highlight.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library.
+ *
+ * libdvdnav 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.
+ *
+ * libdvdnav 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
+ *
+ * $Id: highlight.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/*
+#define BUTTON_TESTING
+*/
+#include <assert.h>
+
+#include <dvdnav.h>
+#include "dvdnav_internal.h"
+
+#include "vm.h"
+#
+#include <dvdread/nav_types.h>
+
+#ifdef BUTTON_TESTING
+#include <dvdread/nav_print.h>
+#endif
+
+/* Highlighting API calls */
+
+dvdnav_status_t dvdnav_get_current_highlight(dvdnav_t *this, int* button) {
+ if(!this)
+ return S_ERR;
+
+ /* Simply return the appropriate value based on the SPRM */
+ (*button) = (this->vm->state.HL_BTNN_REG) >> 10;
+
+ return S_OK;
+}
+
+btni_t *__get_current_button(dvdnav_t *this) {
+ int button = 0;
+
+ if(dvdnav_get_current_highlight(this, &button) != S_OK) {
+ printerrf("Unable to get information on current highlight.");
+ return NULL;
+ }
+#ifdef BUTTON_TESTING
+ navPrint_PCI(&(this->pci));
+#endif
+
+ return &(this->pci.hli.btnit[button-1]);
+}
+
+dvdnav_status_t dvdnav_button_auto_action(dvdnav_t *this) {
+ btni_t *button_ptr;
+
+ if(!this)
+ return S_ERR;
+
+ if((button_ptr = __get_current_button(this)) == NULL) {
+ return S_ERR;
+ }
+ if (button_ptr->auto_action_mode == 1) {
+ return S_OK;
+ }
+ return S_ERR;
+}
+
+
+dvdnav_status_t dvdnav_upper_button_select(dvdnav_t *this) {
+ btni_t *button_ptr;
+
+ if(!this)
+ return S_ERR;
+
+ if((button_ptr = __get_current_button(this)) == NULL) {
+ return S_ERR;
+ }
+
+ dvdnav_button_select(this, button_ptr->up);
+ if (dvdnav_button_auto_action(this) ) {
+ dvdnav_button_activate(this);
+ }
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_lower_button_select(dvdnav_t *this) {
+ btni_t *button_ptr;
+
+ if(!this)
+ return S_ERR;
+
+ if((button_ptr = __get_current_button(this)) == NULL) {
+ return S_ERR;
+ }
+
+ dvdnav_button_select(this, button_ptr->down);
+ if (dvdnav_button_auto_action(this) ) {
+ dvdnav_button_activate(this);
+ }
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_right_button_select(dvdnav_t *this) {
+ btni_t *button_ptr;
+
+ if(!this)
+ return S_ERR;
+
+ if((button_ptr = __get_current_button(this)) == NULL) {
+ printerr("Error fetching information on current button.");
+ return S_ERR;
+ }
+
+ dvdnav_button_select(this, button_ptr->right);
+ if (dvdnav_button_auto_action(this) ) {
+ dvdnav_button_activate(this);
+ }
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_left_button_select(dvdnav_t *this) {
+ btni_t *button_ptr;
+
+ if(!this)
+ return S_ERR;
+
+ if((button_ptr = __get_current_button(this)) == NULL) {
+ return S_ERR;
+ }
+
+ dvdnav_button_select(this, button_ptr->left);
+ if (dvdnav_button_auto_action(this) ) {
+ dvdnav_button_activate(this);
+ }
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_get_highlight_area(pci_t* nav_pci , int32_t button, int32_t mode,
+ dvdnav_highlight_area_t* highlight) {
+ btni_t *button_ptr;
+#ifdef BUTTON_TESTING
+ fprintf(stderr,"Button get_highlight_area %i\n", button);
+#endif
+
+ /* Set the highlight SPRM if the passed button was valid*/
+ if((button <= 0) || (button > nav_pci->hli.hl_gi.btn_ns)) {
+ fprintf(stderr,"Unable to select button number %i as it doesn't exist\n",
+ button);
+ return S_ERR;
+ }
+ button_ptr = &nav_pci->hli.btnit[button-1];
+
+ highlight->sx = button_ptr->x_start;
+ highlight->sy = button_ptr->y_start;
+ highlight->ex = button_ptr->x_end;
+ highlight->ey = button_ptr->y_end;
+ if(button_ptr->btn_coln != 0) {
+ highlight->palette = nav_pci->hli.btn_colit.btn_coli[button_ptr->btn_coln-1][mode];
+ } else {
+ highlight->palette = 0;
+ }
+ highlight->pts = nav_pci->hli.hl_gi.hli_s_ptm;
+ highlight->buttonN = button;
+#ifdef BUTTON_TESTING
+ fprintf(stderr,"highlight.c:Highlight area is (%u,%u)-(%u,%u), display = %i, button = %u\n",
+ button_ptr->x_start, button_ptr->y_start,
+ button_ptr->x_end, button_ptr->y_end,
+ 1,
+ button);
+#endif
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_button_activate(dvdnav_t *this) {
+ int button;
+ btni_t *button_ptr = NULL;
+
+ if(!this)
+ return S_ERR;
+ pthread_mutex_lock(&this->vm_lock);
+
+ /* Precisely the same as selecting a button except we want
+ * a different palette */
+ if(dvdnav_get_current_highlight(this, &button) != S_OK) {
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_ERR;
+ }
+/* FIXME: dvdnav_button_select should really return a
+ * special case for explicit NO-BUTTONS.
+ */
+ if(dvdnav_button_select(this, button) != S_OK) {
+ /* Special code to handle still menus with no buttons.
+ * the navigation is expected to report to the appicatino that a STILL is
+ * underway. In turn, the application is supposed to report to the user
+ * that the playback is pause. The user is then expected to undo the pause.
+ * ie: hit play. At that point, the navigation should release the still and
+ * go to the next Cell.
+ * Explanation by Mathieu Lavage <mathieu_lacage@realmagic.fr>
+ * Code added by jcdutton.
+ */
+ if (this->position_current.still != 0) {
+ /* In still, but no buttons. */
+ vm_get_next_cell(this->vm);
+ this->position_current.still = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_ERR;
+ }
+ /* FIXME: The button command should really be passed in the API instead. */
+ button_ptr = __get_current_button(this);
+ /* Finally, make the VM execute the appropriate code and
+ * scedule a jump */
+#ifdef BUTTON_TESTING
+ fprintf(stderr, "libdvdnav: Evaluating Button Activation commands.\n");
+#endif
+ if(vm_eval_cmd(this->vm, &(button_ptr->cmd)) == 1) {
+ /* Command caused a jump */
+ this->vm->hop_channel++;
+ this->position_current.still = 0;
+ }
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_button_select(dvdnav_t *this, int button) {
+
+ if(!this) {
+ printerrf("Unable to select button number %i as this state bad",
+ button);
+ return S_ERR;
+ }
+
+#ifdef BUTTON_TESTING
+ fprintf(stderr,"libdvdnav: Button select %i\n", button);
+#endif
+
+ /* Set the highlight SPRM if the passed button was valid*/
+ /* FIXME: this->pci should be provided by the application. */
+ if((button <= 0) || (button > this->pci.hli.hl_gi.btn_ns)) {
+ printerrf("Unable to select button number %i as it doesn't exist",
+ button);
+ return S_ERR;
+ }
+ this->vm->state.HL_BTNN_REG = (button << 10);
+
+ this->hli_state = 1; /* Selected */
+
+ this->position_current.button = -1; /* Force Highligh change */
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_button_select_and_activate(dvdnav_t *this,
+ int button) {
+ /* A trivial function */
+ if(dvdnav_button_select(this, button) != S_ERR) {
+ return dvdnav_button_activate(this);
+ }
+
+ /* Should never get here without an error */
+ return S_ERR;
+}
+
+dvdnav_status_t dvdnav_mouse_select(dvdnav_t *this, int x, int y) {
+ int button, cur_button;
+ uint32_t best,dist;
+ int mx,my,dx,dy,d;
+
+ /* FIXME: At the moment, the case of no button matchin (x,y) is
+ * silently ignored, is this OK? */
+ if(!this)
+ return S_ERR;
+
+ if(dvdnav_get_current_highlight(this, &cur_button) != S_OK) {
+ return S_ERR;
+ }
+
+ best = 0;
+ dist = 0x08000000; /* >> than (720*720)+(567*567); */
+
+ /* Loop through each button */
+ for(button=1; button <= this->pci.hli.hl_gi.btn_ns; button++) {
+ btni_t *button_ptr = NULL;
+ button_ptr = &(this->pci.hli.btnit[button-1]);
+ if((x >= button_ptr->x_start) && (x <= button_ptr->x_end) &&
+ (y >= button_ptr->y_start) && (y <= button_ptr->y_end)) {
+ mx = (button_ptr->x_start + button_ptr->x_end)/2;
+ my = (button_ptr->y_start + button_ptr->y_end)/2;
+ dx = mx - x;
+ dy = my - y;
+ d = (dx*dx) + (dy*dy);
+ /* If the mouse is within the button and the mouse is closer
+ * to the center of this button then it is the best choice. */
+ if(d < dist) {
+ dist = d; best=button;
+ }
+ }
+ }
+
+ if (best!=0) {
+ /* As an efficiency measure, only re-select the button
+ * if it is different to the previously selected one. */
+ if(best != cur_button) {
+ dvdnav_button_select(this, best);
+ }
+ }
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_mouse_activate(dvdnav_t *this, int x, int y) {
+ /* A trivial function */
+ if(dvdnav_mouse_select(this, x,y) != S_ERR) {
+ return dvdnav_button_activate(this);
+ }
+
+ /* Should never get here without an error */
+ return S_ERR;
+}
+
diff --git a/src/input/libdvdnav/navigation.c b/src/input/libdvdnav/navigation.c
new file mode 100644
index 000000000..ba04b88c4
--- /dev/null
+++ b/src/input/libdvdnav/navigation.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library.
+ *
+ * libdvdnav 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.
+ *
+ * libdvdnav 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
+ *
+ * $Id: navigation.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <dvdnav.h>
+#include "dvdnav_internal.h"
+
+#include "vm.h"
+
+/* Navigation API calls */
+
+dvdnav_status_t dvdnav_still_skip(dvdnav_t *this) {
+ if(!this)
+ return S_ERR;
+
+ this->position_current.still = 0;
+ this->skip_still = 1;
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_get_number_of_titles(dvdnav_t *this, int *titles) {
+ if(!this)
+ return S_ERR;
+
+ if(!titles) {
+ printerr("Passed a NULL pointer");
+ return S_ERR;
+ }
+
+ (*titles) = vm_get_vmgi(this->vm)->tt_srpt->nr_of_srpts;
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_get_number_of_programs(dvdnav_t *this, int *programs) {
+ if(!this)
+ return S_ERR;
+
+ if(!programs) {
+ printerr("Passed a NULL pointer");
+ return S_ERR;
+ }
+
+ (*programs) = this->vm->state.pgc->nr_of_programs;
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_title_play(dvdnav_t *this, int title) {
+ int num_titles;
+
+ if(!this) {
+ return S_ERR;
+ }
+
+ /* Check number of titles */
+ dvdnav_get_number_of_titles(this, &num_titles);
+ if((title > num_titles) || (title <= 0)) {
+ printerrf("Invalid title passed (%i, maximum %i)", title,
+ num_titles);
+ return S_ERR;
+ }
+
+ vm_start_title(this->vm, title);
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_part_play(dvdnav_t *this, int title, int part) {
+ int num_titles, num_progs;
+
+ if(!this) {
+ return S_ERR;
+ }
+
+ /* Check number of titles */
+ dvdnav_get_number_of_titles(this, &num_titles);
+ if((title > num_titles) || (title <= 0)) {
+ printerrf("Invalid title passed (%i, maximum %i)", title,
+ num_titles);
+ return S_ERR;
+ }
+
+ vm_start_title(this->vm, title);
+
+
+ /* Check number of parts */
+ num_progs = this->vm->state.pgc->nr_of_programs;
+ if((part > num_progs) || (part <= 0)) {
+ printerrf("Invalid program passed (%i, maximum %i)", part,
+ num_progs);
+ return S_ERR;
+ }
+
+ vm_jump_prog(this->vm, part);
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_part_play_auto_stop(dvdnav_t *this, int title,
+ int part, int parts_to_play) {
+ /* Perform jump as per usual */
+
+ return dvdnav_part_play(this, title, part);
+
+ /* FIXME: Impement auto-stop */
+
+ /* return S_OK;*/
+}
+
+dvdnav_status_t dvdnav_time_play(dvdnav_t *this, int title,
+ unsigned long int time) {
+ /* FIXME: Implement */
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_stop(dvdnav_t *this) {
+ if(!this)
+ return S_ERR;
+
+ /* Set the STOP flag */
+
+ this->stop = 1;
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_go_up(dvdnav_t *this) {
+ if(!this)
+ return S_ERR;
+
+ /* A nice easy function... delegate to the VM */
+ vm_go_up(this->vm);
+
+ return S_OK;
+}
+
+
+
diff --git a/src/input/libdvdnav/read_cache.c b/src/input/libdvdnav/read_cache.c
new file mode 100644
index 000000000..1d6e68727
--- /dev/null
+++ b/src/input/libdvdnav/read_cache.c
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library.
+ *
+ * libdvdnav 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.
+ *
+ * libdvdnav 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
+ *
+ * $Id: read_cache.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "dvdnav.h"
+#include "read_cache.h"
+#include <pthread.h>
+#include <sys/time.h>
+#include <time.h>
+
+/*
+#define DVDNAV_PROFILE
+*/
+
+/* Read-ahead cache structure. */
+#if _MULTITHREAD_
+
+/* For the multithreaded cache, the cache is a ring buffer + writing
+ * thread that continuously reads data into the buffer until it is
+ * full or the 'upper-bound' has been reached.
+ */
+
+#define CACHE_BUFFER_SIZE 2048 /* Cache this number of blocks at a time */
+
+struct read_cache_s {
+ pthread_mutex_t cache_lock;
+ pthread_t read_thread;
+
+ /* Buffer */
+ uint8_t *buffer;
+
+ /* Size of buffer */
+ int32_t size;
+ /* block offset from sector start of buffer 'head' */
+ uint32_t pos;
+ /* block offset from sector start of read point */
+ uint32_t read_point;
+ /* block offset from buffer start to ring-boundary */
+ uint32_t start;
+
+ /* Bit of strange cross-linking going on here :) -- Gotta love C :) */
+ dvdnav_t *dvd_self;
+};
+
+#else
+
+#define READ_CACHE_CHUNKS 10
+
+typedef struct read_cache_chunk_s {
+ uint8_t *cache_buffer;
+ int32_t cache_start_sector; /* -1 means cache invalid */
+ size_t cache_block_count;
+ size_t cache_malloc_size;
+ int cache_valid;
+ int usage_count; /* counts how many buffers where issued from this chunk */
+} read_cache_chunk_t;
+
+struct read_cache_s {
+ read_cache_chunk_t chunk[READ_CACHE_CHUNKS];
+ int current;
+ int freeing; /* is set to one when we are about to dispose the cache */
+ pthread_mutex_t lock;
+
+ /* Bit of strange cross-linking going on here :) -- Gotta love C :) */
+ dvdnav_t *dvd_self;
+};
+#endif
+
+#define _MT_TRACE 0
+
+#if _MT_TRACE
+#define dprintf(fmt, args...) fprintf(stderr, "%s: "fmt, __func__ , ## args);
+#else
+#define dprintf(fmt, args...) /* Nowt */
+#endif
+
+#if _MULTITHREAD_
+
+void * read_cache_read_thread (void * this_gen) {
+ int cont = 1;
+ int32_t diff, start;
+ uint32_t pos, size, startp, endp;
+ uint32_t s,c;
+ uint8_t *at;
+ read_cache_t *self = (read_cache_t*)this_gen;
+
+ while(cont) {
+
+ pthread_mutex_lock(&self->cache_lock);
+
+ if(self->size >= 0) {
+ diff = self->read_point - self->pos;
+ if(diff >= self->size/2) {
+ dprintf("(II) Read thread -- ");
+
+ startp = (self->start) % CACHE_BUFFER_SIZE;
+ endp = abs((self->start + diff - 1) % CACHE_BUFFER_SIZE);
+ dprintf("startp = %i, endp = %i -- ",startp, endp);
+
+ pos = self->pos + diff;
+ size = self->size - diff;
+ start = (self->start + diff) % CACHE_BUFFER_SIZE;
+
+ /* Fill remainder of buffer */
+
+ if(startp > endp) {
+ s = pos + size; c = CACHE_BUFFER_SIZE - startp;
+ at = self->buffer + (startp * DVD_VIDEO_LB_LEN);
+ if(c > 0) {
+ dprintf("(1) Reading from %i to %i to %i ", s, s+c-1, startp);
+ pthread_mutex_unlock(&self->cache_lock);
+ DVDReadBlocks(self->dvd_self->file, s,c, at);
+ pthread_mutex_lock(&self->cache_lock);
+ }
+
+ s = pos + size + c; c = CACHE_BUFFER_SIZE - size - c;
+ at = self->buffer;
+ if(c > 0) {
+ dprintf("(2) Reading from %i to %i to %i ", s, s+c-1, 0);
+ pthread_mutex_unlock(&self->cache_lock);
+ DVDReadBlocks(self->dvd_self->file, s,c, at);
+ pthread_mutex_lock(&self->cache_lock);
+ }
+ } else {
+ s = pos + size; c = CACHE_BUFFER_SIZE - size;
+ at = self->buffer + (startp * DVD_VIDEO_LB_LEN);
+ if(c > 0) {
+ dprintf("(3) Reading from %i to %i to %i ", s, s+c-1, startp);
+ pthread_mutex_unlock(&self->cache_lock);
+ DVDReadBlocks(self->dvd_self->file, s,c, at);
+ pthread_mutex_lock(&self->cache_lock);
+ }
+ }
+
+ dprintf("\n");
+
+ self->pos = pos;
+ self->start = start; self->size = CACHE_BUFFER_SIZE;
+ }
+ }
+
+ pthread_mutex_unlock(&self->cache_lock);
+ cont = (self->buffer != NULL);
+ usleep(100);
+ }
+
+ return NULL;
+}
+
+read_cache_t *dvdnav_read_cache_new(dvdnav_t* dvd_self) {
+ read_cache_t *me;
+
+ me = (read_cache_t*)malloc(sizeof(struct read_cache_s));
+
+ if(me) {
+ int err;
+
+ me->dvd_self = dvd_self;
+ me->buffer = (uint8_t*)malloc(CACHE_BUFFER_SIZE * DVD_VIDEO_LB_LEN);
+ me->start = 0;
+ me->pos = 0;
+ me->read_point = 0;
+ me->size = -1;
+
+ /* Initialise the mutex */
+ pthread_mutex_init(&me->cache_lock, NULL);
+
+ if ((err = pthread_create (&me->read_thread,
+ NULL, read_cache_read_thread, me)) != 0) {
+ dprintf("read_cache: can't create new thread (%s)\n",strerror(err));
+ }
+ }
+
+ return me;
+}
+
+void dvdnav_read_cache_free(read_cache_t* self) {
+ dvdnav_t *tmp;
+
+ pthread_mutex_lock(&self->cache_lock);
+
+ if(self->buffer) {
+ free(self->buffer);
+ self->buffer = NULL;
+ self->size = -2;
+ }
+
+ pthread_mutex_unlock(&self->cache_lock);
+
+ pthread_join(self->read_thread, NULL);
+
+ pthread_mutex_destroy(&self->cache_lock);
+
+ tmp = self->dvd_self;
+ free(self);
+
+ /* We free the main structure, too, because we have no buffers out there. */
+ free(tmp);
+}
+
+/* This function MUST be called whenever self->file changes. */
+void dvdnav_read_cache_clear(read_cache_t *self) {
+ if(!self)
+ return;
+
+ pthread_mutex_lock(&self->cache_lock);
+ self->size = -1;
+ self->start = 0;
+ self->pos = 0;
+ self->read_point = 0;
+ pthread_mutex_unlock(&self->cache_lock);
+}
+
+/* This function is called just after reading the NAV packet. */
+void dvdnav_pre_cache_blocks(read_cache_t *self, int sector, size_t block_count) {
+ if(!self)
+ return;
+
+ if(!self->dvd_self->use_read_ahead) {
+ return;
+ }
+
+ pthread_mutex_lock(&self->cache_lock);
+ dprintf("Requested pre-cache (%i -> +%i) : current state pos=%i, size=%i.\n",
+ sector, block_count, self->pos, self->size);
+
+ /* Are the contents of the buffer in any way relevant? */
+ if((self->size > 0) && (sector >= self->pos) && (sector <= self->pos+self->size)) {
+ dprintf("Contents relevant ... adjusting\n");
+ self->read_point = sector;
+ } else {
+ /* Flush the cache as its not much use */
+ dprintf("Contents irrelevent... flushing\n");
+ self->size = 0;
+ self->start = 0;
+ self->pos = sector;
+ self->read_point = sector;
+ }
+
+ pthread_mutex_unlock(&self->cache_lock);
+}
+
+/* This function will do the cache read once implemented */
+int dvdnav_read_cache_block( read_cache_t *self, int sector, size_t block_count, uint8_t **buf) {
+ int result, diff;
+
+ if(!self)
+ return 0;
+
+ pthread_mutex_lock(&self->cache_lock);
+ dprintf("Read from %i -> +%i (buffer pos=%i, read_point=%i, size=%i)... ", sector, block_count,
+ self->pos, self->read_point, self->size);
+ if((self->size > 0) && (sector >= self->read_point) &&
+ (sector + block_count <= self->pos + self->size)) {
+ /* Hit */
+
+ /* Drop any skipped blocks */
+ diff = sector - self->read_point;
+ if(diff > 0)
+ self->read_point += diff;
+
+ diff = self->read_point - self->pos;
+
+ if(((self->start + diff) % CACHE_BUFFER_SIZE) + block_count <= CACHE_BUFFER_SIZE) {
+ dprintf("************** Single read\n");
+ memcpy(*buf, self->buffer + (((self->start + diff) % CACHE_BUFFER_SIZE) * DVD_VIDEO_LB_LEN),
+ block_count * DVD_VIDEO_LB_LEN);
+ self->read_point += block_count;
+ pthread_mutex_unlock(&self->cache_lock);
+
+ return (int)block_count;
+ } else {
+ int32_t boundary = CACHE_BUFFER_SIZE - self->start;
+
+ dprintf("************** Multiple read\n");
+ memcpy(*buf, self->buffer + (((self->start + diff) % CACHE_BUFFER_SIZE) * DVD_VIDEO_LB_LEN),
+ boundary * DVD_VIDEO_LB_LEN);
+ memcpy(*buf + (boundary * DVD_VIDEO_LB_LEN), self->buffer,
+ (block_count-boundary) * DVD_VIDEO_LB_LEN);
+ self->read_point += block_count;
+ pthread_mutex_unlock(&self->cache_lock);
+
+ return (int)block_count;
+ }
+ } else {
+ /* Miss */
+
+ fprintf(stderr, "DVD read cache miss! (not bad but a performance hit) sector=%d\n", sector);
+ result = DVDReadBlocks( self->dvd_self->file, sector, block_count, *buf);
+ self->read_point = sector+block_count;
+ if(self->read_point > self->pos + self->size) {
+ /* Flush the cache as its not much use */
+ dprintf("Contents irrelevent... flushing\n");
+ self->size = 0;
+ self->start = 0;
+ self->pos = sector+block_count;
+ }
+ pthread_mutex_unlock(&self->cache_lock);
+ usleep(300);
+ return result;
+ }
+
+ /* Should never get here */
+ return 0;
+}
+
+dvdnav_status_t dvdnav_free_cache_block(dvdnav_t *self, unsigned char *buf) {
+ return DVDNAV_STATUS_OK;
+}
+
+#else
+
+read_cache_t *dvdnav_read_cache_new(dvdnav_t* dvd_self) {
+ read_cache_t *self;
+ int i;
+
+ self = (read_cache_t *)malloc(sizeof(read_cache_t));
+
+ if(self) {
+ self->current = 0;
+ self->freeing = 0;
+ self->dvd_self = dvd_self;
+ pthread_mutex_init(&self->lock, NULL);
+ dvdnav_read_cache_clear(self);
+ for (i = 0; i < READ_CACHE_CHUNKS; i++) {
+ self->chunk[i].cache_buffer = NULL;
+ self->chunk[i].usage_count = 0;
+ }
+ }
+
+ return self;
+}
+
+void dvdnav_read_cache_free(read_cache_t* self) {
+ dvdnav_t *tmp;
+ int i;
+
+ pthread_mutex_lock(&self->lock);
+ self->freeing = 1;
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ if (self->chunk[i].cache_buffer && self->chunk[i].usage_count == 0) {
+ free(self->chunk[i].cache_buffer);
+ self->chunk[i].cache_buffer = NULL;
+ }
+ pthread_mutex_unlock(&self->lock);
+
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ if (self->chunk[i].cache_buffer) return;
+
+ /* all buffers returned, free everything */
+ tmp = self->dvd_self;
+ pthread_mutex_destroy(&self->lock);
+ free(self);
+ free(tmp);
+}
+
+/* This function MUST be called whenever self->file changes. */
+void dvdnav_read_cache_clear(read_cache_t *self) {
+ int i;
+
+ if(!self)
+ return;
+
+ pthread_mutex_lock(&self->lock);
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ self->chunk[i].cache_valid = 0;
+ pthread_mutex_unlock(&self->lock);
+}
+
+#ifdef DVDNAV_PROFILE
+//#ifdef ARCH_X86
+__inline__ unsigned long long int dvdnav_rdtsc()
+{
+ unsigned long long int x;
+ __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
+ return x;
+}
+//#endif
+#endif
+
+/* This function is called just after reading the NAV packet. */
+void dvdnav_pre_cache_blocks(read_cache_t *self, int sector, size_t block_count) {
+ int i, use, result;
+#ifdef DVDNAV_PROFILE
+ struct timeval tv1, tv2, tv3;
+ unsigned long long p1, p2, p3;
+#endif
+
+ if(!self)
+ return;
+
+ if(!self->dvd_self->use_read_ahead)
+ return;
+
+ pthread_mutex_lock(&self->lock);
+
+ /* find a free cache chunk that best fits the required size */
+ use = -1;
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ if (self->chunk[i].usage_count == 0 && self->chunk[i].cache_buffer &&
+ self->chunk[i].cache_malloc_size >= block_count &&
+ (use == -1 || self->chunk[use].cache_malloc_size > self->chunk[i].cache_malloc_size))
+ use = i;
+
+ if (use == -1) {
+ /* we haven't found a cache chunk, so we try to reallocate an existing one */
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ if (self->chunk[i].usage_count == 0 && self->chunk[i].cache_buffer &&
+ (use == -1 || self->chunk[use].cache_malloc_size < self->chunk[i].cache_malloc_size))
+ use = i;
+ if (use >= 0) {
+ self->chunk[use].cache_buffer = realloc(self->chunk[use].cache_buffer,
+ block_count * DVD_VIDEO_LB_LEN);
+ dprintf("pre_cache DVD read realloc happened\n");
+ self->chunk[use].cache_malloc_size = block_count;
+ } else {
+ /* we still haven't found a cache chunk, let's allocate a new one */
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ if (!self->chunk[i].cache_buffer) {
+ use = i;
+ break;
+ }
+ if (use >= 0) {
+ /* We start with a sensible figure for the first malloc of 500 blocks.
+ * Some DVDs I have seen venture to 450 blocks.
+ * This is so that fewer realloc's happen if at all.
+ */
+ self->chunk[i].cache_buffer = malloc((block_count > 500 ? block_count : 500) * DVD_VIDEO_LB_LEN);
+ self->chunk[i].cache_malloc_size = block_count > 500 ? block_count : 500;
+ dprintf("pre_cache DVD read malloc %d blocks\n",
+ (block_count > 500 ? block_count : 500 ));
+ }
+ }
+ }
+
+ if (use >= 0) {
+ self->chunk[use].cache_start_sector = sector;
+ self->chunk[use].cache_block_count = block_count;
+ self->current = use;
+#ifdef DVDNAV_PROFILE
+ gettimeofday(&tv1, NULL);
+ p1 = dvdnav_rdtsc();
+#endif
+ result = DVDReadBlocks (self->dvd_self->file, sector, block_count, self->chunk[use].cache_buffer);
+#ifdef DVDNAV_PROFILE
+ p2 = dvdnav_rdtsc();
+ gettimeofday(&tv2, NULL);
+ timersub(&tv2, &tv1, &tv3);
+ dprintf("pre_cache DVD read %ld us, profile = %lld, block_count = %d\n",
+ tv3.tv_usec, p2-p1, block_count);
+#endif
+ self->chunk[use].cache_valid = 1;
+ } else
+ dprintf("pre_caching was impossible, no cache chunk available\n");
+
+ pthread_mutex_unlock(&self->lock);
+}
+
+int dvdnav_read_cache_block(read_cache_t *self, int sector, size_t block_count, uint8_t **buf) {
+ int i, use;
+
+ if(!self)
+ return 0;
+
+ pthread_mutex_lock(&self->lock);
+
+ use = -1;
+ if(self->dvd_self->use_read_ahead) {
+ /* first check, if sector is in current chunk */
+ read_cache_chunk_t cur = self->chunk[self->current];
+ if (cur.cache_valid && sector >= cur.cache_start_sector &&
+ sector + block_count <= cur.cache_start_sector + cur.cache_block_count)
+ use = self->current;
+ else
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ if (self->chunk[i].cache_valid && sector >= self->chunk[i].cache_start_sector &&
+ sector + block_count <= self->chunk[i].cache_start_sector + self->chunk[i].cache_block_count)
+ use = i;
+ }
+
+ if (use >= 0) {
+ self->chunk[use].usage_count++;
+ *buf = &self->chunk[use].cache_buffer[(sector - self->chunk[use].cache_start_sector) *
+ DVD_VIDEO_LB_LEN * block_count];
+ pthread_mutex_unlock(&self->lock);
+ return DVD_VIDEO_LB_LEN * block_count;
+ } else {
+ if (self->dvd_self->use_read_ahead)
+ dprintf("cache miss on sector %d\n", sector);
+ pthread_mutex_unlock(&self->lock);
+ return DVDReadBlocks(self->dvd_self->file, sector, block_count, *buf);
+ }
+}
+
+dvdnav_status_t dvdnav_free_cache_block(dvdnav_t *self, unsigned char *buf) {
+ read_cache_t *cache;
+ int i;
+
+ if (!self)
+ return DVDNAV_STATUS_ERR;
+
+ cache = self->cache;
+ if (!cache)
+ return DVDNAV_STATUS_ERR;
+
+ pthread_mutex_lock(&cache->lock);
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ if (cache->chunk[i].cache_buffer && buf >= cache->chunk[i].cache_buffer &&
+ buf < cache->chunk[i].cache_buffer + cache->chunk[i].cache_malloc_size * DVD_VIDEO_LB_LEN)
+ cache->chunk[i].usage_count--;
+ pthread_mutex_unlock(&cache->lock);
+
+ if (cache->freeing)
+ /* when we want to dispose the cache, try freeing it now */
+ dvdnav_read_cache_free(cache);
+
+ return DVDNAV_STATUS_OK;
+}
+
+#endif
diff --git a/src/input/libdvdnav/read_cache.h b/src/input/libdvdnav/read_cache.h
new file mode 100644
index 000000000..23c58c58e
--- /dev/null
+++ b/src/input/libdvdnav/read_cache.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library.
+ *
+ * libdvdnav 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.
+ *
+ * libdvdnav 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
+ *
+ * $Id: read_cache.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifndef __DVDNAV_READ_CACHE_H
+#define __DVDNAV_READ_CACHE_H
+
+#include "dvdnav_internal.h"
+
+/* Opaque cache type -- defined in dvdnav_internal.h */
+/* typedef struct read_cache_s read_cache_t; */
+
+/* EXPERIMENTAL: Setting the following to 1 will use an experimental multi-threaded
+ * read-ahead cache.
+ */
+#define _MULTITHREAD_ 0
+
+/* Constructor/destructors */
+read_cache_t *dvdnav_read_cache_new(dvdnav_t* dvd_self);
+void dvdnav_read_cache_free(read_cache_t* self);
+
+/* This function MUST be called whenever self->file changes. */
+void dvdnav_read_cache_clear(read_cache_t *self);
+/* This function is called just after reading the NAV packet. */
+void dvdnav_pre_cache_blocks(read_cache_t *self, int sector, size_t block_count);
+/* This function will do the cache read.
+ * The buffer handed in must be malloced to take one dvd block.
+ * On a cache hit, a different buffer will be returned though.
+ * Those buffers must _never_ be freed. */
+int dvdnav_read_cache_block(read_cache_t *self, int sector, size_t block_count, uint8_t **buf);
+
+#endif /* __DVDNAV_READ_CACHE_H */
diff --git a/src/input/libdvdnav/searching.c b/src/input/libdvdnav/searching.c
new file mode 100644
index 000000000..43ac770f4
--- /dev/null
+++ b/src/input/libdvdnav/searching.c
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library.
+ *
+ * libdvdnav 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.
+ *
+ * libdvdnav 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
+ *
+ * $Id: searching.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <dvdnav.h>
+#include "dvdnav_internal.h"
+
+#include "vm.h"
+#include <dvdread/nav_types.h>
+
+/* Searching API calls */
+
+dvdnav_status_t dvdnav_time_search(dvdnav_t *this,
+ unsigned long int time) {
+/* Time search the current PGC based on the xxx table */
+ return S_OK;
+}
+
+/* Scan the ADMAP for a particular block number. */
+/* Return placed in vobu. */
+/* Returns error status */
+
+dvdnav_status_t dvdnav_scan_admap(dvdnav_t *this, int32_t domain, int32_t seekto_block, int32_t *vobu) {
+ /* FIXME:Need to handle seeking outside current cell. */
+ vobu_admap_t *admap = NULL;
+ *vobu = -1;
+ fprintf(stderr,"Seeking to target %u ...\n",
+ seekto_block);
+
+ /* Search through the VOBU_ADMAP for the nearest VOBU
+ * to the target block */
+ switch(domain) {
+ case FP_DOMAIN:
+ case VMGM_DOMAIN:
+ admap = this->vm->vmgi->menu_vobu_admap;
+ break;
+ case VTSM_DOMAIN:
+ admap = this->vm->vtsi->menu_vobu_admap;
+ break;
+ case VTS_DOMAIN:
+ admap = this->vm->vtsi->vts_vobu_admap;
+ break;
+ default:
+ fprintf(stderr,"Error: Unknown domain for seeking seek.\n");
+ }
+ if(admap) {
+ int32_t address = 0;
+ int32_t vobu_start, next_vobu;
+ int found = 0;
+
+ /* Search through ADMAP for best sector */
+ vobu_start = 0x3fffffff;
+ /* FIXME: Implement a faster search algorithm */
+ while((!found) && ((address<<2) < admap->last_byte)) {
+ next_vobu = admap->vobu_start_sectors[address];
+
+ /* printf("Found block %u\n", next_vobu); */
+
+ if(vobu_start <= seekto_block &&
+ next_vobu > seekto_block) {
+ found = 1;
+ } else {
+ vobu_start = next_vobu;
+ }
+
+ address ++;
+ }
+ if(found) {
+ *vobu = vobu_start;
+ return S_OK;
+ } else {
+ fprintf(stderr,"Could not locate block\n");
+ return S_ERR;
+ }
+ }
+ fprintf(stderr,"admap not located\n");
+ return S_ERR;
+}
+
+dvdnav_status_t dvdnav_sector_search(dvdnav_t *this,
+ unsigned long int offset, int origin) {
+/* FIXME: Implement */
+
+ uint32_t target = 0;
+ uint32_t length = 0;
+ uint32_t first_cell_nr, last_cell_nr, cell_nr, fnd_cell_nr;
+ int found;
+ cell_playback_t *cell, *fnd_cell;
+ dvd_state_t *state;
+ dvdnav_status_t result;
+
+ if((!this) || (!this->vm) )
+ return -1;
+
+ state = &(this->vm->state);
+ if((!state) || (!state->pgc) )
+ return -1;
+
+ if(offset == 0)
+ return -1;
+
+ if(this->position_current.still != 0)
+ /* Cannot do seeking in a still frame. */
+ return -1;
+
+ pthread_mutex_lock(&this->vm_lock);
+ result = dvdnav_get_position(this, &target, &length);
+ fprintf(stderr,"FIXME: seeking to offset=%lu pos=%u length=%u\n", offset, target, length);
+ fprintf(stderr,"FIXME: Before cellN=%u blockN=%u\n" ,
+ state->cellN,
+ state->blockN);
+ if(!result) {
+ pthread_mutex_unlock(&this->vm_lock);
+ return -1;
+ }
+
+ switch(origin) {
+ case SEEK_SET:
+ if(offset > length) {
+ pthread_mutex_unlock(&this->vm_lock);
+ return -1;
+ }
+ target = offset;
+ break;
+ case SEEK_CUR:
+ if(target + offset > length) {
+ pthread_mutex_unlock(&this->vm_lock);
+ return -1;
+ }
+ target += offset;
+ break;
+ case SEEK_END:
+ if(length - offset < 0) {
+ pthread_mutex_unlock(&this->vm_lock);
+ return -1;
+ }
+ target = length - offset;
+ default:
+ /* Error occured */
+ pthread_mutex_unlock(&this->vm_lock);
+ return -1;
+ }
+
+ /* First find closest cell number in program */
+ first_cell_nr = state->pgc->program_map[state->pgN-1];
+ if(state->pgN < state->pgc->nr_of_programs) {
+ last_cell_nr = state->pgc->program_map[state->pgN] - 1;
+ } else {
+ last_cell_nr = state->pgc->nr_of_cells;
+ }
+
+ found = 0; target += state->pgc->cell_playback[first_cell_nr-1].first_sector;
+ fnd_cell_nr = last_cell_nr + 1;
+ for(cell_nr = first_cell_nr; (cell_nr <= last_cell_nr) && !found; cell_nr ++) {
+ cell = &(state->pgc->cell_playback[cell_nr-1]);
+ if((cell->first_sector <= target) && (cell->last_sector >= target)) {
+ state->cellN = cell_nr;
+ state->blockN = 0;
+ found = 1;
+ fnd_cell_nr = cell_nr;
+ fnd_cell = cell;
+ }
+ }
+
+ if(fnd_cell_nr <= last_cell_nr) {
+ int32_t vobu, start;
+ dvdnav_status_t status;
+ fprintf(stderr,"Seeking to cell %i from choice of %i to %i\n",
+ fnd_cell_nr, first_cell_nr, last_cell_nr);
+ status = dvdnav_scan_admap(this, state->domain, target, &vobu);
+ /*
+ * Clut does not actually change,
+ * but as the decoders have been closed then opened,
+ * A new clut has to be sent.
+ */
+ start =(state->pgc->cell_playback[state->cellN - 1].first_sector);
+ fprintf(stderr,"FIXME: After cellN=%u blockN=%u target=%x vobu=%x start=%x\n" ,
+ state->cellN,
+ state->blockN,
+ target,
+ vobu,
+ start);
+ state->blockN = vobu - start;
+ fprintf(stderr,"FIXME: After vobu=%x start=%x blockN=%x\n" ,
+ vobu,
+ start,
+ state->blockN);
+ pthread_mutex_unlock(&this->vm_lock);
+ return target;
+ } else {
+ fprintf(stderr, "Error when seeking, asked to seek outside program\n");
+ }
+
+ fprintf(stderr,"FIXME: Implement seeking to location %u\n", target);
+
+ pthread_mutex_unlock(&this->vm_lock);
+ return -1;
+}
+
+dvdnav_status_t dvdnav_part_search(dvdnav_t *this, int part) {
+
+ if((!this) || (!this->vm) )
+ return S_ERR;
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_prev_pg_search(dvdnav_t *this) {
+ dvd_state_t *state;
+
+ if((!this) || (!this->vm) )
+ return S_ERR;
+
+ state = &(this->vm->state);
+ if((!state) || (!state->pgc) )
+ return S_ERR;
+
+ /* Make sure this is not the first chapter */
+ if(state->pgN <= 1 ) {
+ fprintf(stderr,"dvdnav: at first chapter. prev chapter failed.\n");
+ return S_ERR;
+ }
+ fprintf(stderr,"dvdnav: previous chapter\n");
+ vm_jump_prog(this->vm, state->pgN - 1);
+ this->vm->hop_channel++;
+ fprintf(stderr,"dvdnav: previous chapter done\n");
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_top_pg_search(dvdnav_t *this) {
+
+ if((!this) || (!this->vm) )
+ return S_ERR;
+
+ fprintf(stderr,"dvdnav: top chapter. NOP.\n");
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *this) {
+ dvd_state_t *state;
+
+ if((!this) || (!this->vm) )
+ return S_ERR;
+
+ state = &(this->vm->state);
+ if((!state) || (!state->pgc) )
+ return S_ERR;
+
+ /* Make sure this is not the last chapter */
+ if(state->pgN >= state->pgc->nr_of_programs) {
+ fprintf(stderr,"dvdnav: at last chapter. next chapter failed.\n");
+ return S_ERR;
+ }
+ fprintf(stderr,"dvdnav: next chapter\n");
+ vm_jump_prog(this->vm, state->pgN + 1);
+ this->vm->hop_channel++;
+ fprintf(stderr,"dvdnav: next chapter done\n");
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_menu_call(dvdnav_t *this, DVDMenuID_t menu) {
+ dvd_state_t *state;
+
+ if((!this) || (!this->vm) )
+ return S_ERR;
+
+ pthread_mutex_lock(&this->vm_lock);
+ state = &(this->vm->state);
+ if (vm_menu_call(this->vm, menu, 0))
+ this->vm->hop_channel++;
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_current_title_info(dvdnav_t *this, int *tt, int *pr) {
+int vts_ttn = 0;
+ int vts, i;
+ domain_t domain;
+ tt_srpt_t* srpt;
+
+ if(!this)
+ return S_ERR;
+
+ if(!tt || !pr) {
+ printerr("Passed a NULL pointer");
+ }
+
+ if(tt)
+ *tt = -1;
+ if(*pr)
+ *pr = -1;
+
+ domain = this->vm->state.domain;
+ if((domain == FP_DOMAIN) || (domain == VMGM_DOMAIN)) {
+ /* Not in a title */
+ return S_OK;
+ }
+
+ vts_ttn = this->vm->state.VTS_TTN_REG;
+ vts = this->vm->state.vtsN;
+
+ if(pr) {
+ *pr = this->vm->state.pgN;
+ }
+
+ /* Search TT_SRPT for title */
+ if(!(vm_get_vmgi(this->vm))) {
+ printerr("Oh poo, no SRPT");
+ return S_ERR;
+ }
+
+ srpt = vm_get_vmgi(this->vm)->tt_srpt;
+ for(i=0; i<srpt->nr_of_srpts; i++) {
+ title_info_t* info = &(srpt->title[i]);
+ if((info->title_set_nr == vts) && (info->vts_ttn == vts_ttn)) {
+ if(tt)
+ *tt = i+1;
+ }
+ }
+
+ return S_OK;
+}
+
+static char __title_str[] = "DVDNAV";
+
+dvdnav_status_t dvdnav_get_title_string(dvdnav_t *this, char **title_str) {
+ if(!this)
+ return S_ERR;
+
+ if(!title_str) {
+ printerr("Passed a NULL pointer");
+ return S_ERR;
+ }
+
+ (*title_str) = __title_str;
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_get_position(dvdnav_t *this, unsigned int* pos,
+ unsigned int *len) {
+ uint32_t cur_sector;
+ uint32_t first_cell_nr;
+ uint32_t last_cell_nr;
+ cell_playback_t *first_cell;
+ cell_playback_t *last_cell;
+ dvd_state_t *state;
+ if((!this) || (!this->vm) )
+ return 0;
+
+ state = &(this->vm->state);
+ if((!state) || (!state->pgc) )
+ return 0;
+
+ /* Sanity check */
+ if(state->pgN > state->pgc->nr_of_programs) {
+ return 0;
+ }
+
+ /* Get current sector */
+ cur_sector = this->vobu.vobu_start + this->vobu.blockN;
+
+ /* Find start cell of program. */
+ first_cell_nr = state->pgc->program_map[state->pgN-1];
+ first_cell = &(state->pgc->cell_playback[first_cell_nr-1]);
+ if(state->pgN < state->pgc->nr_of_programs) {
+ last_cell_nr = state->pgc->program_map[state->pgN] - 1;
+ } else {
+ last_cell_nr = state->pgc->nr_of_cells;
+ }
+ last_cell = &(state->pgc->cell_playback[last_cell_nr-1]);
+
+ *pos= cur_sector - first_cell->first_sector;
+ *len= last_cell->last_sector - first_cell->first_sector;
+ /* printf("dvdnav:searching:current pos=%u length=%u\n",*pos,*len); */
+
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_get_position_in_title(dvdnav_t *this,
+ unsigned int *pos,
+ unsigned int *len) {
+ uint32_t cur_sector;
+ uint32_t first_cell_nr;
+ uint32_t last_cell_nr;
+ cell_playback_t *first_cell;
+ cell_playback_t *last_cell;
+ dvd_state_t *state;
+ if((!this) || (!this->vm) )
+ return S_ERR;
+
+ state = &(this->vm->state);
+ if((!state) || (!state->pgc) )
+ return S_ERR;
+
+ /* Sanity check */
+ if(state->pgN > state->pgc->nr_of_programs) {
+ return S_ERR;
+ }
+
+ /* Get current sector */
+ cur_sector = this->vobu.vobu_start + this->vobu.blockN;
+
+ /* Now find first and last cells in title. */
+ first_cell_nr = state->pgc->program_map[0];
+ first_cell = &(state->pgc->cell_playback[first_cell_nr-1]);
+ last_cell_nr = state->pgc->nr_of_cells;
+ last_cell = &(state->pgc->cell_playback[last_cell_nr-1]);
+
+ *pos = cur_sector - first_cell->first_sector;
+ *len = last_cell->last_sector - first_cell->first_sector;
+
+ return S_OK;
+}
+
+
diff --git a/src/input/libdvdnav/settings.c b/src/input/libdvdnav/settings.c
new file mode 100644
index 000000000..a9a5df45e
--- /dev/null
+++ b/src/input/libdvdnav/settings.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library.
+ *
+ * libdvdnav 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.
+ *
+ * libdvdnav 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
+ *
+ * $Id: settings.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <dvdnav.h>
+#include "dvdnav_internal.h"
+
+#include "vm.h"
+
+/* Characteristics/setting API calls */
+
+dvdnav_status_t dvdnav_get_region_mask(dvdnav_t *this, int *region) {
+ if(!this)
+ return S_ERR;
+
+ if(!region) {
+ printerr("Passed a NULL pointer");
+ return S_ERR;
+ }
+
+ if(!this->vm) {
+ printerr("VM not yet initialised");
+ return S_ERR;
+ }
+
+ (*region) = this->vm->state.registers.SPRM[20];
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_set_region_mask(dvdnav_t *this, int mask) {
+ if(!this)
+ return S_ERR;
+
+ if(!this->vm) {
+ printerr("VM not yet initialised");
+ return S_ERR;
+ }
+
+ this->vm->state.registers.SPRM[20] = (mask & 0xff);
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_set_readahead_flag(dvdnav_t *this, int use_readahead) {
+ if(!this)
+ return S_ERR;
+
+ this->use_read_ahead = use_readahead;
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_get_readahead_flag(dvdnav_t *this, int* flag) {
+ if(!this)
+ return S_ERR;
+
+ if(!flag) {
+ printerr("Passed a NULL pointer");
+ return S_ERR;
+ }
+
+ (*flag) = this->use_read_ahead;
+ return S_OK;
+}
+
+static dvdnav_status_t set_language_register(dvdnav_t *this, char *code, int reg) {
+ if(!this)
+ return S_ERR;
+
+ if(!code[0] || !code[1]) {
+ printerr("Passed illegal language code");
+ return S_ERR;
+ }
+
+ if(!this->vm) {
+ printerr("VM not yet initialised");
+ return S_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ this->vm->state.registers.SPRM[reg] = (code[0] << 8) | code[1];
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_menu_language_select(dvdnav_t *this, char *code) {
+ return set_language_register(this, code, 0);
+}
+
+dvdnav_status_t dvdnav_audio_language_select(dvdnav_t *this, char *code) {
+ return set_language_register(this, code, 16);
+}
+
+dvdnav_status_t dvdnav_spu_language_select(dvdnav_t *this, char *code) {
+ return set_language_register(this, code, 18);
+}
diff --git a/src/input/libdvdnav/vm.c b/src/input/libdvdnav/vm.c
new file mode 100644
index 000000000..ec4ac75fe
--- /dev/null
+++ b/src/input/libdvdnav/vm.c
@@ -0,0 +1,1905 @@
+/*
+ * Copyright (C) 2000, 2001 Håkan Hjort
+ * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav 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.
+ *
+ * libdvdnav 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
+ *
+ * $Id: vm.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include <dvdread/ifo_types.h>
+#include <dvdread/ifo_read.h>
+
+#include "decoder.h"
+#include "vmcmd.h"
+#include "vm.h"
+#include "dvdnav_internal.h"
+
+/* Local prototypes */
+
+static void saveRSMinfo(vm_t *vm,int cellN, int blockN);
+static link_t play_PGC(vm_t *vm);
+static link_t play_PGC_PG(vm_t *vm, int pgN);
+static link_t play_PGC_post(vm_t *vm);
+static link_t play_PG(vm_t *vm);
+static link_t play_Cell(vm_t *vm);
+static link_t play_Cell_post(vm_t *vm);
+static link_t process_command(vm_t *vm,link_t link_values);
+
+static void ifoOpenNewVTSI(vm_t *vm,dvd_reader_t *dvd, int vtsN);
+static pgcit_t* get_PGCIT(vm_t *vm);
+
+/* get_XYZ returns a value.
+ * ser_XYZ sets state using passed parameters.
+ * returns success/failure.
+ */
+
+/* Can only be called when in VTS_DOMAIN */
+static int set_FP_PGC(vm_t *vm); /* FP */
+static int set_TT(vm_t *vm,int tt);
+static int set_VTS_TT(vm_t *vm,int vtsN, int vts_ttn);
+static int set_VTS_PTT(vm_t *vm,int vtsN, int vts_ttn, int part);
+
+static int set_MENU(vm_t *vm,int menu); /* VTSM & VMGM */
+
+/* Called in any domain */
+static int get_TT(vm_t *vm, int vtsN, int vts_ttn);
+static int get_ID(vm_t *vm,int id);
+static int get_PGCN(vm_t *vm);
+static int set_PGN(vm_t *vm); /* Set PGN based on (vm->state).CellN */
+static int set_PGC(vm_t *vm,int pgcN);
+
+/* Initialisation */
+
+vm_t* vm_new_vm() {
+ vm_t *vm = (vm_t*)calloc(sizeof(vm_t), sizeof(char));
+
+ return vm;
+}
+
+static void vm_print_current_domain_state(vm_t *vm) {
+ switch((vm->state).domain) {
+ case VTS_DOMAIN:
+ fprintf(stderr, "Video Title Domain: -\n");
+ break;
+
+ case VTSM_DOMAIN:
+ fprintf(stderr, "Video Title Menu Domain: -\n");
+ break;
+
+ case VMGM_DOMAIN:
+ fprintf(stderr, "Video Manager Menu Domain: -\n");
+ break;
+
+ case FP_DOMAIN:
+ fprintf(stderr, "First Play Domain: -\n");
+ break;
+
+ default:
+ fprintf(stderr, "Unknown Domain: -\n");
+ break;
+ }
+ fprintf(stderr, "VTS:%d PG:%u CELL:%u BLOCK:%u VTS_TTN:%u TTN:%u TT_PGCN:%u\n",
+ (vm->state).vtsN,
+ (vm->state).pgN,
+ (vm->state).cellN,
+ (vm->state).blockN,
+ (vm->state).VTS_TTN_REG,
+ (vm->state).TTN_REG,
+ (vm->state).TT_PGCN_REG);
+}
+
+void vm_stop(vm_t *vm) {
+ if(!vm)
+ return;
+
+ if(vm->vmgi) {
+ ifoClose(vm->vmgi);
+ vm->vmgi=NULL;
+ }
+
+ if(vm->vtsi) {
+ ifoClose(vm->vtsi);
+ vm->vmgi=NULL;
+ }
+
+ if(vm->dvd) {
+ DVDClose(vm->dvd);
+ vm->dvd=NULL;
+ }
+}
+
+void vm_free_vm(vm_t *vm) {
+ if(vm) {
+ vm_stop(vm);
+ free(vm);
+ }
+}
+
+/* IFO Access */
+
+ifo_handle_t *vm_get_vmgi(vm_t *vm) {
+ if(!vm)
+ return NULL;
+
+ return vm->vmgi;
+}
+
+ifo_handle_t *vm_get_vtsi(vm_t *vm) {
+ if(!vm)
+ return NULL;
+
+ return vm->vtsi;
+}
+
+/* Reader Access */
+
+dvd_reader_t *vm_get_dvd_reader(vm_t *vm) {
+ if(!vm)
+ return NULL;
+
+ return vm->dvd;
+}
+
+int vm_reset(vm_t *vm, char *dvdroot) /* , register_t regs) */ {
+ /* Setup State */
+ memset((vm->state).registers.SPRM, 0, sizeof(uint16_t)*24);
+ memset((vm->state).registers.GPRM, 0, sizeof((vm->state).registers.GPRM));
+ memset((vm->state).registers.GPRM_mode, 0, sizeof((vm->state).registers.GPRM_mode));
+ memset((vm->state).registers.GPRM_mode, 0, sizeof((vm->state).registers.GPRM_mode));
+ memset((vm->state).registers.GPRM_time, 0, sizeof((vm->state).registers.GPRM_time));
+ (vm->state).registers.SPRM[0] = ('e'<<8)|'n'; /* Player Menu Languange code */
+ (vm->state).AST_REG = 15; /* 15 why? */
+ (vm->state).SPST_REG = 62; /* 62 why? */
+ (vm->state).AGL_REG = 1;
+ (vm->state).TTN_REG = 1;
+ (vm->state).VTS_TTN_REG = 1;
+ /* (vm->state).TT_PGCN_REG = 0 */
+ (vm->state).PTTN_REG = 1;
+ (vm->state).HL_BTNN_REG = 1 << 10;
+
+ (vm->state).PTL_REG = 15; /* Parental Level */
+ (vm->state).registers.SPRM[12] = ('U'<<8)|'S'; /* Parental Management Country Code */
+ (vm->state).registers.SPRM[16] = ('e'<<8)|'n'; /* Initial Language Code for Audio */
+ (vm->state).registers.SPRM[18] = ('e'<<8)|'n'; /* Initial Language Code for Spu */
+ /* Player Regional Code Mask.
+ * bit0 = Region 1
+ * bit1 = Region 2
+ */
+ (vm->state).registers.SPRM[20] = 0x1; /* Player Regional Code Mask. Region free! */
+ (vm->state).registers.SPRM[14] = 0x100; /* Try Pan&Scan */
+
+ (vm->state).pgN = 0;
+ (vm->state).cellN = 0;
+ (vm->state).cell_restart = 0;
+
+ (vm->state).domain = FP_DOMAIN;
+ (vm->state).rsm_vtsN = 0;
+ (vm->state).rsm_cellN = 0;
+ (vm->state).rsm_blockN = 0;
+
+ (vm->state).vtsN = -1;
+
+ if (vm->dvd && dvdroot) {
+ /* a new dvd device has been requested */
+ vm_stop(vm);
+ }
+ if (!vm->dvd) {
+ vm->dvd = DVDOpen(dvdroot);
+ if(!vm->dvd) {
+ fprintf(stderr, "vm: faild to open/read the DVD\n");
+ return -1;
+ }
+
+ vm->vmgi = ifoOpenVMGI(vm->dvd);
+ if(!vm->vmgi) {
+ fprintf(stderr, "vm: faild to read VIDEO_TS.IFO\n");
+ return -1;
+ }
+ if(!ifoRead_FP_PGC(vm->vmgi)) {
+ fprintf(stderr, "vm: ifoRead_FP_PGC failed\n");
+ return -1;
+ }
+ if(!ifoRead_TT_SRPT(vm->vmgi)) {
+ fprintf(stderr, "vm: ifoRead_TT_SRPT failed\n");
+ return -1;
+ }
+ if(!ifoRead_PGCI_UT(vm->vmgi)) {
+ fprintf(stderr, "vm: ifoRead_PGCI_UT failed\n");
+ return -1;
+ }
+ if(!ifoRead_PTL_MAIT(vm->vmgi)) {
+ fprintf(stderr, "vm: ifoRead_PTL_MAIT failed\n");
+ ; /* return -1; Not really used for now.. */
+ }
+ if(!ifoRead_VTS_ATRT(vm->vmgi)) {
+ fprintf(stderr, "vm: ifoRead_VTS_ATRT failed\n");
+ ; /* return -1; Not really used for now.. */
+ }
+ if(!ifoRead_VOBU_ADMAP(vm->vmgi)) {
+ fprintf(stderr, "vm: ifoRead_VOBU_ADMAP vgmi failed\n");
+ ; /* return -1; Not really used for now.. */
+ }
+ /* ifoRead_TXTDT_MGI(vmgi); Not implemented yet */
+ }
+ else fprintf(stderr, "vm: reset\n");
+ if (vm->vmgi) {
+ fprintf(stderr, "DVD disk reports itself with Region mask 0x%08x. Maybe region %u.\n",
+ vm->vmgi->vmgi_mat->vmg_category,
+ (((vm->vmgi->vmgi_mat->vmg_category >> 16) ^ 0xff) & 0xff) );
+ }
+ return 0;
+}
+
+/* FIXME TODO XXX $$$ Handle error condition too... */
+int vm_start(vm_t *vm)
+{
+ link_t link_values;
+
+ /* Set pgc to FP(First Play) pgc */
+ set_FP_PGC(vm);
+ link_values = play_PGC(vm);
+ link_values = process_command(vm,link_values);
+ assert(link_values.command == PlayThis);
+ (vm->state).blockN = link_values.data1;
+#ifdef TRACE
+ fprintf(stderr, "vm_start: blockN set to 0x%x\n", (vm->state).blockN);
+#endif
+ assert( (vm->state).blockN == 0 );
+
+
+ return 0; /* ?? */
+}
+
+int vm_position_get(vm_t *vm, vm_position_t *position) {
+ position->button = (vm->state).HL_BTNN_REG >> 10;
+ position->spu_channel = (vm->state).SPST_REG;
+ position->audio_channel = (vm->state).AST_REG;
+ position->angle_channel = (vm->state).AGL_REG;
+ position->hop_channel = vm->hop_channel; /* Increases by one on each hop */
+ position->vts = (vm->state).vtsN;
+ position->domain = (vm->state).domain;
+ position->cell = (vm->state).cellN;
+ position->cell_restart = (vm->state).cell_restart;
+ position->still = (vm->state).pgc->cell_playback[(vm->state).cellN - 1].still_time;
+ position->vobu_start = (vm->state).pgc->cell_playback[(vm->state).cellN - 1].first_sector;
+ position->vobu_next = (vm->state).blockN;
+ /* position->vobu_next = 0; Just for now */
+ return 1;
+}
+
+int vm_position_print(vm_t *vm, vm_position_t *position) {
+ fprintf(stderr, "But=%x Spu=%x Aud=%x Ang=%x Hop=%x vts=%x dom=%x cell=%x cell_restart=%x still=%x start=%x next=%x\n",
+ position->button,
+ position->spu_channel,
+ position->audio_channel,
+ position->angle_channel,
+ position->hop_channel,
+ position->vts,
+ position->domain,
+ position->cell,
+ position->cell_restart,
+ position->still,
+ position->vobu_start,
+ position->vobu_next);
+ return 1;
+}
+
+
+int vm_start_title(vm_t *vm, int tt) {
+ link_t link_values;
+
+ set_TT(vm, tt);
+ link_values = play_PGC(vm);
+ link_values = process_command(vm, link_values);
+ assert(link_values.command == PlayThis);
+ (vm->state).blockN = link_values.data1;
+#ifdef TRACE
+ fprintf(stderr, "vm_start_title: blockN set to 0x%x\n", (vm->state).blockN);
+#endif
+ assert( (vm->state).blockN == 0 );
+
+ return 0; /* ?? */
+}
+
+int vm_jump_prog(vm_t *vm, int pr) {
+ link_t link_values;
+
+
+ set_PGC(vm, get_PGCN(vm));
+ (vm->state).pgN = pr; /* ?? set_PGC() clobbers pgN */
+ link_values = play_PG(vm);
+ link_values = process_command(vm, link_values);
+ assert(link_values.command == PlayThis);
+ (vm->state).blockN = link_values.data1;
+#ifdef TRACE
+ fprintf(stderr, "vm_jump_prog: blockN set to 0x%x\n", (vm->state).blockN);
+#endif
+ assert( (vm->state).blockN == 0 );
+
+ return 0; /* ?? */
+}
+
+int vm_eval_cmd(vm_t *vm, vm_cmd_t *cmd)
+{
+ link_t link_values;
+
+ if(vmEval_CMD(cmd, 1, &(vm->state).registers, &link_values)) {
+ link_values = process_command(vm, link_values);
+ assert(link_values.command == PlayThis);
+ (vm->state).blockN = link_values.data1;
+#ifdef TRACE
+ fprintf(stderr, "vm_eval_cmd: blockN set to 0x%x\n", (vm->state).blockN);
+#endif
+ assert( (vm->state).blockN == 0 );
+ return 1; /* Something changed, Jump */
+ } else {
+ return 0; /* It updated some state thats all... */
+ }
+}
+
+int vm_get_next_cell(vm_t *vm)
+{
+ link_t link_values;
+ link_values = play_Cell_post(vm);
+ link_values = process_command(vm,link_values);
+ assert(link_values.command == PlayThis);
+ (vm->state).blockN = link_values.data1;
+#ifdef TRACE
+ fprintf(stderr, "vm_get_next_cell: blockN set to 0x%x\n", (vm->state).blockN);
+#endif
+ assert( (vm->state).blockN == 0 );
+
+ return 0; /* ?? */
+}
+
+int vm_top_pg(vm_t *vm)
+{
+ link_t link_values;
+ link_values = play_PG(vm);
+ link_values = process_command(vm,link_values);
+ assert(link_values.command == PlayThis);
+ (vm->state).blockN = link_values.data1;
+#ifdef TRACE
+ fprintf(stderr, "vm_top_pg: blockN set to 0x%x\n", (vm->state).blockN);
+#endif
+ assert( (vm->state).blockN == 0 );
+
+ return 1; /* Jump */
+}
+
+int vm_go_up(vm_t *vm)
+{
+ link_t link_values;
+
+ if(set_PGC(vm, (vm->state).pgc->goup_pgc_nr))
+ assert(0);
+
+ link_values = play_PGC(vm);
+ link_values = process_command(vm,link_values);
+ assert(link_values.command == PlayThis);
+ (vm->state).blockN = link_values.data1;
+#ifdef TRACE
+ fprintf(stderr, "vm_go_up: blockN set to 0x%x\n", (vm->state).blockN);
+#endif
+ assert( (vm->state).blockN == 0 );
+
+ return 1; /* Jump */
+}
+
+int vm_next_pg(vm_t *vm)
+{
+ /* Do we need to get a updated pgN value first? */
+ (vm->state).pgN += 1;
+ return vm_top_pg(vm);
+}
+
+int vm_prev_pg(vm_t *vm)
+{
+ /* Do we need to get a updated pgN value first? */
+ (vm->state).pgN -= 1;
+ if((vm->state).pgN == 0) {
+ /* Check for previous PGCN ? */
+ (vm->state).pgN = 1;
+ /* return 0; */
+ }
+ return vm_top_pg(vm);
+}
+
+
+static domain_t menuid2domain(DVDMenuID_t menuid)
+{
+ domain_t result = VTSM_DOMAIN; /* Really shouldn't have to.. */
+
+ switch(menuid) {
+ case DVD_MENU_Title:
+ result = VMGM_DOMAIN;
+ break;
+ case DVD_MENU_Root:
+ case DVD_MENU_Subpicture:
+ case DVD_MENU_Audio:
+ case DVD_MENU_Angle:
+ case DVD_MENU_Part:
+ result = VTSM_DOMAIN;
+ break;
+ }
+
+ return result;
+}
+
+int vm_menu_call(vm_t *vm, DVDMenuID_t menuid, int block)
+{
+ domain_t old_domain;
+ link_t link_values;
+
+ /* Should check if we are allowed/can acces this menu */
+
+
+ /* FIXME XXX $$$ How much state needs to be restored
+ * when we fail to find a menu? */
+
+ old_domain = (vm->state).domain;
+
+ switch((vm->state).domain) {
+ case VTS_DOMAIN:
+ saveRSMinfo(vm, 0, block);
+ /* FALL THROUGH */
+ case VTSM_DOMAIN:
+ case VMGM_DOMAIN:
+ (vm->state).domain = menuid2domain(menuid);
+ if(get_PGCIT(vm) != NULL && set_MENU(vm, menuid) != -1) {
+ link_values = play_PGC(vm);
+ link_values = process_command(vm, link_values);
+ assert(link_values.command == PlayThis);
+ (vm->state).blockN = link_values.data1;
+#ifdef TRACE
+ fprintf(stderr, "vm_menu_call: blockN set to 0x%x\n", (vm->state).blockN);
+#endif
+ assert( (vm->state).blockN == 0 );
+ return 1; /* Jump */
+ } else {
+ (vm->state).domain = old_domain;
+ }
+ break;
+ case FP_DOMAIN: /* FIXME XXX $$$ What should we do here? */
+ break;
+ }
+
+ return 0;
+}
+
+
+int vm_resume(vm_t *vm)
+{
+ int i;
+ link_t link_values;
+
+ /* Check and see if there is any rsm info!! */
+ if((vm->state).rsm_vtsN == 0) {
+ return 0;
+ }
+
+ (vm->state).domain = VTS_DOMAIN;
+ ifoOpenNewVTSI(vm, vm->dvd, (vm->state).rsm_vtsN);
+ set_PGC(vm, (vm->state).rsm_pgcN);
+
+ /* These should never be set in SystemSpace and/or MenuSpace */
+ /* (vm->state).TTN_REG = (vm->state).rsm_tt; */
+ /* (vm->state).TT_PGCN_REG = (vm->state).rsm_pgcN; */
+ /* (vm->state).HL_BTNN_REG = (vm->state).rsm_btnn; */
+ for(i = 0; i < 5; i++) {
+ (vm->state).registers.SPRM[4 + i] = (vm->state).rsm_regs[i];
+ }
+
+ if((vm->state).rsm_cellN == 0) {
+ assert((vm->state).cellN); /* Checking if this ever happens */
+ (vm->state).pgN = 1;
+ link_values = play_PG(vm);
+ link_values = process_command(vm, link_values);
+ assert(link_values.command == PlayThis);
+ (vm->state).blockN = link_values.data1;
+#ifdef TRACE
+ fprintf(stderr, "vm_resume1: blockN set to 0x%x\n", (vm->state).blockN);
+#endif
+ assert( (vm->state).blockN == 0 );
+ } else {
+ (vm->state).cellN = (vm->state).rsm_cellN;
+ (vm->state).blockN = (vm->state).rsm_blockN;
+#ifdef TRACE
+ fprintf(stderr, "vm_resume2: blockN set to 0x%x\n", (vm->state).blockN);
+#endif
+ /* (vm->state).pgN = ?? does this gets the righ value in play_Cell, no! */
+ if(set_PGN(vm)) {
+ /* Were at or past the end of the PGC, should not happen for a RSM */
+ assert(0);
+ play_PGC_post(vm);
+ }
+ }
+
+ return 1; /* Jump */
+}
+
+/**
+ * Return the substream id for 'logical' audio stream audioN.
+ * 0 <= audioN < 8
+ */
+int vm_get_audio_stream(vm_t *vm, int audioN)
+{
+ int streamN = -1;
+#ifdef TRACE
+ fprintf(stderr,"dvdnav:vm.c:get_audio_stream audioN=%d\n",audioN);
+#endif
+ if((vm->state).domain == VTSM_DOMAIN
+ || (vm->state).domain == VMGM_DOMAIN
+ || (vm->state).domain == FP_DOMAIN) {
+ audioN = 0;
+ }
+
+ if(audioN < 8) {
+ /* Is there any contol info for this logical stream */
+ if((vm->state).pgc->audio_control[audioN] & (1<<15)) {
+ streamN = ((vm->state).pgc->audio_control[audioN] >> 8) & 0x07;
+ }
+ }
+
+ if((vm->state).domain == VTSM_DOMAIN
+ || (vm->state).domain == VMGM_DOMAIN
+ || (vm->state).domain == FP_DOMAIN) {
+ if(streamN == -1)
+ streamN = 0;
+ }
+
+ /* Should also check in vtsi/vmgi status that what kind of stream
+ * it is (ac3/lpcm/dts/sdds...) to find the right (sub)stream id */
+ return streamN;
+}
+
+/**
+ * Return the substream id for 'logical' subpicture stream subpN and given mode.
+ * 0 <= subpN < 32
+ * mode == 0 - widescreen
+ * mode == 1 - letterbox
+ * mode == 2 - pan&scan
+ */
+int vm_get_subp_stream(vm_t *vm, int subpN, int mode)
+{
+ int streamN = -1;
+ int source_aspect = vm_get_video_aspect(vm);
+
+ if((vm->state).domain == VTSM_DOMAIN
+ || (vm->state).domain == VMGM_DOMAIN
+ || (vm->state).domain == FP_DOMAIN) {
+ subpN = 0;
+ }
+
+ if(subpN < 32) { /* a valid logical stream */
+ /* Is this logical stream present */
+ if((vm->state).pgc->subp_control[subpN] & (1<<31)) {
+ if(source_aspect == 0) /* 4:3 */
+ streamN = ((vm->state).pgc->subp_control[subpN] >> 24) & 0x1f;
+ if(source_aspect == 3) /* 16:9 */
+ switch (mode) {
+ case 0:
+ streamN = ((vm->state).pgc->subp_control[subpN] >> 16) & 0x1f;
+ break;
+ case 1:
+ streamN = ((vm->state).pgc->subp_control[subpN] >> 8) & 0x1f;
+ break;
+ case 2:
+ streamN = (vm->state).pgc->subp_control[subpN] & 0x1f;
+ }
+ }
+ }
+
+ /* Paranoia.. if no stream select 0 anyway */
+/* I am not paranoid */
+/* if((vm->state).domain == VTSM_DOMAIN
+ || (vm->state).domain == VMGM_DOMAIN
+ || (vm->state).domain == FP_DOMAIN) {
+ if(streamN == -1)
+ streamN = 0;
+ }
+*/
+ /* Should also check in vtsi/vmgi status that what kind of stream it is. */
+ return streamN;
+}
+
+int vm_get_subp_active_stream(vm_t *vm, int mode)
+{
+ int subpN;
+ int streamN;
+ subpN = (vm->state).SPST_REG & ~0x40;
+ streamN = vm_get_subp_stream(vm, subpN, mode);
+
+ /* If no such stream, then select the first one that exists. */
+ if(streamN == -1) {
+ for(subpN = 0; subpN < 32; subpN++) {
+ if((vm->state).pgc->subp_control[subpN] & (1<<31)) {
+
+ streamN = vm_get_subp_stream(vm, subpN, mode);
+ break;
+ }
+ }
+ }
+
+ /* We should instead send the on/off status to the spudecoder / mixer */
+ /* If we are in the title domain see if the spu mixing is on */
+ if((vm->state).domain == VTS_DOMAIN && !((vm->state).SPST_REG & 0x40)) {
+ /* Bit 7 set means hide, and only let Forced display show */
+ return (streamN | 0x80);
+ } else {
+ return streamN;
+ }
+}
+
+int vm_get_audio_active_stream(vm_t *vm)
+{
+ int audioN;
+ int streamN;
+ audioN = (vm->state).AST_REG ;
+ streamN = vm_get_audio_stream(vm, audioN);
+
+ /* If no such stream, then select the first one that exists. */
+ if(streamN == -1) {
+ for(audioN = 0; audioN < 8; audioN++) {
+ if((vm->state).pgc->audio_control[audioN] & (1<<15)) {
+ streamN = vm_get_audio_stream(vm, audioN);
+ break;
+ }
+ }
+ }
+
+ return streamN;
+}
+
+
+void vm_get_angle_info(vm_t *vm, int *num_avail, int *current)
+{
+ *num_avail = 1;
+ *current = 1;
+
+ if((vm->state).domain == VTS_DOMAIN) {
+ /* TTN_REG does not allways point to the correct title.. */
+ title_info_t *title;
+ if((vm->state).TTN_REG > vm->vmgi->tt_srpt->nr_of_srpts)
+ return;
+ title = &vm->vmgi->tt_srpt->title[(vm->state).TTN_REG - 1];
+ if(title->title_set_nr != (vm->state).vtsN ||
+ title->vts_ttn != (vm->state).VTS_TTN_REG)
+ return;
+ *num_avail = title->nr_of_angles;
+ *current = (vm->state).AGL_REG;
+ if(*current > *num_avail) /* Is this really a good idea? */
+ *current = *num_avail;
+ }
+}
+
+
+void vm_get_audio_info(vm_t *vm, int *num_avail, int *current)
+{
+ if((vm->state).domain == VTS_DOMAIN) {
+ *num_avail = vm->vtsi->vtsi_mat->nr_of_vts_audio_streams;
+ *current = (vm->state).AST_REG;
+ } else if((vm->state).domain == VTSM_DOMAIN) {
+ *num_avail = vm->vtsi->vtsi_mat->nr_of_vtsm_audio_streams; /* 1 */
+ *current = 1;
+ } else if((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN) {
+ *num_avail = vm->vmgi->vmgi_mat->nr_of_vmgm_audio_streams; /* 1 */
+ *current = 1;
+ }
+}
+
+void vm_get_subp_info(vm_t *vm, int *num_avail, int *current)
+{
+ if((vm->state).domain == VTS_DOMAIN) {
+ *num_avail = vm->vtsi->vtsi_mat->nr_of_vts_subp_streams;
+ *current = (vm->state).SPST_REG;
+ } else if((vm->state).domain == VTSM_DOMAIN) {
+ *num_avail = vm->vtsi->vtsi_mat->nr_of_vtsm_subp_streams; /* 1 */
+ *current = 0x41;
+ } else if((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN) {
+ *num_avail = vm->vmgi->vmgi_mat->nr_of_vmgm_subp_streams; /* 1 */
+ *current = 0x41;
+ }
+}
+
+subp_attr_t vm_get_subp_attr(vm_t *vm, int streamN)
+{
+ subp_attr_t attr;
+
+ if((vm->state).domain == VTS_DOMAIN) {
+ attr = vm->vtsi->vtsi_mat->vts_subp_attr[streamN];
+ } else if((vm->state).domain == VTSM_DOMAIN) {
+ attr = vm->vtsi->vtsi_mat->vtsm_subp_attr;
+ } else if((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN) {
+ attr = vm->vmgi->vmgi_mat->vmgm_subp_attr;
+ }
+ return attr;
+}
+
+audio_attr_t vm_get_audio_attr(vm_t *vm, int streamN)
+{
+ audio_attr_t attr;
+
+ if((vm->state).domain == VTS_DOMAIN) {
+ attr = vm->vtsi->vtsi_mat->vts_audio_attr[streamN];
+ } else if((vm->state).domain == VTSM_DOMAIN) {
+ attr = vm->vtsi->vtsi_mat->vtsm_audio_attr;
+ } else if((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN) {
+ attr = vm->vmgi->vmgi_mat->vmgm_audio_attr;
+ }
+ return attr;
+}
+
+video_attr_t vm_get_video_attr(vm_t *vm)
+{
+ video_attr_t attr;
+
+ if((vm->state).domain == VTS_DOMAIN) {
+ attr = vm->vtsi->vtsi_mat->vts_video_attr;
+ } else if((vm->state).domain == VTSM_DOMAIN) {
+ attr = vm->vtsi->vtsi_mat->vtsm_video_attr;
+ } else if((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN) {
+ attr = vm->vmgi->vmgi_mat->vmgm_video_attr;
+ }
+ return attr;
+}
+
+void vm_get_video_res(vm_t *vm, int *width, int *height)
+{
+ video_attr_t attr;
+
+ attr = vm_get_video_attr(vm);
+
+ if(attr.video_format != 0)
+ *height = 576;
+ else
+ *height = 480;
+ switch(attr.picture_size) {
+ case 0:
+ *width = 720;
+ break;
+ case 1:
+ *width = 704;
+ break;
+ case 2:
+ *width = 352;
+ break;
+ case 3:
+ *width = 352;
+ *height /= 2;
+ break;
+ }
+}
+
+/* Must be called before domain is changed (get_PGCN()) */
+static void saveRSMinfo(vm_t *vm, int cellN, int blockN)
+{
+ int i;
+
+ if(cellN != 0) {
+ (vm->state).rsm_cellN = cellN;
+ (vm->state).rsm_blockN = 0;
+ } else {
+ (vm->state).rsm_cellN = (vm->state).cellN;
+ (vm->state).rsm_blockN = blockN;
+ }
+ (vm->state).rsm_vtsN = (vm->state).vtsN;
+ (vm->state).rsm_pgcN = get_PGCN(vm);
+
+ /* assert((vm->state).rsm_pgcN == (vm->state).TT_PGCN_REG); for VTS_DOMAIN */
+
+ for(i = 0; i < 5; i++) {
+ (vm->state).rsm_regs[i] = (vm->state).registers.SPRM[4 + i];
+ }
+}
+
+
+
+/* Figure out the correct pgN from the cell and update (vm->state). */
+static int set_PGN(vm_t *vm) {
+ int new_pgN = 0;
+
+ while(new_pgN < (vm->state).pgc->nr_of_programs
+ && (vm->state).cellN >= (vm->state).pgc->program_map[new_pgN])
+ new_pgN++;
+
+ if(new_pgN == (vm->state).pgc->nr_of_programs) /* We are at the last program */
+ if((vm->state).cellN > (vm->state).pgc->nr_of_cells)
+ return 1; /* We are past the last cell */
+
+ (vm->state).pgN = new_pgN;
+
+ if((vm->state).domain == VTS_DOMAIN) {
+ playback_type_t *pb_ty;
+ if((vm->state).TTN_REG > vm->vmgi->tt_srpt->nr_of_srpts)
+ return 0; /* ?? */
+ pb_ty = &vm->vmgi->tt_srpt->title[(vm->state).TTN_REG - 1].pb_ty;
+ if(pb_ty->multi_or_random_pgc_title == /* One_Sequential_PGC_Title */ 0) {
+#if 0 /* TTN_REG can't be trusted to have a correct value here... */
+ vts_ptt_srpt_t *ptt_srpt = vtsi->vts_ptt_srpt;
+ assert((vm->state).VTS_TTN_REG <= ptt_srpt->nr_of_srpts);
+ assert(get_PGCN() == ptt_srpt->title[(vm->state).VTS_TTN_REG - 1].ptt[0].pgcn);
+ assert(1 == ptt_srpt->title[(vm->state).VTS_TTN_REG - 1].ptt[0].pgn);
+#endif
+ (vm->state).PTTN_REG = (vm->state).pgN;
+ }
+ }
+
+ return 0;
+}
+
+static link_t play_PGC(vm_t *vm)
+{
+ link_t link_values;
+
+#ifdef TRACE
+ fprintf(stderr, "vm: play_PGC:");
+ if((vm->state).domain != FP_DOMAIN) {
+ fprintf(stderr, " (vm->state).pgcN (%i)\n", get_PGCN(vm));
+ } else {
+ fprintf(stderr, " first_play_pgc\n");
+ }
+#endif
+
+ /* This must be set before the pre-commands are executed because they */
+ /* might contain a CallSS that will save resume state */
+
+ /* FIXME: This may be only a temporary fix for something... */
+ (vm->state).pgN = 1;
+ (vm->state).cellN = 0;
+
+ /* eval -> updates the state and returns either
+ - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN)
+ - just play video i.e first PG
+ (This is what happens if you fall of the end of the pre_cmds)
+ - or a error (are there more cases?) */
+ if((vm->state).pgc->command_tbl && (vm->state).pgc->command_tbl->nr_of_pre) {
+ if(vmEval_CMD((vm->state).pgc->command_tbl->pre_cmds,
+ (vm->state).pgc->command_tbl->nr_of_pre,
+ &(vm->state).registers, &link_values)) {
+ /* link_values contains the 'jump' return value */
+ return link_values;
+ } else {
+#ifdef TRACE
+ fprintf(stderr, "PGC pre commands didn't do a Jump, Link or Call\n");
+#endif
+ }
+ }
+ return play_PG(vm);
+}
+
+static link_t play_PGC_PG(vm_t *vm, int pgN)
+{
+ link_t link_values;
+
+#ifdef TRACE
+ fprintf(stderr, "vm: play_PGC:");
+ if((vm->state).domain != FP_DOMAIN) {
+ fprintf(stderr, " (vm->state).pgcN (%i)\n", get_PGCN(vm));
+ } else {
+ fprintf(stderr, " first_play_pgc\n");
+ }
+#endif
+
+ /* This must be set before the pre-commands are executed because they */
+ /* might contain a CallSS that will save resume state */
+
+ /* FIXME: This may be only a temporary fix for something... */
+ (vm->state).pgN = pgN;
+ (vm->state).cellN = 0;
+
+ /* eval -> updates the state and returns either
+ - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN)
+ - just play video i.e first PG
+ (This is what happens if you fall of the end of the pre_cmds)
+ - or a error (are there more cases?) */
+ if((vm->state).pgc->command_tbl && (vm->state).pgc->command_tbl->nr_of_pre) {
+ if(vmEval_CMD((vm->state).pgc->command_tbl->pre_cmds,
+ (vm->state).pgc->command_tbl->nr_of_pre,
+ &(vm->state).registers, &link_values)) {
+ /* link_values contains the 'jump' return value */
+ return link_values;
+ } else {
+#ifdef TRACE
+ fprintf(stderr, "PGC pre commands didn't do a Jump, Link or Call\n");
+#endif
+ }
+ }
+ return play_PG(vm);
+}
+
+static link_t play_PG(vm_t *vm)
+{
+#ifdef TRACE
+ fprintf(stderr, "play_PG: (vm->state).pgN (%i)\n", (vm->state).pgN);
+#endif
+
+ assert((vm->state).pgN > 0);
+ if((vm->state).pgN > (vm->state).pgc->nr_of_programs) {
+#ifdef TRACE
+ fprintf(stderr, "(vm->state).pgN (%i) == pgc->nr_of_programs + 1 (%i)\n",
+ (vm->state).pgN, (vm->state).pgc->nr_of_programs + 1);
+#endif
+ /*assert((vm->state).pgN == (vm->state).pgc->nr_of_programs + 1);*/
+ return play_PGC_post(vm);
+ }
+
+ (vm->state).cellN = (vm->state).pgc->program_map[(vm->state).pgN - 1];
+
+ return play_Cell(vm);
+}
+
+
+static link_t play_Cell(vm_t *vm)
+{
+#ifdef TRACE
+ fprintf(stderr, "play_Cell: (vm->state).cellN (%i)\n", (vm->state).cellN);
+#endif
+
+ assert((vm->state).cellN > 0);
+ if((vm->state).cellN > (vm->state).pgc->nr_of_cells) {
+#ifdef TRACE
+ fprintf(stderr, "(vm->state).cellN (%i) == pgc->nr_of_cells + 1 (%i)\n",
+ (vm->state).cellN, (vm->state).pgc->nr_of_cells + 1);
+#endif
+ assert((vm->state).cellN == (vm->state).pgc->nr_of_cells + 1);
+ return play_PGC_post(vm);
+ }
+
+
+ /* Multi angle/Interleaved */
+ switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode) {
+ case 0: /* Normal */
+ assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 0);
+ break;
+ case 1: /* The first cell in the block */
+ switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type) {
+ case 0: /* Not part of a block */
+ assert(0);
+ case 1: /* Angle block */
+ /* Loop and check each cell instead? So we don't get outsid the block. */
+ (vm->state).cellN += (vm->state).AGL_REG - 1;
+ assert((vm->state).cellN <= (vm->state).pgc->nr_of_cells);
+ assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode != 0);
+ assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 1);
+ break;
+ case 2: /* ?? */
+ case 3: /* ?? */
+ default:
+ fprintf(stderr, "Invalid? Cell block_mode (%d), block_type (%d)\n",
+ (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode,
+ (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type);
+ }
+ break;
+ case 2: /* Cell in the block */
+ case 3: /* Last cell in the block */
+ /* These might perhaps happen for RSM or LinkC commands? */
+ default:
+ fprintf(stderr, "Cell is in block but did not enter at first cell!\n");
+ }
+
+ /* Updates (vm->state).pgN and PTTN_REG */
+ if(set_PGN(vm)) {
+ /* Should not happen */
+ link_t tmp = {LinkTailPGC, /* No Button */ 0, 0, 0};
+ assert(0);
+ return tmp;
+ }
+ (vm->state).cell_restart++;
+ fprintf(stderr, "libdvdnav: Cell should restart here\n");
+ {
+ link_t tmp = {PlayThis, /* Block in Cell */ 0, 0, 0};
+ return tmp;
+ }
+
+}
+
+static link_t play_Cell_post(vm_t *vm)
+{
+ cell_playback_t *cell;
+
+#ifdef TRACE
+ fprintf(stderr, "play_Cell_post: (vm->state).cellN (%i)\n", (vm->state).cellN);
+#endif
+
+ cell = &(vm->state).pgc->cell_playback[(vm->state).cellN - 1];
+
+ /* Still time is already taken care of before we get called. */
+
+ /* Deal with a Cell command, if any */
+ if(cell->cell_cmd_nr != 0) {
+ link_t link_values;
+
+ assert((vm->state).pgc->command_tbl != NULL);
+ assert((vm->state).pgc->command_tbl->nr_of_cell >= cell->cell_cmd_nr);
+#ifdef TRACE
+ fprintf(stderr, "Cell command pressent, executing\n");
+#endif
+ if(vmEval_CMD(&(vm->state).pgc->command_tbl->cell_cmds[cell->cell_cmd_nr - 1], 1,
+ &(vm->state).registers, &link_values)) {
+ return link_values;
+ } else {
+ fprintf(stderr, "Cell command didn't do a Jump, Link or Call\n");
+ /* Error ?? goto tail? goto next PG? or what? just continue? */
+ }
+ }
+
+
+ /* Where to continue after playing the cell... */
+ /* Multi angle/Interleaved */
+ switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode) {
+ case 0: /* Normal */
+ assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 0);
+ (vm->state).cellN++;
+ break;
+ case 1: /* The first cell in the block */
+ case 2: /* A cell in the block */
+ case 3: /* The last cell in the block */
+ default:
+ switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type) {
+ case 0: /* Not part of a block */
+ assert(0);
+ case 1: /* Angle block */
+ /* Skip the 'other' angles */
+ (vm->state).cellN++;
+ while((vm->state).cellN <= (vm->state).pgc->nr_of_cells
+ && (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode >= 2) {
+ (vm->state).cellN++;
+ }
+ break;
+ case 2: /* ?? */
+ case 3: /* ?? */
+ default:
+ fprintf(stderr, "Invalid? Cell block_mode (%d), block_type (%d)\n",
+ (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode,
+ (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type);
+ }
+ break;
+ }
+
+
+ /* Figure out the correct pgN for the new cell */
+ if(set_PGN(vm)) {
+#ifdef TRACE
+ fprintf(stderr, "last cell in this PGC\n");
+#endif
+ return play_PGC_post(vm);
+ }
+
+ return play_Cell(vm);
+}
+
+
+static link_t play_PGC_post(vm_t *vm)
+{
+ link_t link_values;
+
+#ifdef TRACE
+ fprintf(stderr, "play_PGC_post:\n");
+#endif
+
+ assert((vm->state).pgc->still_time == 0); /* FIXME $$$ */
+
+ /* eval -> updates the state and returns either
+ - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN)
+ - or a error (are there more cases?)
+ - if you got to the end of the post_cmds, then what ?? */
+ if((vm->state).pgc->command_tbl &&
+ vmEval_CMD((vm->state).pgc->command_tbl->post_cmds,
+ (vm->state).pgc->command_tbl->nr_of_post,
+ &(vm->state).registers, &link_values)) {
+ return link_values;
+ }
+
+ /* Or perhaps handle it here? */
+ {
+ link_t link_next_pgc = {LinkNextPGC, 0, 0, 0};
+ fprintf(stderr, "** Fell of the end of the pgc, continuing in NextPGC\n");
+ assert((vm->state).pgc->next_pgc_nr != 0);
+ /* Should end up in the STOP_DOMAIN if next_pgc is 0. */
+ return link_next_pgc;
+ }
+}
+
+
+static link_t process_command(vm_t *vm, link_t link_values)
+{
+ /* FIXME $$$ Move this to a separate function? */
+ vm->badness_counter++;
+ if (vm->badness_counter > 1) fprintf(stderr, "**** process_command re-entered %d*****\n",vm->badness_counter);
+ while(link_values.command != PlayThis) {
+
+#ifdef TRACE
+ vmPrint_LINK(link_values);
+
+ fprintf(stderr, "Link values %i %i %i %i\n", link_values.command,
+ link_values.data1, link_values.data2, link_values.data3);
+
+ fprintf(stderr, "Before:");
+ vm_print_current_domain_state(vm);
+#endif
+
+ switch(link_values.command) {
+ case LinkNoLink:
+ /* No Link */
+ /* BUTTON number:data1 */
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ fprintf(stderr, "libdvdnav: FIXME: in trouble...LinkNoLink - CRASHING!!!\n");
+ assert(0);
+
+ case LinkTopC:
+ /* Restart playing from the beginning of the current Cell. */
+ /* BUTTON number:data1 */
+ fprintf(stderr, "libdvdnav: FIXME: LinkTopC. Replay current Cell\n");
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ link_values = play_Cell(vm);
+ break;
+ case LinkNextC:
+ /* Link to Next Cell */
+ /* BUTTON number:data1 */
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ (vm->state).cellN += 1; /* if cellN becomes > nr_of_cells? it is handled in play_Cell() */
+ link_values = play_Cell(vm);
+ break;
+ case LinkPrevC:
+ /* Link to Previous Cell */
+ /* BUTTON number:data1 */
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ (vm->state).cellN -= 1; /* If cellN becomes < 1? it is handled in play_Cell() */
+ link_values = play_Cell(vm);
+ break;
+
+ case LinkTopPG:
+ /* Link to Top Program */
+ /* BUTTON number:data1 */
+ fprintf(stderr, "libdvdnav: FIXME: LinkTopPG. This should start the current PG again.\n");
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ /* Does pgN always contain the current value? */
+ link_values = play_PG(vm);
+ break;
+ case LinkNextPG:
+ /* Link to Next Program */
+ /* BUTTON number:data1 */
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ /* Does pgN always contain the current value? */
+ (vm->state).pgN += 1; /* FIXME: What if pgN becomes > pgc.nr_of_programs? */
+ link_values = play_PG(vm);
+ break;
+ case LinkPrevPG:
+ /* Link to Previous Program */
+ /* BUTTON number:data1 */
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ /* Does pgN always contain the current value? */
+ assert((vm->state).pgN > 1);
+ (vm->state).pgN -= 1; /* FIXME: What if pgN becomes < 1? */
+ link_values = play_PG(vm);
+ break;
+
+ case LinkTopPGC:
+ /* Restart playing from beginning of current Program Chain */
+ /* BUTTON number:data1 */
+ fprintf(stderr, "libdvdnav: FIXME: LinkTopPGC. Restart from beginning of current Program Chain\n");
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ link_values = play_PGC(vm);
+ break;
+ case LinkNextPGC:
+ /* Link to Next Program Chain */
+ /* BUTTON number:data1 */
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ assert((vm->state).pgc->next_pgc_nr != 0);
+ if(set_PGC(vm, (vm->state).pgc->next_pgc_nr))
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case LinkPrevPGC:
+ /* Link to Previous Program Chain */
+ /* BUTTON number:data1 */
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ assert((vm->state).pgc->prev_pgc_nr != 0);
+ if(set_PGC(vm, (vm->state).pgc->prev_pgc_nr))
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case LinkGoUpPGC:
+ /* Link to GoUp??? Program Chain */
+ /* BUTTON number:data1 */
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ assert((vm->state).pgc->goup_pgc_nr != 0);
+ if(set_PGC(vm, (vm->state).pgc->goup_pgc_nr))
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case LinkTailPGC:
+ /* Link to Tail??? Program Chain */
+ /* BUTTON number:data1 */
+ /* fprintf(stderr, "libdvdnav: FIXME: LinkTailPGC. What is LinkTailPGC?\n"); */
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ link_values = play_PGC_post(vm);
+ break;
+
+ case LinkRSM:
+ {
+ /* Link to Resume */
+ int i;
+ /* Check and see if there is any rsm info!! */
+ (vm->state).domain = VTS_DOMAIN;
+ ifoOpenNewVTSI(vm, vm->dvd, (vm->state).rsm_vtsN);
+ set_PGC(vm, (vm->state).rsm_pgcN);
+
+ /* These should never be set in SystemSpace and/or MenuSpace */
+ /* (vm->state).TTN_REG = rsm_tt; ?? */
+ /* (vm->state).TT_PGCN_REG = (vm->state).rsm_pgcN; ?? */
+ for(i = 0; i < 5; i++) {
+ (vm->state).registers.SPRM[4 + i] = (vm->state).rsm_regs[i];
+ }
+
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+
+ if((vm->state).rsm_cellN == 0) {
+ assert((vm->state).cellN); /* Checking if this ever happens */
+ /* assert( time/block/vobu is 0 ); */
+ (vm->state).pgN = 1;
+ link_values = play_PG(vm);
+ } else {
+ /* assert( time/block/vobu is _not_ 0 ); */
+ /* play_Cell_at_time */
+ /* (vm->state).pgN = ?? this gets the righ value in play_Cell */
+ (vm->state).cellN = (vm->state).rsm_cellN;
+ link_values.command = PlayThis;
+ link_values.data1 = (vm->state).rsm_blockN;
+ if(set_PGN(vm)) {
+ /* Were at the end of the PGC, should not happen for a RSM */
+ assert(0);
+ link_values.command = LinkTailPGC;
+ link_values.data1 = 0; /* No button */
+ }
+ }
+ }
+ break;
+ case LinkPGCN:
+ /* Link to Program Chain Number:data1 */
+ if(set_PGC(vm, link_values.data1))
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case LinkPTTN:
+ /* Link to Part of this Title Number:data1 */
+ /* BUTTON number:data2 */
+ assert((vm->state).domain == VTS_DOMAIN);
+ if(link_values.data2 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data2 << 10;
+ if(set_VTS_PTT(vm, (vm->state).vtsN, (vm->state).VTS_TTN_REG, link_values.data1) == -1)
+ assert(0);
+ link_values = play_PG(vm);
+ break;
+ case LinkPGN:
+ /* Link to Program Number:data1 */
+ /* BUTTON number:data2 */
+ if(link_values.data2 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data2 << 10;
+ /* Update any other state, PTTN perhaps? */
+ (vm->state).pgN = link_values.data1;
+ link_values = play_PG(vm);
+ break;
+ case LinkCN:
+ /* Link to Cell Number:data1 */
+ /* BUTTON number:data2 */
+ if(link_values.data2 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data2 << 10;
+ /* Update any other state, pgN, PTTN perhaps? */
+ (vm->state).cellN = link_values.data1;
+ link_values = play_Cell(vm);
+ break;
+
+ case Exit:
+ fprintf(stderr, "libdvdnav: FIXME:in trouble...Link Exit - CRASHING!!!\n");
+ assert(0); /* What should we do here?? */
+
+ case JumpTT:
+ /* Jump to VTS Title Domain */
+ /* Only allowed from the First Play domain(PGC) */
+ /* or the Video Manager domain (VMG) */
+ assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN); /* ?? */
+ if(set_TT(vm,link_values.data1) == -1)
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case JumpVTS_TT:
+ /* Jump to Title:data1 in same VTS Title Domain */
+ /* Only allowed from the VTS Menu Domain(VTSM) */
+ /* or the Video Title Set Domain(VTS) */
+ assert((vm->state).domain == VTSM_DOMAIN || (vm->state).domain == VTS_DOMAIN); /* ?? */
+ fprintf(stderr, "libdvdnav: FIXME: Should be able to use get_VTS_PTT here.\n");
+ if(set_VTS_TT(vm,(vm->state).vtsN, link_values.data1) == -1)
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case JumpVTS_PTT:
+ /* Jump to Part:data2 of Title:data1 in same VTS Title Domain */
+ /* Only allowed from the VTS Menu Domain(VTSM) */
+ /* or the Video Title Set Domain(VTS) */
+ assert((vm->state).domain == VTSM_DOMAIN || (vm->state).domain == VTS_DOMAIN); /* ?? */
+ if(set_VTS_PTT(vm,(vm->state).vtsN, link_values.data1, link_values.data2) == -1)
+ assert(0);
+ link_values = play_PGC_PG(vm, link_values.data2);
+ break;
+
+ case JumpSS_FP:
+ /* Jump to First Play Domain */
+ /* Only allowed from the VTS Menu Domain(VTSM) */
+ /* or the Video Manager domain (VMG) */
+ assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == VTSM_DOMAIN); /* ?? */
+ set_FP_PGC(vm);
+ link_values = play_PGC(vm);
+ break;
+ case JumpSS_VMGM_MENU:
+ /* Jump to Video Manger domain - Title Menu:data1 or any PGC in VMG */
+ /* Allowed from anywhere except the VTS Title domain */
+ assert((vm->state).domain == VMGM_DOMAIN ||
+ (vm->state).domain == VTSM_DOMAIN ||
+ (vm->state).domain == FP_DOMAIN); /* ?? */
+ (vm->state).domain = VMGM_DOMAIN;
+ if(set_MENU(vm,link_values.data1) == -1)
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case JumpSS_VTSM:
+ /* Jump to a menu in Video Title domain, */
+ /* or to a Menu is the current VTS */
+ /* FIXME: This goes badly wrong for some DVDs. */
+ /* FIXME: Keep in touch with ogle people regarding what to do here */
+ /* ifoOpenNewVTSI:data1 */
+ /* VTS_TTN_REG:data2 */
+ /* get_MENU:data3 */
+#ifdef TRACE
+ fprintf(stderr, "dvdnav: BUG TRACKING *******************************************************************\n");
+ fprintf(stderr, " data1=%u data2=%u data3=%u\n",
+ link_values.data1,
+ link_values.data2,
+ link_values.data3);
+ fprintf(stderr, "dvdnav: *******************************************************************\n");
+#endif
+
+ if(link_values.data1 !=0) {
+ assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN); /* ?? */
+ (vm->state).domain = VTSM_DOMAIN;
+ ifoOpenNewVTSI(vm, vm->dvd, link_values.data1); /* Also sets (vm->state).vtsN */
+ } else {
+ /* This happens on 'The Fifth Element' region 2. */
+ assert((vm->state).domain == VTSM_DOMAIN);
+ }
+ /* I don't know what title is supposed to be used for. */
+ /* Alien or Aliens has this != 1, I think. */
+ /* assert(link_values.data2 == 1); */
+ (vm->state).VTS_TTN_REG = link_values.data2;
+ if(set_MENU(vm, link_values.data3) == -1)
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case JumpSS_VMGM_PGC:
+ /* get_PGC:data1 */
+ assert((vm->state).domain == VMGM_DOMAIN ||
+ (vm->state).domain == VTSM_DOMAIN ||
+ (vm->state).domain == FP_DOMAIN); /* ?? */
+ (vm->state).domain = VMGM_DOMAIN;
+ if(set_PGC(vm,link_values.data1) == -1)
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+
+ case CallSS_FP:
+ /* saveRSMinfo:data1 */
+ assert((vm->state).domain == VTS_DOMAIN); /* ?? */
+ /* Must be called before domain is changed */
+ saveRSMinfo(vm, link_values.data1, /* We dont have block info */ 0);
+ set_FP_PGC(vm);
+ link_values = play_PGC(vm);
+ break;
+ case CallSS_VMGM_MENU:
+ /* get_MENU:data1 */
+ /* saveRSMinfo:data2 */
+ assert((vm->state).domain == VTS_DOMAIN); /* ?? */
+ /* Must be called before domain is changed */
+ saveRSMinfo(vm,link_values.data2, /* We dont have block info */ 0);
+ (vm->state).domain = VMGM_DOMAIN;
+ if(set_MENU(vm,link_values.data1) == -1)
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case CallSS_VTSM:
+ /* get_MENU:data1 */
+ /* saveRSMinfo:data2 */
+ assert((vm->state).domain == VTS_DOMAIN); /* ?? */
+ /* Must be called before domain is changed */
+ saveRSMinfo(vm,link_values.data2, /* We dont have block info */ 0);
+ (vm->state).domain = VTSM_DOMAIN;
+ if(set_MENU(vm,link_values.data1) == -1)
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case CallSS_VMGM_PGC:
+ /* get_PGC:data1 */
+ /* saveRSMinfo:data2 */
+ assert((vm->state).domain == VTS_DOMAIN); /* ?? */
+ /* Must be called before domain is changed */
+ saveRSMinfo(vm,link_values.data2, /* We dont have block info */ 0);
+ (vm->state).domain = VMGM_DOMAIN;
+ if(set_PGC(vm,link_values.data1) == -1)
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case PlayThis:
+ /* Should never happen. */
+ assert(0);
+ break;
+ }
+
+#ifdef TRACE
+ fprintf(stderr, "After:");
+ vm_print_current_domain_state(vm);
+#endif
+
+ }
+ vm->badness_counter--;
+ return link_values;
+}
+
+/* Searches the TT tables, to find the current TT.
+ * returns the current TT.
+ * returns 0 if not found.
+ */
+static int get_TT(vm_t *vm, int vtsN, int vts_ttn) {
+ int i;
+ int tt=0;
+ for(i = 1; i <= vm->vmgi->tt_srpt->nr_of_srpts; i++) {
+ if( vm->vmgi->tt_srpt->title[i - 1].title_set_nr == vtsN &&
+ vm->vmgi->tt_srpt->title[i - 1].vts_ttn == vts_ttn) {
+ tt=i;
+ break;
+ }
+ }
+ return tt;
+}
+
+static int set_TT(vm_t *vm, int tt)
+{
+ assert(tt <= vm->vmgi->tt_srpt->nr_of_srpts);
+
+ (vm->state).TTN_REG = tt;
+
+ return set_VTS_TT(vm, vm->vmgi->tt_srpt->title[tt - 1].title_set_nr,
+ vm->vmgi->tt_srpt->title[tt - 1].vts_ttn);
+}
+
+
+static int set_VTS_TT(vm_t *vm, int vtsN, int vts_ttn)
+{
+ fprintf(stderr, "get_VTS_TT called, testing!!! vtsN=%d, vts_ttn=%d\n", vtsN, vts_ttn);
+ return set_VTS_PTT(vm, vtsN, vts_ttn, 1);
+ /* pgcN = get_ID(vm, vts_ttn); This might return -1 */
+ /*
+ assert(pgcN != -1);
+
+ (vm->state).TTN_REG = get_TT(*vm, vtsN, vts_ttn);
+ (vm->state).VTS_TTN_REG = vts_ttn;
+ (vm->state).vtsN =
+ */
+ /* Any other registers? */
+
+ /* return set_PGC(vm, pgcN); */
+}
+
+
+static int set_VTS_PTT(vm_t *vm, int vtsN, int /* is this really */ vts_ttn, int part)
+{
+ int pgcN, pgN, res;
+
+ (vm->state).domain = VTS_DOMAIN;
+ if(vtsN != (vm->state).vtsN)
+ ifoOpenNewVTSI(vm, vm->dvd, vtsN); /* Also sets (vm->state).vtsN */
+
+ assert(vts_ttn <= vm->vtsi->vts_ptt_srpt->nr_of_srpts);
+ assert(part <= vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].nr_of_ptts);
+
+ pgcN = vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].ptt[part - 1].pgcn;
+ pgN = vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].ptt[part - 1].pgn;
+
+ (vm->state).TT_PGCN_REG = pgcN;
+ (vm->state).PTTN_REG = pgN;
+
+ (vm->state).TTN_REG = get_TT(vm, vtsN, vts_ttn);
+ assert( (vm->state.TTN_REG) != 0 );
+ (vm->state).VTS_TTN_REG = vts_ttn;
+ (vm->state).vtsN = vtsN; /* Not sure about this one. We can get to it easily from TTN_REG */
+ /* Any other registers? */
+
+ res = set_PGC(vm, pgcN); // This clobber's state.pgN (sets it to 1), but we don't want clobbering here.
+ (vm->state).pgN = pgN; /* Part?? */
+ return res;
+}
+
+
+
+static int set_FP_PGC(vm_t *vm)
+{
+ (vm->state).domain = FP_DOMAIN;
+
+ (vm->state).pgc = vm->vmgi->first_play_pgc;
+
+ return 0;
+}
+
+
+static int set_MENU(vm_t *vm, int menu)
+{
+ assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == VTSM_DOMAIN);
+ return set_PGC(vm, get_ID(vm, menu));
+}
+
+/* Search for entry_id match of the PGC Category in the current VTS PGCIT table.
+ * Return pgcN based on entry_id match.
+ */
+static int get_ID(vm_t *vm, int id)
+{
+ int pgcN, i;
+ pgcit_t *pgcit;
+
+ /* Relies on state to get the correct pgcit. */
+ pgcit = get_PGCIT(vm);
+ assert(pgcit != NULL);
+ fprintf(stderr, "** Searching for menu (0x%x) entry PGC\n", id);
+
+ /* Get menu/title */
+ for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+ if((pgcit->pgci_srp[i].entry_id & 0x7f) == id) {
+ assert((pgcit->pgci_srp[i].entry_id & 0x80) == 0x80);
+ pgcN = i + 1;
+ return pgcN;
+ }
+ }
+ fprintf(stderr, "** No such id/menu (%d) entry PGC\n", id);
+ for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+ fprintf(stderr, "Available menus: 0x%x\n",
+ pgcit->pgci_srp[i].entry_id);
+ }
+ assert(0); /* Use assert for now, until the error is handled. */
+ return -1; /* error */
+}
+
+/* Set the vm->state to pgcN.
+ * Returns success/failure.
+ */
+static int set_PGC(vm_t *vm, int pgcN)
+{
+ /* FIXME: Keep this up to date with the ogle people */
+ pgcit_t *pgcit;
+
+ pgcit = get_PGCIT(vm);
+
+ assert(pgcit != NULL); /* ?? Make this return -1 instead */
+ if(pgcN < 1 || pgcN > pgcit->nr_of_pgci_srp) {
+ fprintf(stderr, "** No such pgcN = %d\n", pgcN);
+ assert(0);
+ return -1; /* error */
+ }
+
+ /* (vm->state).pgcN = pgcN; */
+ (vm->state).pgc = pgcit->pgci_srp[pgcN - 1].pgc;
+ (vm->state).pgN = 1;
+
+ if((vm->state).domain == VTS_DOMAIN)
+ (vm->state).TT_PGCN_REG = pgcN;
+
+ return 0;
+}
+
+static int get_PGCN(vm_t *vm)
+{
+ pgcit_t *pgcit;
+ int pgcN = 1;
+
+ pgcit = get_PGCIT(vm);
+
+ assert(pgcit != NULL);
+
+ while(pgcN <= pgcit->nr_of_pgci_srp) {
+ if(pgcit->pgci_srp[pgcN - 1].pgc == (vm->state).pgc)
+ return pgcN;
+ pgcN++;
+ }
+ fprintf(stderr, "libdvdnav: get_PGCN failed. Trying to find pgcN in domain %d \n",
+ (vm->state).domain);
+ assert(0);
+ return -1; /* error */
+}
+
+int vm_get_video_aspect(vm_t *vm)
+{
+ int aspect = 0;
+
+ switch ((vm->state).domain) {
+ case VTS_DOMAIN:
+ aspect = vm->vtsi->vtsi_mat->vts_video_attr.display_aspect_ratio;
+ break;
+ case VTSM_DOMAIN:
+ aspect = vm->vtsi->vtsi_mat->vtsm_video_attr.display_aspect_ratio;
+ break;
+ case VMGM_DOMAIN:
+ aspect = vm->vmgi->vmgi_mat->vmgm_video_attr.display_aspect_ratio;
+ break;
+ default:
+ fprintf(stderr, "libdvdnav: vm_get_video_aspect failed. Unknown domain %d\n",
+ (vm->state).domain);
+ assert(0);
+ break;
+ }
+#ifdef TRACE
+ fprintf(stderr, "dvdnav:get_video_aspect:aspect=%d\n",aspect);
+#endif
+ assert(aspect == 0 || aspect == 3);
+ (vm->state).registers.SPRM[14] &= ~(0x3 << 10);
+ (vm->state).registers.SPRM[14] |= aspect << 10;
+
+ return aspect;
+}
+
+int vm_get_video_scale_permission(vm_t *vm)
+{
+ int permission = 0;
+
+ if((vm->state).domain == VTS_DOMAIN) {
+ permission = vm->vtsi->vtsi_mat->vts_video_attr.permitted_df;
+ } else if((vm->state).domain == VTSM_DOMAIN) {
+ permission = vm->vtsi->vtsi_mat->vtsm_video_attr.permitted_df;
+ } else if((vm->state).domain == VMGM_DOMAIN) {
+ permission = vm->vmgi->vmgi_mat->vmgm_video_attr.permitted_df;
+ }
+#ifdef TRACE
+ fprintf(stderr, "dvdnav:get_video_scale_permission:permission=%d\n",permission);
+#endif
+
+ return permission;
+}
+
+static void ifoOpenNewVTSI(vm_t *vm, dvd_reader_t *dvd, int vtsN)
+{
+ if((vm->state).vtsN == vtsN) {
+ return; /* We alread have it */
+ }
+
+ if(vm->vtsi != NULL)
+ ifoClose(vm->vtsi);
+
+ vm->vtsi = ifoOpenVTSI(dvd, vtsN);
+ if(vm->vtsi == NULL) {
+ fprintf(stderr, "ifoOpenVTSI failed - CRASHING!!!\n");
+ assert(0);
+ }
+ if(!ifoRead_VTS_PTT_SRPT(vm->vtsi)) {
+ fprintf(stderr, "ifoRead_VTS_PTT_SRPT failed - CRASHING!!!\n");
+ assert(0);
+ }
+ if(!ifoRead_PGCIT(vm->vtsi)) {
+ fprintf(stderr, "ifoRead_PGCIT failed - CRASHING!!!\n");
+ assert(0);
+ }
+ if(!ifoRead_PGCI_UT(vm->vtsi)) {
+ fprintf(stderr, "ifoRead_PGCI_UT failed - CRASHING!!!\n");
+ assert(0);
+ }
+ if(!ifoRead_VOBU_ADMAP(vm->vtsi)) {
+ fprintf(stderr, "ifoRead_VOBU_ADMAP vtsi failed - CRASHING\n");
+ assert(0);
+ }
+ if(!ifoRead_TITLE_VOBU_ADMAP(vm->vtsi)) {
+ fprintf(stderr, "ifoRead_TITLE_VOBU_ADMAP vtsi failed - CRASHING\n");
+ assert(0);
+ }
+ (vm->state).vtsN = vtsN;
+}
+
+static pgcit_t* get_MENU_PGCIT(vm_t *vm, ifo_handle_t *h, uint16_t lang)
+{
+ int i;
+
+ if(h == NULL || h->pgci_ut == NULL) {
+ fprintf(stderr, "*** pgci_ut handle is NULL ***\n");
+ return NULL; /* error? */
+ }
+
+ i = 0;
+ while(i < h->pgci_ut->nr_of_lus
+ && h->pgci_ut->lu[i].lang_code != lang)
+ i++;
+ if(i == h->pgci_ut->nr_of_lus) {
+ fprintf(stderr, "Language '%c%c' not found, using '%c%c' instead\n",
+ (char)(lang >> 8), (char)(lang & 0xff),
+ (char)(h->pgci_ut->lu[0].lang_code >> 8),
+ (char)(h->pgci_ut->lu[0].lang_code & 0xff));
+ fprintf(stderr, "Menu Languages available: ");
+ for(i=0;i< h->pgci_ut->nr_of_lus;i++) {
+ fprintf(stderr, "%c%c ",
+ (char)(h->pgci_ut->lu[0].lang_code >> 8),
+ (char)(h->pgci_ut->lu[0].lang_code & 0xff));
+ }
+ fprintf(stderr, "\n");
+
+ i = 0; /* error? */
+ }
+
+ return h->pgci_ut->lu[i].pgcit;
+}
+
+/* Uses state to decide what to return */
+static pgcit_t* get_PGCIT(vm_t *vm) {
+ pgcit_t *pgcit;
+
+ switch ((vm->state).domain) {
+ case VTS_DOMAIN:
+ pgcit = vm->vtsi->vts_pgcit;
+ break;
+ case VTSM_DOMAIN:
+ pgcit = get_MENU_PGCIT(vm, vm->vtsi, (vm->state).registers.SPRM[0]);
+ break;
+ case VMGM_DOMAIN:
+ pgcit = get_MENU_PGCIT(vm, vm->vmgi, (vm->state).registers.SPRM[0]);
+ break;
+ default:
+ pgcit = NULL; /* Should never hapen */
+ fprintf(stderr, "libdvdnav: get_PGCIT: Unknown domain:%d\n",
+ (vm->state).domain);
+ assert(0);
+ break;
+ }
+
+ return pgcit;
+}
+
+/*
+ * $Log: vm.c,v $
+ * Revision 1.1 2002/08/08 17:49:21 richwareham
+ * First stage of DVD plugin -> dvdnav conversion
+ *
+ * Revision 1.24 2002/07/05 14:18:55 mroi
+ * report all spu types (widescreen, letterbox and pan&scan), not widescreen
+ * only and report the stream's scale permissions to detect pan&scan material
+ *
+ * Revision 1.23 2002/07/05 01:42:30 jcdutton
+ * Add more debug info for Menu language selection.
+ * Only do vm_start when we have to.
+ *
+ * Revision 1.22 2002/07/04 00:38:51 jcdutton
+ * Add some menu language printf's.
+ *
+ * Revision 1.21 2002/07/03 02:41:31 jcdutton
+ * Fix another long standing bug.
+ * Now changing PGC will force a start at the first PG of the PGC.
+ *
+ * Revision 1.20 2002/07/02 22:57:10 jcdutton
+ * Rename some of the functions in vm.c to help readability.
+ * Hopefully fix __FUNCTION__ problem. Use __func_ as recommended in C99.
+ * Fix bug where libdvdnav would not immeadiately replay the same cell due to menu buttons.
+ *
+ * Revision 1.19 2002/06/04 13:35:16 richwareham
+ * Removed more C++ style comments
+ *
+ * Revision 1.18 2002/05/30 19:25:08 richwareham
+ * Another small fix
+ *
+ * Revision 1.17 2002/05/30 15:56:41 richwareham
+ * Fixed (what appears to be) an error in JumpVTS_PTT implementation, it didn't call play_PGC after jumping.
+ *
+ * Revision 1.16 2002/04/24 21:15:25 jcdutton
+ * Quiet please!!!
+ *
+ * Revision 1.15 2002/04/23 13:18:31 jcdutton
+ * Insert some assert commands to hopefully catch a DVD which will give us information on what to do if these values are != 0.
+ *
+ * Revision 1.14 2002/04/23 12:34:39 f1rmb
+ * Why rewrite vm function, use it instead (this remark is for me, of course ;-) ).
+ * Comment unused var, shut compiler warnings.
+ *
+ * Revision 1.13 2002/04/23 02:12:27 jcdutton
+ * Re-implemented seeking.
+ *
+ * Revision 1.12 2002/04/22 22:00:48 jcdutton
+ * Start of rewrite of libdvdnav. Still need to re-implement seeking.
+ *
+ * Revision 1.11 2002/04/12 20:06:41 jcdutton
+ * Implement General Register Counters or GPRM counters.
+ * Navigation timers are not supported yet. SPRM[9] and SPRM[10].
+ *
+ * Revision 1.10 2002/04/12 12:43:36 jcdutton
+ * Display DVD disk region setting.
+ * Display possible RCE region protection message.
+ *
+ * Revision 1.9 2002/04/10 16:45:57 jcdutton
+ * Actually fix the const this time!
+ *
+ * Revision 1.8 2002/04/10 16:40:52 jcdutton
+ * Fix a const problem.
+ *
+ * Revision 1.7 2002/04/10 13:09:40 jcdutton
+ * Some improvements to decoder.c
+ * Registers should be updated correctly now, but still needs checking.
+ *
+ * Revision 1.6 2002/04/09 15:19:07 jcdutton
+ * Added some debug info, to hopefully help in tracking bugs in libdvdnav.
+ *
+ * Revision 1.5 2002/04/07 19:35:54 jcdutton
+ * Added some comments into the code.
+ *
+ * Revision 1.4 2002/04/06 18:31:50 jcdutton
+ * Some cleaning up.
+ * changed exit(1) to assert(0) so they actually get seen by the user so that it helps developers more.
+ *
+ * Revision 1.3 2002/04/02 18:22:27 richwareham
+ * Added reset patch from Kees Cook <kees@outflux.net>
+ *
+ * Revision 1.2 2002/04/01 18:56:28 richwareham
+ * Added initial example programs directory and make sure all debug/error output goes to stderr.
+ *
+ * Revision 1.1.1.1 2002/03/12 19:45:55 richwareham
+ * Initial import
+ *
+ * Revision 1.18 2002/01/22 16:56:49 jcdutton
+ * Fix clut after seeking.
+ * Add a few virtual machine debug messages, to help diagnose problems with "Deep Purple - Total Abandon" DVD as I don't have the DVD itvm.
+ * Fix a few debug messages, so they do not say FIXME.
+ * Move the FIXME debug messages to comments in the code.
+ *
+ * Revision 1.17 2002/01/21 01:16:30 jcdutton
+ * Added some debug messages, to hopefully get info from users.
+ *
+ * Revision 1.16 2002/01/20 21:40:46 jcdutton
+ * Start to fix some assert failures.
+ *
+ * Revision 1.15 2002/01/19 20:24:38 jcdutton
+ * Just some FIXME notes added.
+ *
+ * Revision 1.14 2002/01/13 22:17:57 jcdutton
+ * Change logging.
+ *
+ *
+ */
diff --git a/src/input/libdvdnav/vm.h b/src/input/libdvdnav/vm.h
new file mode 100644
index 000000000..a4ba8fb83
--- /dev/null
+++ b/src/input/libdvdnav/vm.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2000, 2001 Håkan Hjort
+ * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav 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.
+ *
+ * libdvdnav 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
+ *
+ * $Id: vm.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifndef VM_H_INCLUDED
+#define VM_H_INCLUDED
+
+#include "decoder.h"
+#include <dvd_types.h>
+
+/* DOMAIN enum */
+
+typedef enum {
+ FP_DOMAIN = 1,
+ VTS_DOMAIN = 2,
+ VMGM_DOMAIN = 4,
+ VTSM_DOMAIN = 8
+} domain_t;
+
+/**
+ * State: SPRM, GPRM, Domain, pgc, pgN, cellN, ?
+ */
+typedef struct {
+ registers_t registers;
+
+ pgc_t *pgc; /* either this or *pgc is enough? */
+
+ domain_t domain;
+ int vtsN; /* 0 is vmgm? */
+ /* int pgcN; // either this or *pgc is enough. Which to use? */
+ int pgN; /* is this needed? can allways fid pgN from cellN? */
+ int cellN;
+ int32_t cell_restart; /* get cell to restart */
+ int blockN;
+
+ /* Resume info */
+ int rsm_vtsN;
+ int rsm_blockN; /* of nav_packet */
+ uint16_t rsm_regs[5]; /* system registers 4-8 */
+ int rsm_pgcN;
+ int rsm_cellN;
+} dvd_state_t;
+
+typedef struct vm_position_s {
+ int16_t button; /* Button highlighted */
+ uint32_t clut; /* CLUT to use, not needed in this struct */
+ int32_t vts; /* vts number to use */
+ int32_t domain; /* domain to use */
+ int32_t spu_channel; /* spu channel to use */
+ int32_t angle_channel; /* angle channel to use */
+ int32_t audio_channel; /* audio channel to use */
+ int32_t hop_channel; /* channel hopping. E.g menu button pressed */
+ int32_t title; /* title number */
+ int32_t chapter; /* chapter number */
+ int32_t cell; /* cell number */
+ int32_t cell_restart; /* get cell to restart */
+ int32_t still; /* is cell still */
+ int32_t vobu_start; /* block number of start of current VOBU in use */
+ int32_t vobu_next; /* block number within VOBU in use */
+} vm_position_t;
+
+typedef struct {
+ dvd_reader_t *dvd;
+ ifo_handle_t *vmgi;
+ ifo_handle_t *vtsi;
+ dvd_state_t state;
+ int badness_counter;
+ int32_t hop_channel;
+} vm_t;
+
+
+/* Audio stream number */
+#define AST_REG registers.SPRM[1]
+/* Subpicture stream number */
+#define SPST_REG registers.SPRM[2]
+/* Angle number */
+#define AGL_REG registers.SPRM[3]
+/* Title Track Number */
+#define TTN_REG registers.SPRM[4]
+/* VTS Title Track Number */
+#define VTS_TTN_REG registers.SPRM[5]
+/* PGC Number for this Title Track */
+#define TT_PGCN_REG registers.SPRM[6]
+/* Current Part of Title (PTT) number for (One_Sequential_PGC_Title) */
+#define PTTN_REG registers.SPRM[7]
+/* Highlighted Button Number (btn nr 1 == value 1024) */
+#define HL_BTNN_REG registers.SPRM[8]
+/* Parental Level */
+#define PTL_REG registers.SPRM[13]
+
+/* Initialisation & destruction */
+vm_t* vm_new_vm();
+void vm_free_vm(vm_t *vm);
+
+/* IFO access */
+ifo_handle_t *vm_get_vmgi(vm_t *vm);
+ifo_handle_t *vm_get_vtsi(vm_t *vm);
+
+/* Reader Access */
+dvd_reader_t *vm_get_dvd_reader(vm_t *vm);
+
+/* Jumping */
+int vm_start_title(vm_t *vm, int tt);
+int vm_jump_prog(vm_t *vm, int pr);
+
+/* Other calls */
+int vm_reset(vm_t *vm, char *dvdroot); /* , register_t regs); */
+int vm_start(vm_t *vm);
+int vm_position_get(vm_t *vm, vm_position_t *position);
+int vm_position_print(vm_t *vm, vm_position_t *position);
+int vm_eval_cmd(vm_t *vm, vm_cmd_t *cmd);
+int vm_get_next_cell(vm_t *vm);
+int vm_menu_call(vm_t *vm, DVDMenuID_t menuid, int block);
+int vm_resume(vm_t *vm);
+int vm_go_up(vm_t *vm);
+int vm_top_pg(vm_t *vm);
+int vm_next_pg(vm_t *vm);
+int vm_prev_pg(vm_t *vm);
+int vm_get_audio_stream(vm_t *vm, int audioN);
+int vm_get_audio_active_stream(vm_t *vm);
+int vm_get_subp_stream(vm_t *vm, int subpN, int mode);
+int vm_get_subp_active_stream(vm_t *vm, int mode);
+void vm_get_angle_info(vm_t *vm, int *num_avail, int *current);
+void vm_get_audio_info(vm_t *vm, int *num_avail, int *current);
+void vm_get_subp_info(vm_t *vm, int *num_avail, int *current);
+subp_attr_t vm_get_subp_attr(vm_t *vm, int streamN);
+audio_attr_t vm_get_audio_attr(vm_t *vm, int streamN);
+void vm_get_video_res(vm_t *vm, int *width, int *height);
+int vm_get_video_aspect(vm_t *vm);
+int vm_get_video_scale_permission(vm_t *vm);
+
+#endif /* VM_HV_INCLUDED */
+
diff --git a/src/input/libdvdnav/vmcmd.c b/src/input/libdvdnav/vmcmd.c
new file mode 100644
index 000000000..4ae8f92ba
--- /dev/null
+++ b/src/input/libdvdnav/vmcmd.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright (C) 2000, 2001 Martin Norbäck, Håkan Hjort
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav 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.
+ *
+ * libdvdnav 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
+ *
+ * $Id: vmcmd.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "vmcmd.h"
+
+
+/* freebsd compatibility */
+#ifndef PRIu8
+#define PRIu8 "d"
+#endif
+
+/* freebsd compatibility */
+#ifndef PRIu16
+#define PRIu16 "d"
+#endif
+
+static const char *cmp_op_table[] = {
+ NULL, "&", "==", "!=", ">=", ">", "<=", "<"
+};
+static const char *set_op_table[] = {
+ NULL, "=", "<->", "+=", "-=", "*=", "/=", "%=", "rnd", "&=", "|=", "^="
+};
+
+static const char *link_table[] = {
+ "LinkNoLink", "LinkTopC", "LinkNextC", "LinkPrevC",
+ NULL, "LinkTopPG", "LinkNextPG", "LinkPrevPG",
+ NULL, "LinkTopPGC", "LinkNextPGC", "LinkPrevPGC",
+ "LinkGoUpPGC", "LinkTailPGC", NULL, NULL,
+ "RSM"
+};
+
+static const char *system_reg_table[] = {
+ "Menu Description Language Code",
+ "Audio Stream Number",
+ "Sub-picture Stream Number",
+ "Angle Number",
+ "Title Track Number",
+ "VTS Title Track Number",
+ "VTS PGC Number",
+ "PTT Number for One_Sequential_PGC_Title",
+ "Highlighted Button Number",
+ "Navigation Timer",
+ "Title PGC Number for Navigation Timer",
+ "Audio Mixing Mode for Karaoke",
+ "Country Code for Parental Management",
+ "Parental Level",
+ "Player Configurations for Video",
+ "Player Configurations for Audio",
+ "Initial Language Code for Audio",
+ "Initial Language Code Extension for Audio",
+ "Initial Language Code for Sub-picture",
+ "Initial Language Code Extension for Sub-picture",
+ "Player Regional Code",
+ "Reserved 21",
+ "Reserved 22",
+ "Reserved 23"
+};
+
+static const char *system_reg_abbr_table[] = {
+ NULL,
+ "ASTN",
+ "SPSTN",
+ "AGLN",
+ "TTN",
+ "VTS_TTN",
+ "TT_PGCN",
+ "PTTN",
+ "HL_BTNN",
+ "NVTMR",
+ "NV_PGCN",
+ NULL,
+ "CC_PLT",
+ "PLT",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+static void print_system_reg(uint16_t reg) {
+ if(reg < sizeof(system_reg_abbr_table) / sizeof(char *))
+ fprintf(stderr, "%s (SRPM:%d)", system_reg_table[reg], reg);
+ else
+ fprintf(stderr, " WARNING: Unknown system register ( reg=%d ) ", reg);
+}
+
+static void print_reg(uint8_t reg) {
+ if(reg & 0x80)
+ print_system_reg(reg & 0x7f);
+ else
+ if(reg < 16)
+ fprintf(stderr, "g[%" PRIu8 "]", reg);
+ else
+ fprintf(stderr, " WARNING: Unknown general register ");
+}
+
+static void print_cmp_op(uint8_t op) {
+ if(op < sizeof(cmp_op_table) / sizeof(char *) && cmp_op_table[op] != NULL)
+ fprintf(stderr, " %s ", cmp_op_table[op]);
+ else
+ fprintf(stderr, " WARNING: Unknown compare op ");
+}
+
+static void print_set_op(uint8_t op) {
+ if(op < sizeof(set_op_table) / sizeof(char *) && set_op_table[op] != NULL)
+ fprintf(stderr, " %s ", set_op_table[op]);
+ else
+ fprintf(stderr, " WARNING: Unknown set op ");
+}
+
+static void print_reg_or_data(command_t* command, int immediate, int byte) {
+ if(immediate) {
+ int i = vm_getbits(command, (byte*8), 16);
+
+ fprintf(stderr, "0x%x", i);
+ if(isprint(i & 0xff) && isprint((i>>8) & 0xff))
+ fprintf(stderr, " (\"%c%c\")", (char)((i>>8) & 0xff), (char)(i & 0xff));
+ } else {
+ print_reg(vm_getbits(command, ((byte + 1)*8), 8));
+ }
+}
+
+static void print_reg_or_data_2(command_t* command, int immediate, int byte) {
+ if(immediate)
+ fprintf(stderr, "0x%x", vm_getbits(command, ((byte*8)+1), 7));
+ else
+ fprintf(stderr, "g[%" PRIu8 "]", vm_getbits(command, ((byte*8)+4), 4));
+}
+
+static void print_if_version_1(command_t* command) {
+ uint8_t op = vm_getbits(command, 9, 3);
+
+ if(op) {
+ fprintf(stderr, "if (");
+ print_reg(vm_getbits(command,24,8));
+ print_cmp_op(op);
+ print_reg_or_data(command, vm_getbits(command, 8,1), 4);
+ fprintf(stderr, ") ");
+ }
+}
+
+static void print_if_version_2(command_t* command) {
+ uint8_t op = vm_getbits(command, 9, 3);
+
+ if(op) {
+ fprintf(stderr, "if (");
+ print_reg(vm_getbits(command, 48, 8));
+ print_cmp_op(op);
+ print_reg(vm_getbits(command, 56, 8));
+ fprintf(stderr, ") ");
+ }
+}
+
+static void print_if_version_3(command_t* command) {
+ uint8_t op = vm_getbits(command, 9, 3);
+
+ if(op) {
+ fprintf(stderr, "if (");
+ print_reg(vm_getbits(command, 20, 4));
+ print_cmp_op(op);
+ print_reg_or_data(command, vm_getbits(command, 8, 1), 6);
+ fprintf(stderr, ") ");
+ }
+}
+
+static void print_if_version_4(command_t* command) {
+ uint8_t op = vm_getbits(command, 9, 3);
+
+ if(op) {
+ fprintf(stderr, "if (");
+ print_reg(vm_getbits(command, 12, 4));
+ print_cmp_op(op);
+ print_reg_or_data(command, vm_getbits(command, 8, 1), 4);
+ fprintf(stderr, ") ");
+ }
+}
+
+static void print_special_instruction(command_t* command) {
+ uint8_t op = vm_getbits(command, 12, 4);
+
+ switch(op) {
+ case 0: /* NOP */
+ fprintf(stderr, "Nop");
+ break;
+ case 1: /* Goto line */
+ fprintf(stderr, "Goto %" PRIu8, vm_getbits(command, 56, 8));
+ break;
+ case 2: /* Break */
+ fprintf(stderr, "Break");
+ break;
+ case 3: /* Parental level */
+ fprintf(stderr, "SetTmpPML %" PRIu8 ", Goto %" PRIu8,
+ vm_getbits(command, 52, 4), vm_getbits(command, 56, 8));
+ break;
+ default:
+ fprintf(stderr, "WARNING: Unknown special instruction (%i)",
+ vm_getbits(command, 12, 4));
+ }
+}
+
+static void print_linksub_instruction(command_t* command) {
+ int linkop = vm_getbits(command, 59, 5);
+ int button = vm_getbits(command, 48, 6);
+
+ if(linkop < sizeof(link_table)/sizeof(char *) && link_table[linkop] != NULL)
+ fprintf(stderr, "%s (button %" PRIu8 ")", link_table[linkop], button);
+ else
+ fprintf(stderr, "WARNING: Unknown linksub instruction (%i)", linkop);
+}
+
+static void print_link_instruction(command_t* command, int optional) {
+ uint8_t op = vm_getbits(command, 12, 4);
+
+ if(optional && op)
+ fprintf(stderr, ", ");
+
+ switch(op) {
+ case 0:
+ if(!optional)
+ fprintf(stderr, "WARNING: NOP (link)!");
+ break;
+ case 1:
+ print_linksub_instruction(command);
+ break;
+ case 4:
+ fprintf(stderr, "LinkPGCN %" PRIu16, vm_getbits(command, 49, 15));
+ break;
+ case 5:
+ fprintf(stderr, "LinkPTT %" PRIu16 " (button %" PRIu8 ")",
+ vm_getbits(command, 54, 10), vm_getbits(command, 48, 6));
+ break;
+ case 6:
+ fprintf(stderr, "LinkPGN %" PRIu8 " (button %" PRIu8 ")",
+ vm_getbits(command, 57, 7), vm_getbits(command, 48, 6));
+ break;
+ case 7:
+ fprintf(stderr, "LinkCN %" PRIu8 " (button %" PRIu8 ")",
+ vm_getbits(command, 56, 8), vm_getbits(command, 48, 6));
+ break;
+ default:
+ fprintf(stderr, "WARNING: Unknown link instruction");
+ }
+}
+
+static void print_jump_instruction(command_t* command) {
+ switch(vm_getbits(command, 12, 4)) {
+ case 1:
+ fprintf(stderr, "Exit");
+ break;
+ case 2:
+ fprintf(stderr, "JumpTT %" PRIu8, vm_getbits(command, 41, 7));
+ break;
+ case 3:
+ fprintf(stderr, "JumpVTS_TT %" PRIu8, vm_getbits(command, 41, 7));
+ break;
+ case 5:
+ fprintf(stderr, "JumpVTS_PTT %" PRIu8 ":%" PRIu16,
+ vm_getbits(command, 41, 7), vm_getbits(command, 22, 10));
+ break;
+ case 6:
+ switch(vm_getbits(command, 40, 2)) {
+ case 0:
+ fprintf(stderr, "JumpSS FP");
+ break;
+ case 1:
+ fprintf(stderr, "JumpSS VMGM (menu %" PRIu8 ")", vm_getbits(command, 44, 4));
+ break;
+ case 2:
+ fprintf(stderr, "JumpSS VTSM (vts %" PRIu8 ", title %" PRIu8
+ ", menu %" PRIu8 ")", vm_getbits(command, 32, 8), vm_getbits(command, 24, 8), vm_getbits(command, 44, 4));
+ break;
+ case 3:
+ fprintf(stderr, "JumpSS VMGM (pgc %" PRIu8 ")", vm_getbits(command, 17, 15));
+ break;
+ }
+ break;
+ case 8:
+ switch(vm_getbits(command, 40, 2)) {
+ case 0:
+ fprintf(stderr, "CallSS FP (rsm_cell %" PRIu8 ")",
+ vm_getbits(command, 32, 8));
+ break;
+ case 1:
+ fprintf(stderr, "CallSS VMGM (menu %" PRIu8
+ ", rsm_cell %" PRIu8 ")", vm_getbits(command, 44, 4), vm_getbits(command, 32, 8));
+ break;
+ case 2:
+ fprintf(stderr, "CallSS VTSM (menu %" PRIu8
+ ", rsm_cell %" PRIu8 ")", vm_getbits(command, 44, 4), vm_getbits(command, 32, 8));
+ break;
+ case 3:
+ fprintf(stderr, "CallSS VMGM (pgc %" PRIu8 ", rsm_cell %" PRIu8 ")",
+ vm_getbits(command, 17, 15), vm_getbits(command, 32, 8));
+ break;
+ }
+ break;
+ default:
+ fprintf(stderr, "WARNING: Unknown Jump/Call instruction");
+ }
+}
+
+static void print_system_set(command_t* command) {
+ int i;
+
+ switch(vm_getbits(command, 4, 4)) {
+ case 1: /* Set system reg 1 &| 2 &| 3 (Audio, Subp. Angle) */
+ for(i = 1; i <= 3; i++) {
+ if(vm_getbits(command, ((2+i)*8), 1)) {
+ print_system_reg(i);
+ fprintf(stderr, " = ");
+ print_reg_or_data_2(command, vm_getbits(command, 3, 1), 2 + i);
+ fprintf(stderr, " ");
+ }
+ }
+ break;
+ case 2: /* Set system reg 9 & 10 (Navigation timer, Title PGC number) */
+ print_system_reg(9);
+ fprintf(stderr, " = ");
+ print_reg_or_data(command, vm_getbits(command, 3, 1), 2);
+ fprintf(stderr, " ");
+ print_system_reg(10);
+ fprintf(stderr, " = %" PRIu8, vm_getbits(command, 40, 8)); /* ?? */
+ break;
+ case 3: /* Mode: Counter / Register + Set */
+ fprintf(stderr, "SetMode ");
+ if(vm_getbits(command, 40, 1))
+ fprintf(stderr, "Counter ");
+ else
+ fprintf(stderr, "Register ");
+ print_reg(vm_getbits(command, 44, 4));
+ print_set_op(0x1); /* '=' */
+ print_reg_or_data(command, vm_getbits(command, 3, 1), 2);
+ break;
+ case 6: /* Set system reg 8 (Highlighted button) */
+ print_system_reg(8);
+ if(vm_getbits(command, 3, 1)) /* immediate */
+ fprintf(stderr, " = 0x%x (button no %d)", vm_getbits(command, 32, 16), vm_getbits(command, 32, 6));
+ else
+ fprintf(stderr, " = g[%" PRIu8 "]", vm_getbits(command, 44, 4));
+ break;
+ default:
+ fprintf(stderr, "WARNING: Unknown system set instruction (%i)",
+ vm_getbits(command, 4, 4));
+ }
+}
+
+static void print_set_version_1(command_t* command) {
+ uint8_t set_op = vm_getbits(command, 4, 4);
+
+ if(set_op) {
+ print_reg(vm_getbits(command, 24, 8)); /* FIXME: This is different from decoder.c!!! */
+ print_set_op(set_op);
+ print_reg_or_data(command, vm_getbits(command, 3, 1), 4);
+ } else {
+ fprintf(stderr, "NOP");
+ }
+}
+
+static void print_set_version_2(command_t* command) {
+ uint8_t set_op = vm_getbits(command, 4, 4);
+
+ if(set_op) {
+ print_reg(vm_getbits(command, 12, 4));
+ print_set_op(set_op);
+ print_reg_or_data(command, vm_getbits(command, 3, 1), 2);
+ } else {
+ fprintf(stderr, "NOP");
+ }
+}
+
+void vmPrint_mnemonic(vm_cmd_t *vm_command) {
+ command_t command;
+ command.instruction =( (uint64_t) vm_command->bytes[0] << 56 ) |
+ ( (uint64_t) vm_command->bytes[1] << 48 ) |
+ ( (uint64_t) vm_command->bytes[2] << 40 ) |
+ ( (uint64_t) vm_command->bytes[3] << 32 ) |
+ ( (uint64_t) vm_command->bytes[4] << 24 ) |
+ ( (uint64_t) vm_command->bytes[5] << 16 ) |
+ ( (uint64_t) vm_command->bytes[6] << 8 ) |
+ (uint64_t) vm_command->bytes[7] ;
+ command.examined = 0;
+
+ switch(vm_getbits(&command,0,3)) { /* three first bits */
+ case 0: /* Special instructions */
+ print_if_version_1(&command);
+ print_special_instruction(&command);
+ break;
+ case 1: /* Jump/Call or Link instructions */
+ if(vm_getbits(&command,3,1)) {
+ print_if_version_2(&command);
+ print_jump_instruction(&command);
+ } else {
+ print_if_version_1(&command);
+ print_link_instruction(&command, 0); /* must be pressent */
+ }
+ break;
+ case 2: /* Set System Parameters instructions */
+ print_if_version_2(&command);
+ print_system_set(&command);
+ print_link_instruction(&command, 1); /* either 'if' or 'link' */
+ break;
+ case 3: /* Set General Parameters instructions */
+ print_if_version_3(&command);
+ print_set_version_1(&command);
+ print_link_instruction(&command, 1); /* either 'if' or 'link' */
+ break;
+ case 4: /* Set, Compare -> LinkSub instructions */
+ print_set_version_2(&command);
+ fprintf(stderr, ", ");
+ print_if_version_4(&command);
+ print_linksub_instruction(&command);
+ break;
+ case 5: /* Compare -> (Set and LinkSub) instructions */
+ print_if_version_4(&command);
+ fprintf(stderr, "{ ");
+ print_set_version_2(&command);
+ fprintf(stderr, ", ");
+ print_linksub_instruction(&command);
+ fprintf(stderr, " }");
+ break;
+ case 6: /* Compare -> Set, always LinkSub instructions */
+ print_if_version_4(&command);
+ fprintf(stderr, "{ ");
+ print_set_version_2(&command);
+ fprintf(stderr, " } ");
+ print_linksub_instruction(&command);
+ break;
+ default:
+ fprintf(stderr, "WARNING: Unknown instruction type (%i)", vm_getbits(&command, 0, 3));
+ }
+ /* Check if there still are bits set that were not examined */
+
+ if(command.instruction & ~ command.examined) {
+ fprintf(stderr, " libdvdnav: vmcmd.c: [WARNING, unknown bits:");
+ fprintf(stderr, " %08llx", (command.instruction & ~ command.examined) );
+ fprintf(stderr, "]");
+ }
+}
+
+void vmPrint_CMD(int row, vm_cmd_t *vm_command) {
+ int i;
+
+ fprintf(stderr, "(%03d) ", row + 1);
+ for(i = 0; i < 8; i++)
+ fprintf(stderr, "%02x ", vm_command->bytes[i]);
+ fprintf(stderr, "| ");
+
+ vmPrint_mnemonic(vm_command);
+ fprintf(stderr, "\n");
+}
+
+
diff --git a/src/input/libdvdnav/vmcmd.h b/src/input/libdvdnav/vmcmd.h
new file mode 100644
index 000000000..c4079f245
--- /dev/null
+++ b/src/input/libdvdnav/vmcmd.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2000, 2001 Martin Norbäck, Håkan Hjort
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav 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.
+ *
+ * libdvdnav 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
+ *
+ * $Id: vmcmd.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifndef VMCMD_H_INCLUDED
+#define VMCMD_H_INCLUDED
+
+#include <inttypes.h>
+#include <dvdread/ifo_types.h> /* Only for vm_cmd_t */
+#include "decoder.h"
+
+void vmPrint_mnemonic(vm_cmd_t *command);
+void vmPrint_CMD(int row, vm_cmd_t *command);
+
+#endif /* VMCMD_H_INCLUDED */
diff --git a/src/input/libdvdread/Makefile.am b/src/input/libdvdread/Makefile.am
new file mode 100644
index 000000000..2bb8c9f1c
--- /dev/null
+++ b/src/input/libdvdread/Makefile.am
@@ -0,0 +1,29 @@
+DVD_CFLAGS = -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE \
+ -Ilibdvdnav/ \
+ -Ilibdvdread/
+
+CFLAGS = @CFLAGS@ $(DVD_CFLAGS)
+
+noinst_LTLIBRARIES = libdvdread.la
+
+libdvdread_la_SOURCES = dvd_input.c dvd_reader.c dvd_udf.c ifo_read.c \
+ ifo_print.c nav_read.c nav_print.c bswap.h dvd_udf.h dvd_input.h
+libdvdread_la_LDFLAGS = $(THREAD_LIBS) \
+ -avoid-version -module
+
+noinst_HEADERS = dvd_reader.h ifo_types.h ifo_read.h \
+ ifo_print.h nav_types.h nav_read.h nav_print.h
+
+debug:
+ @$(MAKE) CFLAGS="$(DEBUG_CFLAGS) $(OGG_CFLAGS) $(VORBIS_CFLAGS)"
+
+install-debug: debug
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+mostlyclean-generic:
+ -rm -f *~ \#* .*~ .\#*
+
+maintainer-clean-generic:
+ -@echo "This command is intended for maintainers to use;"
+ -@echo "it deletes files that may require special tools to rebuild."
+ -rm -f Makefile.in
diff --git a/src/input/libdvdread/bswap.h b/src/input/libdvdread/bswap.h
new file mode 100644
index 000000000..dd7417059
--- /dev/null
+++ b/src/input/libdvdread/bswap.h
@@ -0,0 +1,87 @@
+#ifndef BSWAP_H_INCLUDED
+#define BSWAP_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001 Billy Biggs <vektor@dumbterm.net>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <config.h>
+
+#if defined(WORDS_BIGENDIAN)
+/* All bigendian systems are fine, just ignore the swaps. */
+#define B2N_16(x) (void)(x)
+#define B2N_32(x) (void)(x)
+#define B2N_64(x) (void)(x)
+
+#else
+
+#if defined(__linux__)
+#include <byteswap.h>
+#define B2N_16(x) x = bswap_16(x)
+#define B2N_32(x) x = bswap_32(x)
+#define B2N_64(x) x = bswap_64(x)
+
+#elif defined(__NetBSD__)
+#include <sys/endian.h>
+#define B2N_16(x) BE16TOH(x)
+#define B2N_32(x) BE32TOH(x)
+#define B2N_64(x) BE64TOH(x)
+
+#elif defined(__OpenBSD__)
+#include <sys/endian.h>
+#define B2N_16(x) x = swap16(x)
+#define B2N_32(x) x = swap32(x)
+#define B2N_64(x) x = swap64(x)
+
+/* This is a slow but portable implementation, it has multiple evaluation
+ * problems so beware.
+ * FreeBSD and Solaris don't have <byteswap.h> or any other such
+ * functionality!
+ */
+
+#elif defined(__FreeBSD__) || defined(__sun) || defined(__bsdi__)
+#define B2N_16(x) \
+ x = ((((x) & 0xff00) >> 8) | \
+ (((x) & 0x00ff) << 8))
+#define B2N_32(x) \
+ x = ((((x) & 0xff000000) >> 24) | \
+ (((x) & 0x00ff0000) >> 8) | \
+ (((x) & 0x0000ff00) << 8) | \
+ (((x) & 0x000000ff) << 24))
+#define B2N_64(x) \
+ x = ((((x) & 0xff00000000000000) >> 56) | \
+ (((x) & 0x00ff000000000000) >> 40) | \
+ (((x) & 0x0000ff0000000000) >> 24) | \
+ (((x) & 0x000000ff00000000) >> 8) | \
+ (((x) & 0x00000000ff000000) << 8) | \
+ (((x) & 0x0000000000ff0000) << 24) | \
+ (((x) & 0x000000000000ff00) << 40) | \
+ (((x) & 0x00000000000000ff) << 56))
+
+#else
+
+/* If there isn't a header provided with your system with this functionality
+ * add the relevant || define( ) to the portable implementation above.
+ */
+#error "You need to add endian swap macros for you're system"
+
+#endif
+
+#endif /* WORDS_BIGENDIAN */
+
+#endif /* BSWAP_H_INCLUDED */
diff --git a/src/input/libdvdread/dvd_input.c b/src/input/libdvdread/dvd_input.c
new file mode 100644
index 000000000..c2d788fdb
--- /dev/null
+++ b/src/input/libdvdread/dvd_input.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2002 Samuel Hocevar <sam@zoy.org>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dlfcn.h>
+
+#include "dvd_reader.h"
+#include "dvd_input.h"
+
+/* For libdvdcss */
+typedef struct dvdcss_s *dvdcss_handle;
+
+dvdcss_handle (*DVDcss_open) (const char *);
+int (*DVDcss_close) (dvdcss_handle);
+int (*DVDcss_seek) (dvdcss_handle, int, int);
+int (*DVDcss_title) (dvdcss_handle, int);
+int (*DVDcss_read) (dvdcss_handle, void *, int, int);
+char * (*DVDcss_error) (dvdcss_handle);
+
+
+/* The DVDinput handle, add stuff here for new input methods. */
+struct dvd_input_s {
+ /* libdvdcss handle */
+ dvdcss_handle dvdcss;
+
+ /* dummy file input */
+ int fd;
+};
+
+
+/**
+ * initialize and open a DVD device or file.
+ */
+static dvd_input_t css_open(const char *target)
+{
+ dvd_input_t dev;
+
+ /* Allocate the handle structure */
+ dev = (dvd_input_t) malloc(sizeof(dvd_input_t));
+ if(dev == NULL) {
+ fprintf(stderr, "libdvdread: Could not allocate memory.\n");
+ return NULL;
+ }
+
+ /* Really open it with libdvdcss */
+ dev->dvdcss = DVDcss_open(target);
+ if(dev->dvdcss == 0) {
+ fprintf(stderr, "libdvdread: Could not open device with libdvdcss.\n");
+ free(dev);
+ return NULL;
+ }
+
+ return dev;
+}
+
+/**
+ * return the last error message
+ */
+static char *css_error(dvd_input_t dev)
+{
+ return DVDcss_error(dev->dvdcss);
+}
+
+/**
+ * seek into the device.
+ */
+static int css_seek(dvd_input_t dev, int blocks, int flags)
+{
+ return DVDcss_seek(dev->dvdcss, blocks, flags);
+}
+
+/**
+ * set the block for the begining of a new title (key).
+ */
+static int css_title(dvd_input_t dev, int block)
+{
+ return DVDcss_title(dev->dvdcss, block);
+}
+
+/**
+ * read data from the device.
+ */
+static int css_read(dvd_input_t dev, void *buffer, int blocks, int flags)
+{
+ return DVDcss_read(dev->dvdcss, buffer, blocks, flags);
+}
+
+/**
+ * close the DVD device and clean up the library.
+ */
+static int css_close(dvd_input_t dev)
+{
+ int ret;
+
+ ret = DVDcss_close(dev->dvdcss);
+
+ if(ret < 0)
+ return ret;
+
+ free(dev);
+
+ return 0;
+}
+
+
+
+
+
+
+/**
+ * initialize and open a DVD device or file.
+ */
+static dvd_input_t file_open(const char *target)
+{
+ dvd_input_t dev;
+
+ /* Allocate the library structure */
+ dev = (dvd_input_t) malloc(sizeof(dvd_input_t));
+ if(dev == NULL) {
+ fprintf(stderr, "libdvdread: Could not allocate memory.\n");
+ return NULL;
+ }
+
+ /* Open the device */
+ dev->fd = open(target, O_RDONLY);
+ if(dev->fd < 0) {
+ perror("libdvdread: Could not open input");
+ free(dev);
+ return NULL;
+ }
+
+ return dev;
+}
+
+/**
+ * return the last error message
+ */
+static char *file_error(dvd_input_t dev)
+{
+ /* use strerror(errno)? */
+ return "unknown error";
+}
+
+/**
+ * seek into the device.
+ */
+static int file_seek(dvd_input_t dev, int blocks, int flags)
+{
+ off_t pos;
+
+ pos = lseek(dev->fd, (off_t)blocks * (off_t)DVD_VIDEO_LB_LEN, SEEK_SET);
+ if(pos < 0) {
+ return pos;
+ }
+ /* assert pos % DVD_VIDEO_LB_LEN == 0 */
+ return (int) (pos / DVD_VIDEO_LB_LEN);
+}
+
+/**
+ * set the block for the begining of a new title (key).
+ */
+static int file_title(dvd_input_t dev, int block)
+{
+ return -1;
+}
+
+/**
+ * read data from the device.
+ */
+static int file_read(dvd_input_t dev, void *buffer, int blocks, int flags)
+{
+ size_t len;
+ ssize_t ret;
+
+ len = (size_t)blocks * DVD_VIDEO_LB_LEN;
+
+ while(len > 0) {
+
+ ret = read(dev->fd, buffer, len);
+
+ if(ret < 0) {
+ /* One of the reads failed, too bad. We won't even bother
+ * returning the reads that went ok, and as in the posix spec
+ * the file postition is left unspecified after a failure. */
+ return ret;
+ }
+
+ if(ret == 0) {
+ /* Nothing more to read. Return the whole blocks, if any, that we got.
+ and adjust the file possition back to the previous block boundary. */
+ size_t bytes = (size_t)blocks * DVD_VIDEO_LB_LEN - len;
+ off_t over_read = -(bytes % DVD_VIDEO_LB_LEN);
+ /*off_t pos =*/ lseek(dev->fd, over_read, SEEK_CUR);
+ /* should have pos % 2048 == 0 */
+ return (int) (bytes / DVD_VIDEO_LB_LEN);
+ }
+
+ len -= ret;
+ }
+
+ return blocks;
+}
+
+/**
+ * close the DVD device and clean up.
+ */
+static int file_close(dvd_input_t dev)
+{
+ int ret;
+
+ ret = close(dev->fd);
+
+ if(ret < 0)
+ return ret;
+
+ free(dev);
+
+ return 0;
+}
+
+
+/**
+ * Setup read functions with either libdvdcss or minimal DVD access.
+ */
+int DVDInputSetup(void)
+{
+ void *dvdcss_library = NULL;
+ char **dvdcss_version = NULL;
+
+ dvdcss_library = dlopen("libdvdcss.so.2", RTLD_LAZY);
+
+ if(dvdcss_library != NULL) {
+#if defined(__OpenBSD__) && !defined(__ELF__)
+#define U_S "_"
+#else
+#define U_S
+#endif
+ DVDcss_open = (dvdcss_handle (*)(const char*))
+ dlsym(dvdcss_library, U_S "dvdcss_open");
+ DVDcss_close = (int (*)(dvdcss_handle))
+ dlsym(dvdcss_library, U_S "dvdcss_close");
+ DVDcss_title = (int (*)(dvdcss_handle, int))
+ dlsym(dvdcss_library, U_S "dvdcss_title");
+ DVDcss_seek = (int (*)(dvdcss_handle, int, int))
+ dlsym(dvdcss_library, U_S "dvdcss_seek");
+ DVDcss_read = (int (*)(dvdcss_handle, void*, int, int))
+ dlsym(dvdcss_library, U_S "dvdcss_read");
+ DVDcss_error = (char* (*)(dvdcss_handle))
+ dlsym(dvdcss_library, U_S "dvdcss_error");
+
+ dvdcss_version = (char **)dlsym(dvdcss_library, U_S "dvdcss_interface_2");
+
+ if(dlsym(dvdcss_library, U_S "dvdcss_crack")) {
+ fprintf(stderr,
+ "libdvdread: Old (pre-0.0.2) version of libdvdcss found.\n"
+ "libdvdread: You should get the latest version from "
+ "http://www.videolan.org/\n" );
+ dlclose(dvdcss_library);
+ dvdcss_library = NULL;
+ } else if(!DVDcss_open || !DVDcss_close || !DVDcss_title || !DVDcss_seek
+ || !DVDcss_read || !DVDcss_error || !dvdcss_version) {
+ fprintf(stderr, "libdvdread: Missing symbols in libdvdcss.so.2, "
+ "this shouldn't happen !\n");
+ dlclose(dvdcss_library);
+ }
+ }
+
+ if(dvdcss_library != NULL) {
+ /*
+ char *psz_method = getenv( "DVDCSS_METHOD" );
+ char *psz_verbose = getenv( "DVDCSS_VERBOSE" );
+ fprintf(stderr, "DVDCSS_METHOD %s\n", psz_method);
+ fprintf(stderr, "DVDCSS_VERBOSE %s\n", psz_verbose);
+ */
+ fprintf(stderr, "libdvdread: Using libdvdcss version %s for DVD access\n",
+ *dvdcss_version);
+
+ /* libdvdcss wraper functions */
+ DVDinput_open = css_open;
+ DVDinput_close = css_close;
+ DVDinput_seek = css_seek;
+ DVDinput_title = css_title;
+ DVDinput_read = css_read;
+ DVDinput_error = css_error;
+ return 1;
+
+ } else {
+ fprintf(stderr, "libdvdread: Encrypted DVD support unavailable.\n");
+
+ /* libdvdcss replacement functions */
+ DVDinput_open = file_open;
+ DVDinput_close = file_close;
+ DVDinput_seek = file_seek;
+ DVDinput_title = file_title;
+ DVDinput_read = file_read;
+ DVDinput_error = file_error;
+ return 0;
+ }
+}
diff --git a/src/input/libdvdread/dvd_input.h b/src/input/libdvdread/dvd_input.h
new file mode 100644
index 000000000..638d60aeb
--- /dev/null
+++ b/src/input/libdvdread/dvd_input.h
@@ -0,0 +1,51 @@
+#ifndef DVD_INPUT_H_INCLUDED
+#define DVD_INPUT_H_INCLUDED
+
+/*
+ * Copyright (C) 2001, 2002 Samuel Hocevar <sam@zoy.org>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+ */
+
+/**
+ * Defines and flags. Make sure they fit the libdvdcss API!
+ */
+#define DVDINPUT_NOFLAGS 0
+
+#define DVDINPUT_READ_DECRYPT (1 << 0)
+
+#define DVDINPUT_SEEK_MPEG (1 << 0)
+#define DVDINPUT_SEEK_KEY (1 << 1)
+
+
+typedef struct dvd_input_s *dvd_input_t;
+
+/**
+ * Pointers which will be filled either the input meathods functions.
+ */
+dvd_input_t (*DVDinput_open) (const char *);
+int (*DVDinput_close) (dvd_input_t);
+int (*DVDinput_seek) (dvd_input_t, int, int);
+int (*DVDinput_title) (dvd_input_t, int);
+int (*DVDinput_read) (dvd_input_t, void *, int, int);
+char * (*DVDinput_error) (dvd_input_t);
+
+/**
+ * Setup function accessed by dvd_reader.c. Returns 1 if there is CSS support.
+ */
+int DVDInputSetup(void);
+
+#endif /* DVD_INPUT_H_INCLUDED */
diff --git a/src/input/libdvdread/dvd_reader.c b/src/input/libdvdread/dvd_reader.c
new file mode 100644
index 000000000..b296a49de
--- /dev/null
+++ b/src/input/libdvdread/dvd_reader.c
@@ -0,0 +1,898 @@
+/*
+ * Copyright (C) 2001, 2002 Billy Biggs <vektor@dumbterm.net>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, 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__)|| defined(__DARWIN__)
+#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
+
+#include "dvd_udf.h"
+#include "dvd_input.h"
+#include "dvd_reader.h"
+
+struct dvd_reader_s {
+ /* Basic information. */
+ int isImageFile;
+
+ /* Hack for keeping track of the css status.
+ * 0: no css, 1: perhaps (need init of keys), 2: have done init */
+ int css_state;
+ int css_title; /* Last title that we have called DVDinpute_title for. */
+
+ /* Information required for an image file. */
+ dvd_input_t dev;
+
+ /* Information required for a directory path drive. */
+ char *path_root;
+};
+
+struct dvd_file_s {
+ /* Basic information. */
+ dvd_reader_t *dvd;
+
+ /* Hack for selecting the right css title. */
+ int css_title;
+
+ /* Information required for an image file. */
+ uint32_t lb_start;
+ uint32_t seek_pos;
+
+ /* Information required for a directory path drive. */
+ size_t title_sizes[ 9 ];
+ dvd_input_t title_devs[ 9 ];
+
+ /* Calculated at open-time, size in blocks. */
+ ssize_t filesize;
+};
+
+/* 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( DVDinput_title( dvd->dev, (int)start ) < 0 ) {
+ fprintf( stderr, "libdvdread: Error cracking CSS key for %s (0x%08x)\n", filename, start);
+ }
+ gettimeofday( &t_e, NULL );
+ fprintf( stderr, "libdvdread: Elapsed time %ld\n",
+ (long int) t_e.tv_sec - t_s.tv_sec );
+ }
+
+ if( title == 0 ) continue;
+
+ gettimeofday( &t_s, NULL );
+ sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, 1 );
+ start = UDFFindFile( dvd, filename, &len );
+ if( start == 0 || len == 0 ) break;
+
+ /* Perform CSS key cracking for this title. */
+ fprintf( stderr, "libdvdread: Get key for %s at 0x%08x\n",
+ filename, start );
+ if( DVDinput_title( dvd->dev, (int)start ) < 0 ) {
+ fprintf( stderr, "libdvdread: Error cracking CSS key for %s (0x%08x)!!\n", filename, start);
+ }
+ gettimeofday( &t_e, NULL );
+ fprintf( stderr, "libdvdread: Elapsed time %ld\n",
+ (long int) t_e.tv_sec - t_s.tv_sec );
+ }
+ title--;
+
+ fprintf( stderr, "libdvdread: Found %d VTS's\n", title );
+ gettimeofday(&all_e, NULL);
+ fprintf( stderr, "libdvdread: Elapsed time %ld\n",
+ (long int) all_e.tv_sec - all_s.tv_sec );
+
+ return 0;
+}
+
+
+
+/**
+ * Open a DVD image or block device file.
+ */
+static dvd_reader_t *DVDOpenImageFile( const char *location, int have_css )
+{
+ dvd_reader_t *dvd;
+ dvd_input_t dev;
+
+ dev = DVDinput_open( location );
+ if( !dev ) {
+ fprintf( stderr, "libdvdread: Can't open %s for reading\n", location );
+ return 0;
+ }
+
+ dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
+ if( !dvd ) return 0;
+ dvd->isImageFile = 1;
+ dvd->dev = dev;
+ dvd->path_root = 0;
+
+ if( have_css ) {
+ /* Only if DVDCSS_METHOD = title, a bit if it's disc or if
+ * DVDCSS_METHOD = key but region missmatch. Unfortunaly we
+ * don't have that information. */
+
+ dvd->css_state = 1; /* Need key init. */
+ }
+
+ return dvd;
+}
+
+static dvd_reader_t *DVDOpenPath( const char *path_root )
+{
+ dvd_reader_t *dvd;
+
+ dvd = (dvd_reader_t *) malloc( sizeof( dvd_reader_t ) );
+ if( !dvd ) return 0;
+ dvd->isImageFile = 0;
+ dvd->dev = 0;
+ dvd->path_root = strdup( path_root );
+
+ return dvd;
+}
+
+#if defined(__sun)
+/* /dev/rdsk/c0t6d0s0 (link to /devices/...)
+ /vol/dev/rdsk/c0t6d0/??
+ /vol/rdsk/<name> */
+static char *sun_block2char( const char *path )
+{
+ char *new_path;
+
+ /* Must contain "/dsk/" */
+ if( !strstr( path, "/dsk/" ) ) return (char *) strdup( path );
+
+ /* Replace "/dsk/" with "/rdsk/" */
+ new_path = malloc( strlen(path) + 2 );
+ strcpy( new_path, path );
+ strcpy( strstr( new_path, "/dsk/" ), "" );
+ strcat( new_path, "/rdsk/" );
+ strcat( new_path, strstr( path, "/dsk/" ) + strlen( "/dsk/" ) );
+
+ return new_path;
+}
+#endif
+
+#if defined(SYS_BSD)
+/* FreeBSD /dev/(r)(a)cd0c (a is for atapi), recomended to _not_ use r
+ OpenBSD /dev/rcd0c, it needs to be the raw device
+ NetBSD /dev/rcd0[d|c|..] d for x86, c (for non x86), perhaps others
+ Darwin /dev/rdisk0, it needs to be the raw device
+ BSD/OS /dev/sr0c (if not mounted) or /dev/rsr0c ('c' any letter will do) */
+static char *bsd_block2char( const char *path )
+{
+ char *new_path;
+
+ /* If it doesn't start with "/dev/" or does start with "/dev/r" exit */
+ if( !strncmp( path, "/dev/", 5 ) || strncmp( path, "/dev/r", 6 ) )
+ return (char *) strdup( path );
+
+ /* Replace "/dev/" with "/dev/r" */
+ new_path = malloc( strlen(path) + 2 );
+ strcpy( new_path, "/dev/r" );
+ strcat( new_path, path + strlen( "/dev/" ) );
+
+ return new_path;
+}
+#endif
+
+dvd_reader_t *DVDOpen( const char *path )
+{
+ struct stat fileinfo;
+ int ret, have_css;
+ char *dev_name = 0;
+
+ if( !path ) return 0;
+
+ ret = stat( path, &fileinfo );
+ if( ret < 0 ) {
+ /* If we can't stat the file, give up */
+ fprintf( stderr, "libdvdread: Can't stat %s\n", path );
+ perror("");
+ return 0;
+ }
+
+ /* Try to open libdvdcss or fall back to standard functions */
+ have_css = DVDInputSetup();
+
+ /* First check if this is a block/char device or a file*/
+ if( S_ISBLK( fileinfo.st_mode ) ||
+ S_ISCHR( fileinfo.st_mode ) ||
+ S_ISREG( fileinfo.st_mode ) ) {
+
+ /**
+ * Block devices and regular files are assumed to be DVD-Video images.
+ */
+#if defined(__sun)
+ return DVDOpenImageFile( sun_block2char( path ), have_css );
+#elif defined(SYS_BSD)
+ return DVDOpenImageFile( bsd_block2char( path ), have_css );
+#else
+ return DVDOpenImageFile( path, have_css );
+#endif
+
+ } else if( S_ISDIR( fileinfo.st_mode ) ) {
+ dvd_reader_t *auth_drive = 0;
+ char *path_copy;
+#if defined(SYS_BSD)
+ struct fstab* fe;
+#elif defined(__sun) || defined(__linux__)
+ 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_copy[ strlen( path_copy ) - 1 ] == '/' )
+ path_copy[ strlen( path_copy ) - 1 ] = '\0';
+ }
+
+ if( strlen( path_copy ) > 9 ) {
+ if( !strcasecmp( &(path_copy[ strlen( path_copy ) - 9 ]),
+ "/video_ts" ) ) {
+ path_copy[ strlen( path_copy ) - 9 ] = '\0';
+ }
+ }
+
+#if defined(SYS_BSD)
+ if( ( fe = getfsfile( path_copy ) ) ) {
+ dev_name = bsd_block2char( fe->fs_spec );
+ fprintf( stderr,
+ "libdvdread: Attempting to use device %s"
+ " mounted on %s for CSS authentication\n",
+ dev_name,
+ fe->fs_file );
+ auth_drive = DVDOpenImageFile( dev_name, have_css );
+ }
+#elif defined(__sun)
+ mntfile = fopen( MNTTAB, "r" );
+ if( mntfile ) {
+ struct mnttab mp;
+ int res;
+
+ while( ( res = getmntent( mntfile, &mp ) ) != -1 ) {
+ if( res == 0 && !strcmp( mp.mnt_mountp, path_copy ) ) {
+ dev_name = sun_block2char( mp.mnt_special );
+ fprintf( stderr,
+ "libdvdread: Attempting to use device %s"
+ " mounted on %s for CSS authentication\n",
+ dev_name,
+ mp.mnt_mountp );
+ auth_drive = DVDOpenImageFile( dev_name, have_css );
+ break;
+ }
+ }
+ fclose( mntfile );
+ }
+#elif defined(__linux__)
+ mntfile = fopen( MOUNTED, "r" );
+ if( mntfile ) {
+ struct mntent *me;
+
+ while( ( me = getmntent( mntfile ) ) ) {
+ if( !strcmp( me->mnt_dir, path_copy ) ) {
+ fprintf( stderr,
+ "libdvdread: Attempting to use device %s"
+ " mounted on %s for CSS authentication\n",
+ me->mnt_fsname,
+ me->mnt_dir );
+ auth_drive = DVDOpenImageFile( me->mnt_fsname, have_css );
+ dev_name = strdup(me->mnt_fsname);
+ break;
+ }
+ }
+ fclose( mntfile );
+ }
+#endif
+ if( !dev_name ) {
+ fprintf( stderr, "libdvdread: Couldn't find device name.\n" );
+ } else if( !auth_drive ) {
+ fprintf( stderr, "libdvdread: Device %s inaccessible, "
+ "CSS authentication not available.\n", dev_name );
+ }
+
+ free( dev_name );
+ free( path_copy );
+
+ /**
+ * If we've opened a drive, just use that.
+ */
+ if( auth_drive ) return auth_drive;
+
+ /**
+ * Otherwise, we now try to open the directory tree instead.
+ */
+ return DVDOpenPath( path );
+ }
+
+ /* If it's none of the above, screw it. */
+ fprintf( stderr, "libdvdread: Could not open %s\n", path );
+ return 0;
+}
+
+void DVDClose( dvd_reader_t *dvd )
+{
+ if( dvd ) {
+ if( dvd->dev ) DVDinput_close( dvd->dev );
+ if( dvd->path_root ) free( dvd->path_root );
+ 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_sizes, 0, sizeof( dvd_file->title_sizes ) );
+ memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
+ dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
+
+ return dvd_file;
+}
+
+/**
+ * Searches for <file> in directory <path>, ignoring case.
+ * Returns 0 and full filename in <filename>.
+ * or -1 on file not found.
+ * or -2 on path not found.
+ */
+static int findDirFile( const char *path, const char *file, char *filename )
+{
+ DIR *dir;
+ struct dirent *ent;
+
+ dir = opendir( path );
+ if( !dir ) return -2;
+
+ while( ( ent = readdir( dir ) ) != NULL ) {
+ if( !strcasecmp( ent->d_name, file ) ) {
+ sprintf( filename, "%s%s%s", path,
+ ( ( path[ strlen( path ) - 1 ] == '/' ) ? "" : "/" ),
+ ent->d_name );
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int findDVDFile( dvd_reader_t *dvd, const char *file, char *filename )
+{
+ char video_path[ PATH_MAX + 1 ];
+ const char *nodirfile;
+ int ret;
+
+ /* Strip off the directory for our search */
+ if( !strncasecmp( "/VIDEO_TS/", file, 10 ) ) {
+ nodirfile = &(file[ 10 ]);
+ } else {
+ nodirfile = file;
+ }
+
+ ret = findDirFile( dvd->path_root, nodirfile, filename );
+ if( ret < 0 ) {
+ /* Try also with adding the path, just in case. */
+ sprintf( video_path, "%s/VIDEO_TS/", dvd->path_root );
+ ret = findDirFile( video_path, nodirfile, filename );
+ if( ret < 0 ) {
+ /* Try with the path, but in lower case. */
+ sprintf( video_path, "%s/video_ts/", dvd->path_root );
+ ret = findDirFile( video_path, nodirfile, filename );
+ if( ret < 0 ) {
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * Open an unencrypted file from a DVD directory tree.
+ */
+static dvd_file_t *DVDOpenFilePath( dvd_reader_t *dvd, char *filename )
+{
+ char full_path[ PATH_MAX + 1 ];
+ dvd_file_t *dvd_file;
+ struct stat fileinfo;
+ dvd_input_t dev;
+
+ /* Get the full path of the file. */
+ if( !findDVDFile( dvd, filename, full_path ) ) return 0;
+
+ dev = DVDinput_open( full_path );
+ if( !dev ) return 0;
+
+ dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
+ if( !dvd_file ) return 0;
+ dvd_file->dvd = dvd;
+ dvd_file->lb_start = 0;
+ dvd_file->seek_pos = 0;
+ memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
+ memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
+ dvd_file->filesize = 0;
+
+ if( stat( full_path, &fileinfo ) < 0 ) {
+ fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
+ free( dvd_file );
+ return 0;
+ }
+ dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+ dvd_file->title_devs[ 0 ] = dev;
+ dvd_file->filesize = dvd_file->title_sizes[ 0 ];
+
+ return dvd_file;
+}
+
+static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *dvd, int title, int menu )
+{
+ char filename[ MAX_UDF_FILE_NAME_LEN ];
+ uint32_t start, len;
+ dvd_file_t *dvd_file;
+
+ if( title == 0 ) {
+ sprintf( filename, "/VIDEO_TS/VIDEO_TS.VOB" );
+ } else {
+ sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, menu ? 0 : 1 );
+ }
+ start = UDFFindFile( dvd, filename, &len );
+ if( start == 0 ) return 0;
+
+ dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
+ if( !dvd_file ) return 0;
+ dvd_file->dvd = dvd;
+ /*Hack*/ dvd_file->css_title = title << 1 | menu;
+ dvd_file->lb_start = start;
+ dvd_file->seek_pos = 0;
+ memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
+ memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
+ dvd_file->filesize = len / DVD_VIDEO_LB_LEN;
+
+ /* Calculate the complete file size for every file in the VOBS */
+ if( !menu ) {
+ int cur;
+
+ for( cur = 2; cur < 10; cur++ ) {
+ sprintf( filename, "/VIDEO_TS/VTS_%02d_%d.VOB", title, cur );
+ if( !UDFFindFile( dvd, filename, &len ) ) break;
+ dvd_file->filesize += len / DVD_VIDEO_LB_LEN;
+ }
+ }
+
+ if( dvd->css_state == 1 /* Need key init */ ) {
+ initAllCSSKeys( dvd );
+ dvd->css_state = 2;
+ }
+ /*
+ if( DVDinput_seek( dvd_file->dvd->dev,
+ (int)start, DVDINPUT_SEEK_KEY ) < 0 ) {
+ fprintf( stderr, "libdvdread: Error cracking CSS key for %s\n",
+ filename );
+ }
+ */
+
+ return dvd_file;
+}
+
+static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *dvd, int title, int menu )
+{
+ char filename[ MAX_UDF_FILE_NAME_LEN ];
+ char full_path[ PATH_MAX + 1 ];
+ struct stat fileinfo;
+ dvd_file_t *dvd_file;
+ int i;
+
+ dvd_file = (dvd_file_t *) malloc( sizeof( dvd_file_t ) );
+ if( !dvd_file ) return 0;
+ dvd_file->dvd = dvd;
+ /*Hack*/ dvd_file->css_title = title << 1 | menu;
+ dvd_file->lb_start = 0;
+ dvd_file->seek_pos = 0;
+ memset( dvd_file->title_sizes, 0, sizeof( dvd_file->title_sizes ) );
+ memset( dvd_file->title_devs, 0, sizeof( dvd_file->title_devs ) );
+ dvd_file->filesize = 0;
+
+ if( menu ) {
+ dvd_input_t dev;
+
+ if( title == 0 ) {
+ sprintf( filename, "VIDEO_TS.VOB" );
+ } else {
+ sprintf( filename, "VTS_%02i_0.VOB", title );
+ }
+ if( !findDVDFile( dvd, filename, full_path ) ) {
+ free( dvd_file );
+ return 0;
+ }
+
+ dev = DVDinput_open( full_path );
+ if( dev == NULL ) {
+ free( dvd_file );
+ return 0;
+ }
+
+ if( stat( full_path, &fileinfo ) < 0 ) {
+ fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
+ free( dvd_file );
+ return 0;
+ }
+ dvd_file->title_sizes[ 0 ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+ dvd_file->title_devs[ 0 ] = dev;
+ DVDinput_seek( dvd_file->title_devs[0], 0, DVDINPUT_SEEK_KEY );
+ dvd_file->filesize = dvd_file->title_sizes[ 0 ];
+
+ } else {
+ for( i = 0; i < 9; ++i ) {
+
+ sprintf( filename, "VTS_%02i_%i.VOB", title, i + 1 );
+ if( !findDVDFile( dvd, filename, full_path ) ) {
+ break;
+ }
+
+ if( stat( full_path, &fileinfo ) < 0 ) {
+ fprintf( stderr, "libdvdread: Can't stat() %s.\n", filename );
+ break;
+ }
+
+ dvd_file->title_sizes[ i ] = fileinfo.st_size / DVD_VIDEO_LB_LEN;
+ dvd_file->title_devs[ i ] = DVDinput_open( full_path );
+ dvd_file->filesize += dvd_file->title_sizes[ i ];
+ }
+ if( dvd_file->title_devs[ 0 ] ) {
+ DVDinput_seek( dvd_file->title_devs[ 0 ], 0, DVDINPUT_SEEK_KEY );
+ } else {
+ 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 ) {
+ ;
+ } else {
+ for( i = 0; i < 9; ++i ) {
+ if( dvd_file->title_devs[ i ] ) {
+ DVDinput_close( dvd_file->title_devs[i] );
+ }
+ }
+ }
+
+ free( dvd_file );
+ dvd_file = 0;
+ }
+}
+
+/* Internal, but used from dvd_udf.c */
+int DVDReadBlocksUDFRaw( dvd_reader_t *device, uint32_t lb_number,
+ size_t block_count, unsigned char *data,
+ int encrypted )
+{
+ int ret;
+
+ if( !device->dev ) {
+ fprintf( stderr, "libdvdread: Fatal error in block read.\n" );
+ return 0;
+ }
+
+ ret = DVDinput_seek( device->dev, (int) lb_number, DVDINPUT_NOFLAGS );
+ if( ret != (int) lb_number ) {
+ fprintf( stderr, "libdvdread: Can't seek to block %u\n", lb_number );
+ return 0;
+ }
+
+ return DVDinput_read( device->dev, (char *) data,
+ (int) block_count, encrypted );
+}
+
+/* This is using a single input and starting from 'dvd_file->lb_start' offset.
+ *
+ * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
+ * into the buffer located at 'data' and if 'encrypted' is set
+ * descramble the data if it's encrypted. Returning either an
+ * negative error or the number of blocks read. */
+static int DVDReadBlocksUDF( dvd_file_t *dvd_file, uint32_t offset,
+ size_t block_count, unsigned char *data,
+ int encrypted )
+{
+ return DVDReadBlocksUDFRaw( dvd_file->dvd, dvd_file->lb_start + offset,
+ block_count, data, encrypted );
+}
+
+/* This is using possibly several inputs and starting from an offset of '0'.
+ *
+ * Reads 'block_count' blocks from 'dvd_file' at block offset 'offset'
+ * into the buffer located at 'data' and if 'encrypted' is set
+ * descramble the data if it's encrypted. Returning either an
+ * negative error or the number of blocks read. */
+static int DVDReadBlocksPath( dvd_file_t *dvd_file, unsigned int offset,
+ size_t block_count, unsigned char *data,
+ int encrypted )
+{
+ int i;
+ int ret, ret2, off;
+
+ ret = 0;
+ ret2 = 0;
+ for( i = 0; i < 9; ++i ) {
+ if( !dvd_file->title_sizes[ i ] ) return 0; /* Past end of file */
+
+ if( offset < dvd_file->title_sizes[ i ] ) {
+ if( ( offset + block_count ) <= dvd_file->title_sizes[ i ] ) {
+ off = DVDinput_seek( dvd_file->title_devs[ i ],
+ (int)offset, DVDINPUT_NOFLAGS );
+ if( off < 0 || off != (int)offset ) {
+ fprintf( stderr, "libdvdread: Can't seek to block %d\n",
+ offset );
+ return off < 0 ? off : 0;
+ }
+ ret = DVDinput_read( dvd_file->title_devs[ i ], data,
+ (int)block_count, encrypted );
+ break;
+ } else {
+ size_t part1_size = dvd_file->title_sizes[ i ] - offset;
+ /* FIXME: Really needs to be a while loop.
+ * (This is only true if you try and read >1GB at a time) */
+
+ /* Read part 1 */
+ off = DVDinput_seek( dvd_file->title_devs[ i ],
+ (int)offset, DVDINPUT_NOFLAGS );
+ if( off < 0 || off != (int)offset ) {
+ fprintf( stderr, "libdvdread: Can't seek to block %d\n",
+ offset );
+ return off < 0 ? off : 0;
+ }
+ ret = DVDinput_read( dvd_file->title_devs[ i ], data,
+ (int)part1_size, encrypted );
+ if( ret < 0 ) return ret;
+ /* FIXME: This is wrong if i is the last file in the set.
+ * also error from this read will not show in ret. */
+
+ /* Read part 2 */
+ off = DVDinput_seek( dvd_file->title_devs[ i + 1 ],
+ 0, DVDINPUT_NOFLAGS );
+ if( off < 0 || off != 0 ) {
+ fprintf( stderr, "libdvdread: Can't seek to block %d\n",
+ 0 );
+ return off < 0 ? off : 0;
+ }
+ ret2 = DVDinput_read( dvd_file->title_devs[ i + 1 ],
+ data + ( part1_size
+ * (int64_t)DVD_VIDEO_LB_LEN ),
+ (int)(block_count - part1_size),
+ encrypted );
+ if( ret2 < 0 ) return ret2;
+ break;
+ }
+ } else {
+ offset -= dvd_file->title_sizes[ i ];
+ }
+ }
+
+ return ret + ret2;
+}
+
+/* This is broken reading more than 2Gb at a time is ssize_t is 32-bit. */
+ssize_t DVDReadBlocks( dvd_file_t *dvd_file, int offset,
+ size_t block_count, unsigned char *data )
+{
+ int ret;
+
+ /* Hack, and it will still fail for multiple opens in a threaded app ! */
+ if( dvd_file->dvd->css_title != dvd_file->css_title ) {
+ dvd_file->dvd->css_title = dvd_file->css_title;
+ if( dvd_file->dvd->isImageFile ) {
+ DVDinput_title( dvd_file->dvd->dev, (int)dvd_file->lb_start );
+ } else {
+ DVDinput_title( dvd_file->title_devs[ 0 ], (int)dvd_file->lb_start );
+ }
+ }
+
+ if( dvd_file->dvd->isImageFile ) {
+ ret = DVDReadBlocksUDF( dvd_file, (uint32_t)offset,
+ block_count, data, DVDINPUT_READ_DECRYPT );
+ } else {
+ ret = DVDReadBlocksPath( dvd_file, (unsigned int)offset,
+ block_count, data, DVDINPUT_READ_DECRYPT );
+ }
+
+ return (ssize_t)ret;
+}
+
+int32_t DVDFileSeek( dvd_file_t *dvd_file, int32_t offset )
+{
+ if( offset > dvd_file->filesize * DVD_VIDEO_LB_LEN ) {
+ return -1;
+ }
+ dvd_file->seek_pos = (uint32_t) offset;
+ return offset;
+}
+
+ssize_t DVDReadBytes( dvd_file_t *dvd_file, void *data, size_t byte_size )
+{
+ unsigned char *secbuf;
+ unsigned int numsec, seek_sector, seek_byte;
+ int ret;
+
+ 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;
+ }
+
+ if( dvd_file->dvd->isImageFile ) {
+ ret = DVDReadBlocksUDF( dvd_file, (uint32_t) seek_sector,
+ (size_t) numsec, secbuf, DVDINPUT_NOFLAGS );
+ } else {
+ ret = DVDReadBlocksPath( dvd_file, seek_sector,
+ (size_t) numsec, secbuf, DVDINPUT_NOFLAGS );
+ }
+
+ if( ret != (int) numsec ) {
+ free( secbuf );
+ return ret < 0 ? ret : 0;
+ }
+
+ memcpy( data, &(secbuf[ seek_byte ]), byte_size );
+ free( secbuf );
+
+ dvd_file->seek_pos += byte_size;
+ return byte_size;
+}
+
+ssize_t DVDFileSize( dvd_file_t *dvd_file )
+{
+ return dvd_file->filesize;
+}
diff --git a/src/input/libdvdread/dvd_reader.h b/src/input/libdvdread/dvd_reader.h
new file mode 100644
index 000000000..75f718274
--- /dev/null
+++ b/src/input/libdvdread/dvd_reader.h
@@ -0,0 +1,151 @@
+#ifndef DVD_READER_H_INCLUDED
+#define DVD_READER_H_INCLUDED
+
+/*
+ * Copyright (C) 2001, 2002 Billy Biggs <vektor@dumbterm.net>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sys/types.h>
+
+/**
+ * The 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;
+
+/**
+ * dvd = DVDOpen(path);
+ *
+ * Opens a block device of a DVD-ROM file, or an image file, or a directory
+ * name for a mounted DVD or HD copy of a DVD. Returns 0 if we can't get any
+ * of those methods to work.
+ *
+ * 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 * );
+
+/**
+ * DVDClose(dvd);
+ *
+ * Closes and cleans up the DVD reader object. You must close all open files
+ * before calling this function.
+ */
+void DVDClose( dvd_reader_t * );
+
+/**
+ * INFO_FILE : VIDEO_TS.IFO (manager)
+ * VTS_XX_0.IFO (title)
+ *
+ * INFO_BACKUP_FILE: VIDEO_TS.BUP (manager)
+ * VTS_XX_0.BUP (title)
+ *
+ * MENU_VOBS : VIDEO_TS.VOB (manager)
+ * VTS_XX_0.VOB (title)
+ *
+ * TITLE_VOBS : VTS_XX_[1-9].VOB (title)
+ * All files in the title set are opened and
+ * read as a single file.
+ */
+typedef enum {
+ DVD_READ_INFO_FILE,
+ DVD_READ_INFO_BACKUP_FILE,
+ DVD_READ_MENU_VOBS,
+ DVD_READ_TITLE_VOBS
+} dvd_read_domain_t;
+
+/**
+ * dvd_file = DVDOpenFile(dvd, titlenum, domain);
+ *
+ * Opens a file on the DVD given the title number and domain. If the title
+ * number is 0, the video manager information is opened
+ * (VIDEO_TS.[IFO,BUP,VOB]). Returns a file structure which may be used for
+ * reads, or 0 if the file was not found.
+ */
+dvd_file_t *DVDOpenFile( dvd_reader_t *, int,
+ dvd_read_domain_t );
+
+/**
+ * DVDCloseFile(dvd_file);
+ *
+ * Closes a file and frees the associated structure.
+ */
+void DVDCloseFile( dvd_file_t * );
+
+/**
+ * blocks_read = DVDReadBlocks(dvd_file, offset, block_count, data);
+ *
+ * Reads block_count number of blocks from the file at the given block offset.
+ * Returns number of blocks read on success, -1 on error. This call is only
+ * for reading VOB data, and should not be used when reading the IFO files.
+ * When reading from an encrypted drive, blocks are decrypted using libdvdcss
+ * where required.
+ */
+ssize_t DVDReadBlocks( dvd_file_t *, int, size_t, unsigned char * );
+
+/**
+ * offset_set = DVDFileSeek(dvd_file, seek_offset);
+ *
+ * Seek to the given position in the file. Returns the resulting position in
+ * bytes from the beginning of the file. The seek position is only used for
+ * byte reads from the file, the block read call always reads from the given
+ * offset.
+ */
+int DVDFileSeek( dvd_file_t *, int );
+
+/**
+ * bytes_read = DVDReadBytes(dvd_file, data, bytes);
+ *
+ * Reads the given number of bytes from the file. This call can only be used
+ * on the information files, and may not be used for reading from a VOB. This
+ * reads from and increments the currrent seek position for the file.
+ */
+ssize_t DVDReadBytes( dvd_file_t *, void *, size_t );
+
+/**
+ * blocks = DVDFileSize(dvd_file);
+ *
+ * Returns the file size in blocks.
+ */
+ssize_t DVDFileSize( dvd_file_t * );
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* DVD_READER_H_INCLUDED */
diff --git a/src/input/libdvdread/dvd_udf.c b/src/input/libdvdread/dvd_udf.c
new file mode 100644
index 000000000..1cd2a0d2f
--- /dev/null
+++ b/src/input/libdvdread/dvd_udf.c
@@ -0,0 +1,494 @@
+/*
+ * This code is based on dvdudf by:
+ * Christian Wolff <scarabaeus@convergence.de>.
+ *
+ * Modifications by:
+ * Billy Biggs <vektor@dumbterm.net>.
+ *
+ * dvdudf: parse and read the UDF volume information of a DVD Video
+ * Copyright (C) 1999 Christian Wolff for convergence integrated media
+ * GmbH The author can be reached at scarabaeus@convergence.de, the
+ * project's page is at http://linuxtv.org/dvd/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA. Or, point your browser to
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "dvd_reader.h"
+#include "dvd_udf.h"
+
+/* Private but located in/shared with dvd_reader.c */
+extern int DVDReadBlocksUDFRaw( dvd_reader_t *device, uint32_t lb_number,
+ size_t block_count, unsigned char *data,
+ int encrypted );
+
+/* It's required to either fail or deliver all the blocks asked for. */
+static int DVDReadLBUDF( dvd_reader_t *device, uint32_t lb_number,
+ size_t block_count, unsigned char *data,
+ int encrypted )
+{
+ int ret;
+ size_t count = block_count;
+
+ while(count > 0) {
+
+ ret = DVDReadBlocksUDFRaw(device, lb_number, count, data, encrypted);
+
+ if(ret <= 0) {
+ /* One of the reads failed or nothing more to read, too bad.
+ * We won't even bother returning the reads that went ok. */
+ return ret;
+ }
+
+ count -= (size_t)ret;
+ lb_number += (uint32_t)ret;
+ }
+
+ return block_count;
+}
+
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+struct Partition {
+ int valid;
+ char VolumeDesc[128];
+ uint16_t Flags;
+ uint16_t Number;
+ char Contents[32];
+ uint32_t AccessType;
+ uint32_t Start;
+ uint32_t Length;
+};
+
+struct AD {
+ uint32_t Location;
+ uint32_t Length;
+ uint8_t Flags;
+ uint16_t Partition;
+};
+
+/* For direct data access, LSB first */
+#define GETN1(p) ((uint8_t)data[p])
+#define GETN2(p) ((uint16_t)data[p] | ((uint16_t)data[(p) + 1] << 8))
+#define GETN3(p) ((uint32_t)data[p] | ((uint32_t)data[(p) + 1] << 8) \
+ | ((uint32_t)data[(p) + 2] << 16))
+#define GETN4(p) ((uint32_t)data[p] \
+ | ((uint32_t)data[(p) + 1] << 8) \
+ | ((uint32_t)data[(p) + 2] << 16) \
+ | ((uint32_t)data[(p) + 3] << 24))
+/* This is wrong with regard to endianess */
+#define GETN(p, n, target) memcpy(target, &data[p], n)
+
+static int Unicodedecode( uint8_t *data, int len, char *target )
+{
+ int p = 1, i = 0;
+
+ if( ( data[ 0 ] == 8 ) || ( data[ 0 ] == 16 ) ) do {
+ if( data[ 0 ] == 16 ) p++; /* Ignore MSB of unicode16 */
+ if( p < len ) {
+ target[ i++ ] = data[ p++ ];
+ }
+ } while( p < len );
+
+ target[ i ] = '\0';
+ return 0;
+}
+
+static int UDFDescriptor( uint8_t *data, uint16_t *TagID )
+{
+ *TagID = GETN2(0);
+ // TODO: check CRC 'n stuff
+ return 0;
+}
+
+static int UDFExtentAD( uint8_t *data, uint32_t *Length, uint32_t *Location )
+{
+ *Length = GETN4(0);
+ *Location = GETN4(4);
+ return 0;
+}
+
+static int UDFShortAD( uint8_t *data, struct AD *ad,
+ struct Partition *partition )
+{
+ ad->Length = GETN4(0);
+ ad->Flags = ad->Length >> 30;
+ ad->Length &= 0x3FFFFFFF;
+ ad->Location = GETN4(4);
+ ad->Partition = partition->Number; // use number of current partition
+ return 0;
+}
+
+static int UDFLongAD( uint8_t *data, struct AD *ad )
+{
+ ad->Length = GETN4(0);
+ ad->Flags = ad->Length >> 30;
+ ad->Length &= 0x3FFFFFFF;
+ ad->Location = GETN4(4);
+ ad->Partition = GETN2(8);
+ //GETN(10, 6, Use);
+ return 0;
+}
+
+static int UDFExtAD( uint8_t *data, struct AD *ad )
+{
+ ad->Length = GETN4(0);
+ ad->Flags = ad->Length >> 30;
+ ad->Length &= 0x3FFFFFFF;
+ ad->Location = GETN4(12);
+ ad->Partition = GETN2(16);
+ //GETN(10, 6, Use);
+ return 0;
+}
+
+static int UDFICB( uint8_t *data, uint8_t *FileType, uint16_t *Flags )
+{
+ *FileType = GETN1(11);
+ *Flags = GETN2(18);
+ return 0;
+}
+
+
+static int UDFPartition( uint8_t *data, uint16_t *Flags, uint16_t *Number,
+ char *Contents, uint32_t *Start, uint32_t *Length )
+{
+ *Flags = GETN2(20);
+ *Number = GETN2(22);
+ GETN(24, 32, Contents);
+ *Start = GETN4(188);
+ *Length = GETN4(192);
+ return 0;
+}
+
+/**
+ * Reads the volume descriptor and checks the parameters. Returns 0 on OK, 1
+ * on error.
+ */
+static int UDFLogVolume( uint8_t *data, char *VolumeDescriptor )
+{
+ uint32_t lbsize, MT_L, N_PM;
+ Unicodedecode(&data[84], 128, VolumeDescriptor);
+ lbsize = GETN4(212); // should be 2048
+ MT_L = GETN4(264); // should be 6
+ N_PM = GETN4(268); // should be 1
+ if (lbsize != DVD_VIDEO_LB_LEN) return 1;
+ return 0;
+}
+
+static int UDFFileEntry( uint8_t *data, uint8_t *FileType,
+ struct Partition *partition, struct AD *ad )
+{
+ uint16_t flags;
+ uint32_t L_EA, L_AD;
+ unsigned int p;
+
+ UDFICB( &data[ 16 ], FileType, &flags );
+
+ /* Init ad for an empty file (i.e. there isn't a AD, L_AD == 0 ) */
+ ad->Length = GETN4( 60 ); // Really 8 bytes a 56
+ ad->Flags = 0;
+ ad->Location = 0; // what should we put here?
+ ad->Partition = partition->Number; // use number of current partition
+
+ L_EA = GETN4( 168 );
+ L_AD = GETN4( 172 );
+ p = 176 + L_EA;
+ while( p < 176 + L_EA + L_AD ) {
+ switch( flags & 0x0007 ) {
+ case 0: UDFShortAD( &data[ p ], ad, partition ); p += 8; break;
+ case 1: UDFLongAD( &data[ p ], ad ); p += 16; break;
+ case 2: UDFExtAD( &data[ p ], ad ); p += 20; break;
+ case 3:
+ switch( L_AD ) {
+ case 8: UDFShortAD( &data[ p ], ad, partition ); break;
+ case 16: UDFLongAD( &data[ p ], ad ); break;
+ case 20: UDFExtAD( &data[ p ], ad ); break;
+ }
+ p += L_AD;
+ break;
+ default:
+ p += L_AD; break;
+ }
+ }
+ return 0;
+}
+
+static int UDFFileIdentifier( uint8_t *data, uint8_t *FileCharacteristics,
+ char *FileName, struct AD *FileICB )
+{
+ uint8_t L_FI;
+ uint16_t L_IU;
+
+ *FileCharacteristics = GETN1(18);
+ L_FI = GETN1(19);
+ UDFLongAD(&data[20], FileICB);
+ L_IU = GETN2(36);
+ if (L_FI) Unicodedecode(&data[38 + L_IU], L_FI, FileName);
+ else FileName[0] = '\0';
+ return 4 * ((38 + L_FI + L_IU + 3) / 4);
+}
+
+/**
+ * Maps ICB to FileAD
+ * ICB: Location of ICB of directory to scan
+ * FileType: Type of the file
+ * File: Location of file the ICB is pointing to
+ * return 1 on success, 0 on error;
+ */
+static int UDFMapICB( dvd_reader_t *device, struct AD ICB, uint8_t *FileType,
+ struct Partition *partition, struct AD *File )
+{
+ uint8_t LogBlock[DVD_VIDEO_LB_LEN];
+ uint32_t lbnum;
+ uint16_t TagID;
+
+ lbnum = partition->Start + ICB.Location;
+ do {
+ if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) {
+ TagID = 0;
+ } else {
+ UDFDescriptor( LogBlock, &TagID );
+ }
+
+ if( TagID == 261 ) {
+ UDFFileEntry( LogBlock, FileType, partition, File );
+ return 1;
+ };
+ } while( ( lbnum <= partition->Start + ICB.Location + ( ICB.Length - 1 )
+ / DVD_VIDEO_LB_LEN ) && ( TagID != 261 ) );
+
+ return 0;
+}
+
+/**
+ * Dir: Location of directory to scan
+ * FileName: Name of file to look for
+ * FileICB: Location of ICB of the found file
+ * return 1 on success, 0 on error;
+ */
+static int UDFScanDir( dvd_reader_t *device, struct AD Dir, char *FileName,
+ struct Partition *partition, struct AD *FileICB )
+{
+ char filename[ MAX_UDF_FILE_NAME_LEN ];
+ uint8_t directory[ 2 * DVD_VIDEO_LB_LEN ];
+ uint32_t lbnum;
+ uint16_t TagID;
+ uint8_t filechar;
+ unsigned int p;
+
+ /* Scan dir for ICB of file */
+ lbnum = partition->Start + Dir.Location;
+
+ if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) {
+ return 0;
+ }
+
+ p = 0;
+ while( p < Dir.Length ) {
+ if( p > DVD_VIDEO_LB_LEN ) {
+ ++lbnum;
+ p -= DVD_VIDEO_LB_LEN;
+ Dir.Length -= DVD_VIDEO_LB_LEN;
+ if( DVDReadLBUDF( device, lbnum, 2, directory, 0 ) <= 0 ) {
+ return 0;
+ }
+ }
+ UDFDescriptor( &directory[ p ], &TagID );
+ if( TagID == 257 ) {
+ p += UDFFileIdentifier( &directory[ p ], &filechar,
+ filename, FileICB );
+ if( !strcasecmp( FileName, filename ) ) {
+ return 1;
+ }
+ } else {
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Looks for partition on the disc. Returns 1 if partition found, 0 on error.
+ * partnum: Number of the partition, starting at 0.
+ * part: structure to fill with the partition information
+ */
+static int UDFFindPartition( dvd_reader_t *device, int partnum,
+ struct Partition *part )
+{
+ uint8_t LogBlock[ DVD_VIDEO_LB_LEN ], Anchor[ DVD_VIDEO_LB_LEN ];
+ uint32_t lbnum, MVDS_location, MVDS_length;
+ uint16_t TagID;
+ uint32_t lastsector;
+ int i, terminate, volvalid;
+
+ /* Find Anchor */
+ lastsector = 0;
+ lbnum = 256; /* Try #1, prime anchor */
+ terminate = 0;
+
+ for(;;) {
+ if( DVDReadLBUDF( device, lbnum, 1, Anchor, 0 ) > 0 ) {
+ UDFDescriptor( Anchor, &TagID );
+ } else {
+ TagID = 0;
+ }
+ if (TagID != 2) {
+ /* Not an anchor */
+ if( terminate ) return 0; /* Final try failed */
+
+ if( lastsector ) {
+
+ /* We already found the last sector. Try #3, alternative
+ * backup anchor. If that fails, don't try again.
+ */
+ lbnum = lastsector;
+ terminate = 1;
+ } else {
+ /* TODO: Find last sector of the disc (this is optional). */
+ if( lastsector ) {
+ /* Try #2, backup anchor */
+ lbnum = lastsector - 256;
+ } else {
+ /* Unable to find last sector */
+ return 0;
+ }
+ }
+ } else {
+ /* It's an anchor! We can leave */
+ break;
+ }
+ }
+ /* Main volume descriptor */
+ UDFExtentAD( &Anchor[ 16 ], &MVDS_length, &MVDS_location );
+
+ part->valid = 0;
+ volvalid = 0;
+ part->VolumeDesc[ 0 ] = '\0';
+ i = 1;
+ do {
+ /* Find Volume Descriptor */
+ lbnum = MVDS_location;
+ do {
+
+ if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) {
+ TagID = 0;
+ } else {
+ UDFDescriptor( LogBlock, &TagID );
+ }
+
+ if( ( TagID == 5 ) && ( !part->valid ) ) {
+ /* Partition Descriptor */
+ UDFPartition( LogBlock, &part->Flags, &part->Number,
+ part->Contents, &part->Start, &part->Length );
+ part->valid = ( partnum == part->Number );
+ } else if( ( TagID == 6 ) && ( !volvalid ) ) {
+ /* Logical Volume Descriptor */
+ if( UDFLogVolume( LogBlock, part->VolumeDesc ) ) {
+ /* TODO: sector size wrong! */
+ } else {
+ volvalid = 1;
+ }
+ }
+
+ } while( ( lbnum <= MVDS_location + ( MVDS_length - 1 )
+ / DVD_VIDEO_LB_LEN ) && ( TagID != 8 )
+ && ( ( !part->valid ) || ( !volvalid ) ) );
+
+ if( ( !part->valid) || ( !volvalid ) ) {
+ /* Backup volume descriptor */
+ UDFExtentAD( &Anchor[ 24 ], &MVDS_length, &MVDS_location );
+ }
+ } while( i-- && ( ( !part->valid ) || ( !volvalid ) ) );
+
+ /* We only care for the partition, not the volume */
+ return part->valid;
+}
+
+uint32_t UDFFindFile( dvd_reader_t *device, char *filename,
+ uint32_t *filesize )
+{
+ uint8_t LogBlock[ DVD_VIDEO_LB_LEN ];
+ uint32_t lbnum;
+ uint16_t TagID;
+ struct Partition partition;
+ struct AD RootICB, File, ICB;
+ char tokenline[ MAX_UDF_FILE_NAME_LEN ];
+ char *token;
+ uint8_t filetype;
+
+ *filesize = 0;
+ tokenline[0] = '\0';
+ strcat( tokenline, filename );
+
+ /* Find partition, 0 is the standard location for DVD Video.*/
+ if( !UDFFindPartition( device, 0, &partition ) ) return 0;
+
+ /* Find root dir ICB */
+ lbnum = partition.Start;
+ do {
+ if( DVDReadLBUDF( device, lbnum++, 1, LogBlock, 0 ) <= 0 ) {
+ TagID = 0;
+ } else {
+ UDFDescriptor( LogBlock, &TagID );
+ }
+
+ /* File Set Descriptor */
+ if( TagID == 256 ) { // File Set Descriptor
+ UDFLongAD( &LogBlock[ 400 ], &RootICB );
+ }
+ } while( ( lbnum < partition.Start + partition.Length )
+ && ( TagID != 8 ) && ( TagID != 256 ) );
+
+ /* Sanity checks. */
+ if( TagID != 256 ) return 0;
+ if( RootICB.Partition != 0 ) return 0;
+
+ /* Find root dir */
+ if( !UDFMapICB( device, RootICB, &filetype, &partition, &File ) ) return 0;
+ if( filetype != 4 ) return 0; /* Root dir should be dir */
+
+ /* Tokenize filepath */
+ token = strtok(tokenline, "/");
+ while( token != NULL ) {
+ if( !UDFScanDir( device, File, token, &partition, &ICB ) ) return 0;
+ if( !UDFMapICB( device, ICB, &filetype, &partition, &File ) ) return 0;
+ token = strtok( NULL, "/" );
+ }
+
+ /* Sanity check. */
+ if( File.Partition != 0 ) return 0;
+
+ *filesize = File.Length;
+ /* Hack to not return partition.Start for empty files. */
+ if( !File.Location )
+ return 0;
+ else
+ return partition.Start + File.Location;
+}
diff --git a/src/input/libdvdread/dvd_udf.h b/src/input/libdvdread/dvd_udf.h
new file mode 100644
index 000000000..960ccf405
--- /dev/null
+++ b/src/input/libdvdread/dvd_udf.h
@@ -0,0 +1,53 @@
+#ifndef DVD_UDF_H_INCLUDED
+#define DVD_UDF_H_INCLUDED
+
+/*
+ * This code is based on dvdudf by:
+ * Christian Wolff <scarabaeus@convergence.de>.
+ *
+ * Modifications by:
+ * Billy Biggs <vektor@dumbterm.net>.
+ *
+ * dvdudf: parse and read the UDF volume information of a DVD Video
+ * Copyright (C) 1999 Christian Wolff for convergence integrated media
+ * GmbH The author can be reached at scarabaeus@convergence.de, the
+ * project's page is at http://linuxtv.org/dvd/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA. Or, point your browser to
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <inttypes.h>
+
+#include "dvd_reader.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Looks for a file on the UDF disc/imagefile and returns the block number
+ * where it begins, or 0 if it is not found. The filename should be an
+ * absolute pathname on the UDF filesystem, starting with '/'. For example,
+ * '/VIDEO_TS/VTS_01_1.IFO'. On success, filesize will be set to the size of
+ * the file in bytes.
+ */
+uint32_t UDFFindFile( dvd_reader_t *device, char *filename, uint32_t *size );
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* DVD_UDF_H_INCLUDED */
diff --git a/src/input/libdvdread/ifo_print.c b/src/input/libdvdread/ifo_print.c
new file mode 100644
index 000000000..716f6897f
--- /dev/null
+++ b/src/input/libdvdread/ifo_print.c
@@ -0,0 +1,1049 @@
+/*
+ * Copyright (C) 2000, 2001, 2002 Björn Englund <d4bjorn@dtek.chalmers.se>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include "config.h" // Needed for WORDS_BIGENDIAN
+#include "ifo_types.h"
+#include "ifo_read.h"
+#include "ifo_print.h"
+
+/* Put this in some other file / package? It's used in nav_print too. */
+static void ifoPrint_time(int level, dvd_time_t *dtime) {
+ const char *rate;
+ assert((dtime->hour>>4) < 0xa && (dtime->hour&0xf) < 0xa);
+ assert((dtime->minute>>4) < 0x7 && (dtime->minute&0xf) < 0xa);
+ assert((dtime->second>>4) < 0x7 && (dtime->second&0xf) < 0xa);
+ assert((dtime->frame_u&0xf) < 0xa);
+
+ printf("%02x:%02x:%02x.%02x",
+ dtime->hour,
+ dtime->minute,
+ dtime->second,
+ dtime->frame_u & 0x3f);
+ switch((dtime->frame_u & 0xc0) >> 6) {
+ case 1:
+ rate = "25.00";
+ break;
+ case 3:
+ rate = "29.97";
+ break;
+ default:
+ if(dtime->hour == 0 && dtime->minute == 0
+ && dtime->second == 0 && dtime->frame_u == 0)
+ rate = "no";
+ else
+ rate = "(please send a bug report)";
+ break;
+ }
+ printf(" @ %s fps", rate);
+}
+
+/* Put this in some other file / package? It's used in nav_print too.
+ Possibly also by the vm / navigator. */
+static void ifoPrint_CMD(int row, vm_cmd_t *command) {
+ int i;
+
+ printf("(%03d) ", row + 1);
+ for(i=0;i<8;i++)
+ printf("%02x ", command->bytes[i]);
+ printf("| ");
+
+ //vmcmd(command);
+ printf("\n");
+}
+
+static void ifoPrint_video_attributes(int level, video_attr_t *attr) {
+
+ /* The following test is shorter but not correct ISO C,
+ memcmp(attr,my_friendly_zeros, sizeof(video_attr_t)) */
+ if(attr->mpeg_version == 0
+ && attr->video_format == 0
+ && attr->display_aspect_ratio == 0
+ && attr->permitted_df == 0
+ && attr->unknown1 == 0
+ && attr->line21_cc_1 == 0
+ && attr->line21_cc_2 == 0
+ && attr->video_format == 0
+ && attr->letterboxed == 0
+ && attr->film_mode == 0) {
+ printf("-- Unspecified --");
+ return;
+ }
+
+ switch(attr->mpeg_version) {
+ case 0:
+ printf("mpeg1 ");
+ break;
+ case 1:
+ printf("mpeg2 ");
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ switch(attr->video_format) {
+ case 0:
+ printf("ntsc ");
+ break;
+ case 1:
+ printf("pal ");
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ switch(attr->display_aspect_ratio) {
+ case 0:
+ printf("4:3 ");
+ break;
+ case 3:
+ printf("16:9 ");
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ // Wide is allways allowed..!!!
+ switch(attr->permitted_df) {
+ case 0:
+ printf("pan&scan+letterboxed ");
+ break;
+ case 1:
+ printf("only pan&scan "); //??
+ break;
+ case 2:
+ printf("only letterboxed ");
+ break;
+ case 3:
+ // not specified
+ break;
+ default:
+ printf("(please send a bug report)");
+ }
+
+ printf("U%x ", attr->unknown1);
+ assert(!attr->unknown1);
+
+ if(attr->line21_cc_1 || attr->line21_cc_2) {
+ printf("NTSC CC ");
+ if(attr->line21_cc_1)
+ printf("1 ");
+ if(attr->line21_cc_2)
+ printf("2 ");
+ }
+
+ {
+ int height = 480;
+ if(attr->video_format != 0)
+ height = 576;
+ switch(attr->picture_size) {
+ case 0:
+ printf("720x%d ", height);
+ break;
+ case 1:
+ printf("704x%d ", height);
+ break;
+ case 2:
+ printf("352x%d ", height);
+ break;
+ case 3:
+ printf("352x%d ", height/2);
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+ }
+
+ if(attr->letterboxed) {
+ printf("source letterboxed ");
+ }
+
+ if(attr->film_mode) {
+ printf("film");
+ } else {
+ printf("video"); //camera
+ }
+}
+
+static void ifoPrint_audio_attributes(int level, audio_attr_t *attr) {
+
+ if(attr->audio_format == 0
+ && attr->multichannel_extension == 0
+ && attr->lang_type == 0
+ && attr->application_mode == 0
+ && attr->quantization == 0
+ && attr->sample_frequency == 0
+ && attr->channels == 0
+ && attr->lang_extension == 0
+ && attr->unknown1 == 0
+ && attr->unknown1 == 0) {
+ printf("-- Unspecified --");
+ return;
+ }
+
+ switch(attr->audio_format) {
+ case 0:
+ printf("ac3 ");
+ break;
+ case 1:
+ printf("(please send a bug report) ");
+ break;
+ case 2:
+ printf("mpeg1 ");
+ break;
+ case 3:
+ printf("mpeg2ext ");
+ break;
+ case 4:
+ printf("lpcm ");
+ break;
+ case 5:
+ printf("(please send a bug report) ");
+ break;
+ case 6:
+ printf("dts ");
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ if(attr->multichannel_extension)
+ printf("multichannel_extension ");
+
+ switch(attr->lang_type) {
+ case 0:
+ // not specified
+ assert(attr->lang_code == 0 || attr->lang_code == 0xffff);
+ break;
+ case 1:
+ printf("%c%c ", attr->lang_code>>8, attr->lang_code & 0xff);
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ switch(attr->application_mode) {
+ case 0:
+ // not specified
+ break;
+ case 1:
+ printf("karaoke mode ");
+ break;
+ case 2:
+ printf("surround sound mode ");
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ switch(attr->quantization) {
+ case 0:
+ printf("16bit ");
+ break;
+ case 1:
+ printf("20bit ");
+ break;
+ case 2:
+ printf("24bit ");
+ break;
+ case 3:
+ printf("drc ");
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ switch(attr->sample_frequency) {
+ case 0:
+ printf("48kHz ");
+ break;
+ case 1:
+ printf("??kHz ");
+ break;
+ default:
+ printf("sample_frequency %i (please send a bug report) ",
+ attr->sample_frequency);
+ }
+
+ printf("%dCh ", attr->channels + 1);
+
+ switch(attr->lang_extension) {
+ case 0:
+ printf("Not specified ");
+ break;
+ case 1: // Normal audio
+ printf("Normal Caption ");
+ break;
+ case 2: // visually imparied
+ printf("Audio for visually impaired ");
+ break;
+ case 3: // Directors 1
+ printf("Director's comments 1 ");
+ break;
+ case 4: // Directors 2
+ printf("Director's comments 2 ");
+ break;
+ //case 4: // Music score ?
+ default:
+ printf("(please send a bug report) ");
+ }
+
+ printf("%d ", attr->unknown1);
+ printf("%d ", attr->unknown2);
+}
+
+static void ifoPrint_subp_attributes(int level, subp_attr_t *attr) {
+
+ if(attr->type == 0
+ && attr->lang_code == 0
+ && attr->zero1 == 0
+ && attr->zero2 == 0
+ && attr->lang_extension== 0) {
+ printf("-- Unspecified --");
+ return;
+ }
+
+ printf("type %02x ", attr->type);
+
+ if(isalpha((int)(attr->lang_code >> 8))
+ && isalpha((int)(attr->lang_code & 0xff))) {
+ printf("%c%c ", attr->lang_code >> 8, attr->lang_code & 0xff);
+ } else {
+ printf("%02x%02x ", 0xff & (unsigned)(attr->lang_code >> 8),
+ 0xff & (unsigned)(attr->lang_code & 0xff));
+ }
+
+ printf("%d ", attr->zero1);
+ printf("%d ", attr->zero2);
+
+ switch(attr->lang_extension) {
+ case 0:
+ printf("Not specified ");
+ break;
+ case 1:
+ printf("Caption with normal size character ");
+ break;
+ case 2:
+ printf("Caption with bigger size character ");
+ break;
+ case 3:
+ printf("Caption for children ");
+ break;
+ case 4:
+ printf("reserved ");
+ break;
+ case 5:
+ printf("Closed Caption with normal size character ");
+ break;
+ case 6:
+ printf("Closed Caption with bigger size character ");
+ break;
+ case 7:
+ printf("Closed Caption for children ");
+ break;
+ case 8:
+ printf("reserved ");
+ break;
+ case 9:
+ printf("Forced Caption");
+ break;
+ case 10:
+ printf("reserved ");
+ break;
+ case 11:
+ printf("reserved ");
+ break;
+ case 12:
+ printf("reserved ");
+ break;
+ case 13:
+ printf("Director's comments with normal size character ");
+ break;
+ case 14:
+ printf("Director's comments with bigger size character ");
+ break;
+ case 15:
+ printf("Director's comments for children ");
+ break;
+ default:
+ printf("(please send a bug report) ");
+ }
+
+}
+
+
+static void ifoPrint_USER_OPS(user_ops_t *user_ops) {
+ uint32_t uops;
+ unsigned char *ptr = (unsigned char *)user_ops;
+
+ uops = (*ptr++ << 24);
+ uops |= (*ptr++ << 16);
+ uops |= (*ptr++ << 8);
+ uops |= (*ptr++);
+
+ if(uops == 0) {
+ printf("None\n");
+ } else if(uops == 0x01ffffff) {
+ printf("All\n");
+ } else {
+ if(user_ops->title_or_time_play)
+ printf("Title or Time Play, ");
+ if(user_ops->chapter_search_or_play)
+ printf("Chapter Search or Play, ");
+ if(user_ops->title_play)
+ printf("Title Play, ");
+ if(user_ops->stop)
+ printf("Stop, ");
+ if(user_ops->go_up)
+ printf("Go Up, ");
+ if(user_ops->time_or_chapter_search)
+ printf("Time or Chapter Search, ");
+ if(user_ops->prev_or_top_pg_search)
+ printf("Prev or Top PG Search, ");
+ if(user_ops->next_pg_search)
+ printf("Next PG Search, ");
+ if(user_ops->forward_scan)
+ printf("Forward Scan, ");
+ if(user_ops->backward_scan)
+ printf("Backward Scan, ");
+ if(user_ops->title_menu_call)
+ printf("Title Menu Call, ");
+ if(user_ops->root_menu_call)
+ printf("Root Menu Call, ");
+ if(user_ops->subpic_menu_call)
+ printf("SubPic Menu Call, ");
+ if(user_ops->audio_menu_call)
+ printf("Audio Menu Call, ");
+ if(user_ops->angle_menu_call)
+ printf("Angle Menu Call, ");
+ if(user_ops->chapter_menu_call)
+ printf("Chapter Menu Call, ");
+ if(user_ops->resume)
+ printf("Resume, ");
+ if(user_ops->button_select_or_activate)
+ printf("Button Select or Activate, ");
+ if(user_ops->still_off)
+ printf("Still Off, ");
+ if(user_ops->pause_on)
+ printf("Pause On, ");
+ if(user_ops->audio_stream_change)
+ printf("Audio Stream Change, ");
+ if(user_ops->subpic_stream_change)
+ printf("SubPic Stream Change, ");
+ if(user_ops->angle_change)
+ printf("Angle Change, ");
+ if(user_ops->karaoke_audio_pres_mode_change)
+ printf("Karaoke Audio Pres Mode Change, ");
+ if(user_ops->video_pres_mode_change)
+ printf("Video Pres Mode Change, ");
+ printf("\n");
+ }
+}
+
+
+void ifoPrint_VMGI_MAT(vmgi_mat_t *vmgi_mat) {
+
+ printf("VMG Identifier: %.12s\n", vmgi_mat->vmg_identifier);
+ printf("Last Sector of VMG: %08x\n", vmgi_mat->vmg_last_sector);
+ printf("Last Sector of VMGI: %08x\n", vmgi_mat->vmgi_last_sector);
+ printf("Specification version number: %01x.%01x\n",
+ vmgi_mat->specification_version >> 4,
+ vmgi_mat->specification_version & 0xf);
+ /* Byte 2 of 'VMG Category' (00xx0000) is the Region Code */
+ printf("VMG Category: %08x\n", vmgi_mat->vmg_category);
+ printf("VMG Number of Volumes: %i\n", vmgi_mat->vmg_nr_of_volumes);
+ printf("VMG This Volume: %i\n", vmgi_mat->vmg_this_volume_nr);
+ printf("Disc side %i\n", vmgi_mat->disc_side);
+ printf("VMG Number of Title Sets %i\n", vmgi_mat->vmg_nr_of_title_sets);
+ printf("Provider ID: %.32s\n", vmgi_mat->provider_identifier);
+ printf("VMG POS Code: %08x", (uint32_t)(vmgi_mat->vmg_pos_code >> 32));
+ printf("%08x\n", (uint32_t)vmgi_mat->vmg_pos_code);
+ printf("End byte of VMGI_MAT: %08x\n", vmgi_mat->vmgi_last_byte);
+ printf("Start byte of First Play PGC FP PGC: %08x\n",
+ vmgi_mat->first_play_pgc);
+ printf("Start sector of VMGM_VOBS: %08x\n", vmgi_mat->vmgm_vobs);
+ printf("Start sector of TT_SRPT: %08x\n", vmgi_mat->tt_srpt);
+ printf("Start sector of VMGM_PGCI_UT: %08x\n", vmgi_mat->vmgm_pgci_ut);
+ printf("Start sector of PTL_MAIT: %08x\n", vmgi_mat->ptl_mait);
+ printf("Start sector of VTS_ATRT: %08x\n", vmgi_mat->vts_atrt);
+ printf("Start sector of TXTDT_MG: %08x\n", vmgi_mat->txtdt_mgi);
+ printf("Start sector of VMGM_C_ADT: %08x\n", vmgi_mat->vmgm_c_adt);
+ printf("Start sector of VMGM_VOBU_ADMAP: %08x\n",
+ vmgi_mat->vmgm_vobu_admap);
+ printf("Video attributes of VMGM_VOBS: ");
+ ifoPrint_video_attributes(5, &vmgi_mat->vmgm_video_attr);
+ printf("\n");
+ printf("VMGM Number of Audio attributes: %i\n",
+ vmgi_mat->nr_of_vmgm_audio_streams);
+ if(vmgi_mat->nr_of_vmgm_audio_streams > 0) {
+ printf("\tstream %i status: ", 1);
+ ifoPrint_audio_attributes(5, &vmgi_mat->vmgm_audio_attr);
+ printf("\n");
+ }
+ printf("VMGM Number of Sub-picture attributes: %i\n",
+ vmgi_mat->nr_of_vmgm_subp_streams);
+ if(vmgi_mat->nr_of_vmgm_subp_streams > 0) {
+ printf("\tstream %2i status: ", 1);
+ ifoPrint_subp_attributes(5, &vmgi_mat->vmgm_subp_attr);
+ printf("\n");
+ }
+}
+
+
+void ifoPrint_VTSI_MAT(vtsi_mat_t *vtsi_mat) {
+ int i;
+
+ printf("VTS Identifier: %.12s\n", vtsi_mat->vts_identifier);
+ printf("Last Sector of VTS: %08x\n", vtsi_mat->vts_last_sector);
+ printf("Last Sector of VTSI: %08x\n", vtsi_mat->vtsi_last_sector);
+ printf("Specification version number: %01x.%01x\n",
+ vtsi_mat->specification_version>>4,
+ vtsi_mat->specification_version&0xf);
+ printf("VTS Category: %08x\n", vtsi_mat->vts_category);
+ printf("End byte of VTSI_MAT: %08x\n", vtsi_mat->vtsi_last_byte);
+ printf("Start sector of VTSM_VOBS: %08x\n", vtsi_mat->vtsm_vobs);
+ printf("Start sector of VTSTT_VOBS: %08x\n", vtsi_mat->vtstt_vobs);
+ printf("Start sector of VTS_PTT_SRPT: %08x\n", vtsi_mat->vts_ptt_srpt);
+ printf("Start sector of VTS_PGCIT: %08x\n", vtsi_mat->vts_pgcit);
+ printf("Start sector of VTSM_PGCI_UT: %08x\n", vtsi_mat->vtsm_pgci_ut);
+ printf("Start sector of VTS_TMAPT: %08x\n", vtsi_mat->vts_tmapt);
+ printf("Start sector of VTSM_C_ADT: %08x\n", vtsi_mat->vtsm_c_adt);
+ printf("Start sector of VTSM_VOBU_ADMAP: %08x\n",vtsi_mat->vtsm_vobu_admap);
+ printf("Start sector of VTS_C_ADT: %08x\n", vtsi_mat->vts_c_adt);
+ printf("Start sector of VTS_VOBU_ADMAP: %08x\n", vtsi_mat->vts_vobu_admap);
+
+ printf("Video attributes of VTSM_VOBS: ");
+ ifoPrint_video_attributes(5, &vtsi_mat->vtsm_video_attr);
+ printf("\n");
+
+ printf("VTSM Number of Audio attributes: %i\n",
+ vtsi_mat->nr_of_vtsm_audio_streams);
+ if(vtsi_mat->nr_of_vtsm_audio_streams > 0) {
+ printf("\tstream %i status: ", 1);
+ ifoPrint_audio_attributes(5, &vtsi_mat->vtsm_audio_attr);
+ printf("\n");
+ }
+
+ printf("VTSM Number of Sub-picture attributes: %i\n",
+ vtsi_mat->nr_of_vtsm_subp_streams);
+ if(vtsi_mat->nr_of_vtsm_subp_streams > 0) {
+ printf("\tstream %2i status: ", 1);
+ ifoPrint_subp_attributes(5, &vtsi_mat->vtsm_subp_attr);
+ printf("\n");
+ }
+
+ printf("Video attributes of VTS_VOBS: ");
+ ifoPrint_video_attributes(5, &vtsi_mat->vts_video_attr);
+ printf("\n");
+
+ printf("VTS Number of Audio attributes: %i\n",
+ vtsi_mat->nr_of_vts_audio_streams);
+ for(i = 0; i < vtsi_mat->nr_of_vts_audio_streams; i++) {
+ printf("\tstream %i status: ", i);
+ ifoPrint_audio_attributes(5, &vtsi_mat->vts_audio_attr[i]);
+ printf("\n");
+ }
+
+ printf("VTS Number of Subpicture attributes: %i\n",
+ vtsi_mat->nr_of_vts_subp_streams);
+ for(i = 0; i < vtsi_mat->nr_of_vts_subp_streams; i++) {
+ printf("\tstream %2i status: ", i);
+ ifoPrint_subp_attributes(5, &vtsi_mat->vts_subp_attr[i]);
+ printf("\n");
+ }
+}
+
+
+static void ifoPrint_PGC_COMMAND_TBL(pgc_command_tbl_t *cmd_tbl) {
+ int i;
+
+ if(cmd_tbl == NULL) {
+ printf("No Command table present\n");
+ return;
+ }
+
+ printf("Number of Pre commands: %i\n", cmd_tbl->nr_of_pre);
+ for(i = 0; i < cmd_tbl->nr_of_pre; i++) {
+ ifoPrint_CMD(i, &cmd_tbl->pre_cmds[i]);
+ }
+
+ printf("Number of Post commands: %i\n", cmd_tbl->nr_of_post);
+ for(i = 0; i < cmd_tbl->nr_of_post; i++) {
+ ifoPrint_CMD(i, &cmd_tbl->post_cmds[i]);
+ }
+
+ printf("Number of Cell commands: %i\n", cmd_tbl->nr_of_cell);
+ for(i = 0; i < cmd_tbl->nr_of_cell; i++) {
+ ifoPrint_CMD(i, &cmd_tbl->cell_cmds[i]);
+ }
+}
+
+
+static void ifoPrint_PGC_PROGRAM_MAP(pgc_program_map_t *program_map, int nr) {
+ int i;
+
+ if(program_map == NULL) {
+ printf("No Program map present\n");
+ return;
+ }
+
+ for(i = 0; i < nr; i++) {
+ printf("Program %3i Entry Cell: %3i\n", i + 1, program_map[i]);
+ }
+}
+
+
+static void ifoPrint_CELL_PLAYBACK(cell_playback_t *cell_playback, int nr) {
+ int i;
+
+ if(cell_playback == NULL) {
+ printf("No Cell Playback info present\n");
+ return;
+ }
+
+ for(i=0;i<nr;i++) {
+ printf("Cell: %3i ", i + 1);
+
+ ifoPrint_time(5, &cell_playback[i].playback_time);
+ printf("\t");
+
+ if(cell_playback[i].block_mode || cell_playback[i].block_type) {
+ const char *s;
+ switch(cell_playback[i].block_mode) {
+ case 0:
+ s = "not a"; break;
+ case 1:
+ s = "the first"; break;
+ case 2:
+ default:
+ s = ""; break;
+ case 3:
+ s = "last"; break;
+ }
+ printf("%s cell in the block ", s);
+
+ switch(cell_playback[i].block_type) {
+ case 0:
+ printf("not part of the block ");
+ break;
+ case 1:
+ printf("angle block ");
+ break;
+ case 2:
+ case 3:
+ printf("(send bug repport) ");
+ break;
+ }
+ }
+ if(cell_playback[i].seamless_play)
+ printf("presented seamlessly ");
+ if(cell_playback[i].interleaved)
+ printf("cell is interleaved ");
+ if(cell_playback[i].stc_discontinuity)
+ printf("STC_discontinuty ");
+ if(cell_playback[i].seamless_angle)
+ printf("only seamless angle ");
+ if(cell_playback[i].restricted)
+ printf("restricted cell ");
+
+ if(cell_playback[i].still_time)
+ printf("still time %d ", cell_playback[i].still_time);
+ if(cell_playback[i].cell_cmd_nr)
+ printf("cell command %d", cell_playback[i].cell_cmd_nr);
+
+ printf("\n\tStart sector: %08x\tFirst ILVU end sector: %08x\n",
+ cell_playback[i].first_sector,
+ cell_playback[i].first_ilvu_end_sector);
+ printf("\tEnd sector: %08x\tLast VOBU start sector: %08x\n",
+ cell_playback[i].last_sector,
+ cell_playback[i].last_vobu_start_sector);
+ }
+}
+
+static void ifoPrint_CELL_POSITION(cell_position_t *cell_position, int nr) {
+ int i;
+
+ if(cell_position == NULL) {
+ printf("No Cell Position info present\n");
+ return;
+ }
+
+ for(i=0;i<nr;i++) {
+ printf("Cell: %3i has VOB ID: %3i, Cell ID: %3i\n", i + 1,
+ cell_position[i].vob_id_nr, cell_position[i].cell_nr);
+ }
+}
+
+
+void ifoPrint_PGC(pgc_t *pgc) {
+ int i;
+
+ printf("Number of Programs: %i\n", pgc->nr_of_programs);
+ printf("Number of Cells: %i\n", pgc->nr_of_cells);
+ /* Check that time is 0:0:0:0 also if nr_of_programs==0 */
+ printf("Playback time: ");
+ ifoPrint_time(5, &pgc->playback_time); printf("\n");
+
+ /* If no programs/no time then does this mean anything? */
+ printf("Prohibited user operations: ");
+ ifoPrint_USER_OPS(&pgc->prohibited_ops);
+
+ for(i = 0; i < 8; i++) {
+ if(pgc->audio_control[i] & 0x8000) { /* The 'is present' bit */
+ printf("Audio stream %i control: %04x\n",
+ i, pgc->audio_control[i]);
+ }
+ }
+
+ for(i = 0; i < 32; i++) {
+ if(pgc->subp_control[i] & 0x80000000) { /* The 'is present' bit */
+ printf("Subpicture stream %2i control: %08x\n",
+ i, pgc->subp_control[i]);
+ }
+ }
+
+ printf("Next PGC number: %i\n", pgc->next_pgc_nr);
+ printf("Prev PGC number: %i\n", pgc->prev_pgc_nr);
+ printf("GoUp PGC number: %i\n", pgc->goup_pgc_nr);
+ if(pgc->nr_of_programs != 0) {
+ printf("Still time: %i seconds (255=inf)\n", pgc->still_time);
+ printf("PG Playback mode %02x\n", pgc->pg_playback_mode);
+ }
+
+ if(pgc->nr_of_programs != 0) {
+ for(i = 0; i < 16; i++) {
+ printf("Color %2i: %08x\n", i, pgc->palette[i]);
+ }
+ }
+
+ /* Memmory offsets to div. tables. */
+ ifoPrint_PGC_COMMAND_TBL(pgc->command_tbl);
+ ifoPrint_PGC_PROGRAM_MAP(pgc->program_map, pgc->nr_of_programs);
+ ifoPrint_CELL_PLAYBACK(pgc->cell_playback, pgc->nr_of_cells);
+ ifoPrint_CELL_POSITION(pgc->cell_position, pgc->nr_of_cells);
+}
+
+
+void ifoPrint_TT_SRPT(tt_srpt_t *tt_srpt) {
+ int i;
+
+ printf("Number of TitleTrack search pointers: %i\n",
+ tt_srpt->nr_of_srpts);
+ for(i=0;i<tt_srpt->nr_of_srpts;i++) {
+ printf("Title Track index %i\n", i + 1);
+ printf("\tTitle set number (VTS): %i",
+ tt_srpt->title[i].title_set_nr);
+ printf("\tVTS_TTN: %i\n", tt_srpt->title[i].vts_ttn);
+ printf("\tNumber of PTTs: %i\n", tt_srpt->title[i].nr_of_ptts);
+ printf("\tNumber of angles: %i\n",
+ tt_srpt->title[i].nr_of_angles);
+ printf("\tTitle playback type: %02x\n", /* XXX: TODO FIXME */
+ *(uint8_t *)&(tt_srpt->title[i].pb_ty));
+ printf("\tParental ID field: %04x\n",
+ tt_srpt->title[i].parental_id);
+ printf("\tTitle set starting sector %08x\n",
+ tt_srpt->title[i].title_set_sector);
+ }
+}
+
+
+void ifoPrint_VTS_PTT_SRPT(vts_ptt_srpt_t *vts_ptt_srpt) {
+ int i, j;
+ printf(" nr_of_srpts %i last byte %i\n",
+ vts_ptt_srpt->nr_of_srpts,
+ vts_ptt_srpt->last_byte);
+ for(i=0;i<vts_ptt_srpt->nr_of_srpts;i++) {
+ for(j=0;j<vts_ptt_srpt->title[i].nr_of_ptts;j++) {
+ printf("VTS_PTT_SRPT - Title %3i part %3i: PGC: %3i PG: %3i\n",
+ i + 1, j + 1,
+ vts_ptt_srpt->title[i].ptt[j].pgcn,
+ vts_ptt_srpt->title[i].ptt[j].pgn );
+ }
+ }
+}
+
+
+static void hexdump(uint8_t *ptr, int len) {
+ while(len--)
+ printf("%02x ", *ptr++);
+}
+
+void ifoPrint_PTL_MAIT(ptl_mait_t *ptl_mait) {
+ int i, j;
+
+ printf("Number of Countries: %i\n", ptl_mait->nr_of_countries);
+ printf("Number of VTSs: %i\n", ptl_mait->nr_of_vtss);
+ //printf("Last byte: %i\n", ptl_mait->last_byte);
+
+ for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+ printf("Country code: %c%c\n",
+ ptl_mait->countries[i].country_code >> 8,
+ ptl_mait->countries[i].country_code & 0xff);
+ /*
+ printf("Start byte: %04x %i\n",
+ ptl_mait->countries[i].pf_ptl_mai_start_byte,
+ ptl_mait->countries[i].pf_ptl_mai_start_byte);
+ */
+ /* This seems to be pointing at a array with 8 2byte fields per VTS
+ ? and one extra for the menu? always an odd number of VTSs on
+ all the dics I tested so it might be padding to even also.
+ If it is for the menu it probably the first entry. */
+ for(j=0;j<8;j++) {
+ hexdump( (uint8_t *)ptl_mait->countries - PTL_MAIT_COUNTRY_SIZE
+ + ptl_mait->countries[i].pf_ptl_mai_start_byte
+ + j*(ptl_mait->nr_of_vtss+1)*2, (ptl_mait->nr_of_vtss+1)*2);
+ printf("\n");
+ }
+ }
+}
+
+
+void ifoPrint_C_ADT(c_adt_t *c_adt) {
+ int i, entries;
+
+ printf("Number of VOBs in this VOBS: %i\n", c_adt->nr_of_vobs);
+ //entries = c_adt->nr_of_vobs;
+ entries = (c_adt->last_byte + 1 - C_ADT_SIZE)/sizeof(c_adt_t);
+
+ for(i = 0; i < entries; i++) {
+ printf("VOB ID: %3i, Cell ID: %3i ",
+ c_adt->cell_adr_table[i].vob_id, c_adt->cell_adr_table[i].cell_id);
+ printf("Sector (first): 0x%08x (last): 0x%08x\n",
+ c_adt->cell_adr_table[i].start_sector,
+ c_adt->cell_adr_table[i].last_sector);
+ }
+}
+
+
+void ifoPrint_VOBU_ADMAP(vobu_admap_t *vobu_admap) {
+ int i, entries;
+
+ entries = (vobu_admap->last_byte + 1 - VOBU_ADMAP_SIZE)/4;
+ for(i = 0; i < entries; i++) {
+ printf("VOBU %5i First sector: 0x%08x\n", i + 1,
+ vobu_admap->vobu_start_sectors[i]);
+ }
+}
+
+
+void ifoPrint_PGCIT(pgcit_t *pgcit) {
+ int i;
+
+ for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+ printf("\nProgram (PGC): %3i\t", i + 1);
+ printf("PGC Category: Entry id 0x%02x, ", pgcit->pgci_srp[i].entry_id);
+ printf("Parental ID mask 0x%04x\n", pgcit->pgci_srp[i].ptl_id_mask);
+ ifoPrint_PGC(pgcit->pgci_srp[i].pgc);
+ }
+}
+
+
+void ifoPrint_PGCI_UT(pgci_ut_t *pgci_ut) {
+ int i;
+
+ printf("Number of Menu Language Units (PGCI_LU): %3i\n", pgci_ut->nr_of_lus);
+ for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+ printf("\nMenu Language Code: %c%c\n",
+ pgci_ut->lu[i].lang_code >> 8,
+ pgci_ut->lu[i].lang_code & 0xff);
+ printf("Menu Existence: %02x\n", pgci_ut->lu[i].exists);
+ ifoPrint_PGCIT(pgci_ut->lu[i].pgcit);
+ }
+}
+
+
+static void ifoPrint_VTS_ATTRIBUTES(vts_attributes_t *vts_attributes) {
+ int i;
+
+ printf("VTS_CAT Application type: %08x\n", vts_attributes->vts_cat);
+
+ printf("Video attributes of VTSM_VOBS: ");
+ ifoPrint_video_attributes(5, &vts_attributes->vtsm_vobs_attr);
+ printf("\n");
+ printf("Number of Audio streams: %i\n",
+ vts_attributes->nr_of_vtsm_audio_streams);
+ if(vts_attributes->nr_of_vtsm_audio_streams > 0) {
+ printf("\tstream %i attributes: ", 1);
+ ifoPrint_audio_attributes(5, &vts_attributes->vtsm_audio_attr);
+ printf("\n");
+ }
+ printf("Number of Subpicture streams: %i\n",
+ vts_attributes->nr_of_vtsm_subp_streams);
+ if(vts_attributes->nr_of_vtsm_subp_streams > 0) {
+ printf("\tstream %2i attributes: ", 1);
+ ifoPrint_subp_attributes(5, &vts_attributes->vtsm_subp_attr);
+ printf("\n");
+ }
+
+ printf("Video attributes of VTSTT_VOBS: ");
+ ifoPrint_video_attributes(5, &vts_attributes->vtstt_vobs_video_attr);
+ printf("\n");
+ printf("Number of Audio streams: %i\n",
+ vts_attributes->nr_of_vtstt_audio_streams);
+ for(i = 0; i < vts_attributes->nr_of_vtstt_audio_streams; i++) {
+ printf("\tstream %i attributes: ", i);
+ ifoPrint_audio_attributes(5, &vts_attributes->vtstt_audio_attr[i]);
+ printf("\n");
+ }
+
+ printf("Number of Subpicture streams: %i\n",
+ vts_attributes->nr_of_vtstt_subp_streams);
+ for(i = 0; i < vts_attributes->nr_of_vtstt_subp_streams; i++) {
+ printf("\tstream %2i attributes: ", i);
+ ifoPrint_subp_attributes(5, &vts_attributes->vtstt_subp_attr[i]);
+ printf("\n");
+ }
+}
+
+
+void ifoPrint_VTS_ATRT(vts_atrt_t *vts_atrt) {
+ int i;
+
+ printf("Number of Video Title Sets: %3i\n", vts_atrt->nr_of_vtss);
+ for(i = 0; i < vts_atrt->nr_of_vtss; i++) {
+ printf("\nVideo Title Set %i\n", i + 1);
+ ifoPrint_VTS_ATTRIBUTES(&vts_atrt->vts[i]);
+ }
+}
+
+
+void ifoPrint(dvd_reader_t *dvd, int title) {
+ ifo_handle_t *ifohandle;
+
+ ifohandle = ifoOpen(dvd, title);
+ if(!ifohandle) {
+ fprintf(stderr, "Can't open info file for title %d\n", title);
+ return;
+ }
+
+
+ if(ifohandle->vmgi_mat) {
+
+ printf("VMG top level\n-------------\n");
+ ifoPrint_VMGI_MAT(ifohandle->vmgi_mat);
+
+ printf("\nFirst Play PGC\n--------------\n");
+ ifoPrint_PGC(ifohandle->first_play_pgc);
+
+ printf("\nTitle Track search pointer table\n");
+ printf( "------------------------------------------------\n");
+ ifoPrint_TT_SRPT(ifohandle->tt_srpt);
+
+ printf("\nMenu PGCI Unit table\n");
+ printf( "--------------------\n");
+ if(ifohandle->pgci_ut) {
+ ifoPrint_PGCI_UT(ifohandle->pgci_ut);
+ } else {
+ printf("No PGCI Unit table present\n");
+ }
+
+ printf("\nParental Manegment Information table\n");
+ printf( "------------------------------------\n");
+ if(ifohandle->ptl_mait) {
+ ifoPrint_PTL_MAIT(ifohandle->ptl_mait);
+ } else {
+ printf("No Parental Management Information present\n");
+ }
+
+ printf("\nVideo Title Set Attribute Table\n");
+ printf( "-------------------------------\n");
+ ifoPrint_VTS_ATRT(ifohandle->vts_atrt);
+
+ printf("\nText Data Manager Information\n");
+ printf( "-----------------------------\n");
+ if(ifohandle->txtdt_mgi) {
+ //ifoPrint_TXTDT_MGI(&(vmgi->txtdt_mgi));
+ } else {
+ printf("No Text Data Manager Information present\n");
+ }
+
+ printf("\nMenu Cell Adress table\n");
+ printf( "-----------------\n");
+ if(ifohandle->menu_c_adt) {
+ ifoPrint_C_ADT(ifohandle->menu_c_adt);
+ } else {
+ printf("No Menu Cell Adress table present\n");
+ }
+
+ printf("\nVideo Manager Menu VOBU address map\n");
+ printf( "-----------------\n");
+ if(ifohandle->menu_vobu_admap) {
+ ifoPrint_VOBU_ADMAP(ifohandle->menu_vobu_admap);
+ } else {
+ printf("No Menu VOBU address map present\n");
+ }
+ }
+
+
+ if(ifohandle->vtsi_mat) {
+
+ printf("VTS top level\n-------------\n");
+ ifoPrint_VTSI_MAT(ifohandle->vtsi_mat);
+
+ printf("\nPart of Title Track search pointer table\n");
+ printf( "----------------------------------------------\n");
+ ifoPrint_VTS_PTT_SRPT(ifohandle->vts_ptt_srpt);
+
+ printf("\nPGCI Unit table\n");
+ printf( "--------------------\n");
+ ifoPrint_PGCIT(ifohandle->vts_pgcit);
+
+ printf("\nMenu PGCI Unit table\n");
+ printf( "--------------------\n");
+ if(ifohandle->pgci_ut) {
+ ifoPrint_PGCI_UT(ifohandle->pgci_ut);
+ } else {
+ printf("No Menu PGCI Unit table present\n");
+ }
+
+ printf("\nMenu Cell Adress table\n");
+ printf( "-----------------\n");
+ if(ifohandle->menu_c_adt) {
+ ifoPrint_C_ADT(ifohandle->menu_c_adt);
+ } else {
+ printf("No Cell Adress table present\n");
+ }
+
+ printf("\nVideo Title Set Menu VOBU address map\n");
+ printf( "-----------------\n");
+ if(ifohandle->menu_vobu_admap) {
+ ifoPrint_VOBU_ADMAP(ifohandle->menu_vobu_admap);
+ } else {
+ printf("No Menu VOBU address map present\n");
+ }
+
+ printf("\nCell Adress table\n");
+ printf( "-----------------\n");
+ ifoPrint_C_ADT(ifohandle->vts_c_adt);
+
+ printf("\nVideo Title Set VOBU address map\n");
+ printf( "-----------------\n");
+ ifoPrint_VOBU_ADMAP(ifohandle->vts_vobu_admap);
+ }
+
+ ifoClose(ifohandle);
+}
+
diff --git a/src/input/libdvdread/ifo_print.h b/src/input/libdvdread/ifo_print.h
new file mode 100644
index 000000000..871bc7362
--- /dev/null
+++ b/src/input/libdvdread/ifo_print.h
@@ -0,0 +1,58 @@
+#ifndef IFO_PRINT_H_INCLUDED
+#define IFO_PRINT_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001 Björn Englund <d4bjorn@dtek.chalmers.se>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <dvdread/ifo_types.h>
+#include <dvdread/dvd_reader.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * This file provides example functions for printing information about the IFO
+ * file to stdout.
+ */
+
+/**
+ * Print the complete parsing information for the given file.
+ */
+
+/* ifoPrint(dvd, title); */
+void ifoPrint(dvd_reader_t *, int);
+
+void ifoPrint_VMGI_MAT(vmgi_mat_t *);
+void ifoPrint_VTSI_MAT(vtsi_mat_t *);
+
+void ifoPrint_PTL_MAIT(ptl_mait_t *);
+void ifoPrint_VTS_ATRT(vts_atrt_t *);
+void ifoPrint_TT_SRPT(tt_srpt_t *);
+void ifoPrint_VTS_PTT_SRPT(vts_ptt_srpt_t *);
+void ifoPrint_PGC(pgc_t *);
+void ifoPrint_PGCIT(pgcit_t *);
+void ifoPrint_PGCI_UT(pgci_ut_t *);
+void ifoPrint_C_ADT(c_adt_t *);
+void ifoPrint_VOBU_ADMAP(vobu_admap_t *);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* IFO_PRINT_H_INCLUDED */
diff --git a/src/input/libdvdread/ifo_read.c b/src/input/libdvdread/ifo_read.c
new file mode 100644
index 000000000..d530bcf96
--- /dev/null
+++ b/src/input/libdvdread/ifo_read.c
@@ -0,0 +1,1798 @@
+/*
+ * Copyright (C) 2000, 2001, 2002 Björn Englund <d4bjorn@dtek.chalmers.se>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <string.h>
+#include <assert.h>
+
+#include "dvd_reader.h"
+
+#include "config.h" // Needed for WORDS_BIGENDIAN
+#include "bswap.h"
+#include "ifo_types.h"
+#include "ifo_read.h"
+
+#ifndef DVD_BLOCK_LEN
+#define DVD_BLOCK_LEN 2048
+#endif
+
+#ifndef NDEBUG
+#define CHECK_ZERO(arg) \
+ if(memcmp(my_friendly_zeros, &arg, sizeof(arg))) { \
+ unsigned int i_CZ; \
+ fprintf(stderr, "*** Zero check failed in %s:%i\n for %s = 0x", \
+ __FILE__, __LINE__, # arg ); \
+ for(i_CZ = 0; i_CZ < sizeof(arg); i_CZ++) \
+ fprintf(stderr, "%02x", *((uint8_t *)&arg + i_CZ)); \
+ fprintf(stderr, "\n"); \
+ }
+static const uint8_t my_friendly_zeros[2048];
+#else
+#define CHECK_ZERO(arg) (void)(arg)
+#endif
+
+
+/* Prototypes for internal functions */
+static int ifoRead_VMG(ifo_handle_t *ifofile);
+static int ifoRead_VTS(ifo_handle_t *ifofile);
+static int ifoRead_PGC(ifo_handle_t *ifofile, pgc_t *pgc, unsigned int offset);
+static int ifoRead_PGC_COMMAND_TBL(ifo_handle_t *ifofile,
+ pgc_command_tbl_t *cmd_tbl,
+ unsigned int offset);
+static int ifoRead_PGC_PROGRAM_MAP(ifo_handle_t *ifofile,
+ pgc_program_map_t *program_map,
+ unsigned int nr, unsigned int offset);
+static int ifoRead_CELL_PLAYBACK_TBL(ifo_handle_t *ifofile,
+ cell_playback_t *cell_playback,
+ unsigned int nr, unsigned int offset);
+static int ifoRead_CELL_POSITION_TBL(ifo_handle_t *ifofile,
+ cell_position_t *cell_position,
+ unsigned int nr, unsigned int offset);
+static int ifoRead_VTS_ATTRIBUTES(ifo_handle_t *ifofile,
+ vts_attributes_t *vts_attributes,
+ unsigned int offset);
+static int ifoRead_C_ADT_internal(ifo_handle_t *ifofile, c_adt_t *c_adt,
+ unsigned int sector);
+static int ifoRead_VOBU_ADMAP_internal(ifo_handle_t *ifofile,
+ vobu_admap_t *vobu_admap,
+ unsigned int sector);
+static int ifoRead_PGCIT_internal(ifo_handle_t *ifofile, pgcit_t *pgcit,
+ unsigned int offset);
+
+static void ifoFree_PGC(pgc_t *pgc);
+static void ifoFree_PGC_COMMAND_TBL(pgc_command_tbl_t *cmd_tbl);
+static void ifoFree_PGCIT_internal(pgcit_t *pgcit);
+
+
+static int DVDFileSeek_( dvd_file_t *dvd_file, uint32_t offset ) {
+ return (DVDFileSeek(dvd_file, (int)offset) == (int)offset);
+}
+
+
+ifo_handle_t *ifoOpen(dvd_reader_t *dvd, int title) {
+ ifo_handle_t *ifofile;
+
+ ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
+ if(!ifofile)
+ return 0;
+
+ memset(ifofile, 0, sizeof(ifo_handle_t));
+
+ ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_FILE);
+ if(!ifofile->file) {
+ if(title) {
+ fprintf(stderr, "libdvdread: Can't open file VTS_%02d_0.IFO.\n", title);
+ } else {
+ fprintf(stderr, "libdvdread: Can't open file VIDEO_TS.IFO.\n");
+ }
+ free(ifofile);
+ return 0;
+ }
+
+ /* First check if this is a VMGI file. */
+ if(ifoRead_VMG(ifofile)) {
+
+ /* These are both mandatory. */
+ if(!ifoRead_FP_PGC(ifofile) || !ifoRead_TT_SRPT(ifofile)) {
+ fprintf(stderr, "libdvdread: Invalid main menu IFO (VIDEO_TS.IFO).\n");
+ ifoClose(ifofile);
+ return 0;
+ }
+
+ ifoRead_PGCI_UT(ifofile);
+ ifoRead_PTL_MAIT(ifofile);
+
+ /* This is also mandatory. */
+ if(!ifoRead_VTS_ATRT(ifofile)) {
+ fprintf(stderr, "libdvdread: Invalid main menu IFO (VIDEO_TS.IFO).\n");
+ ifoClose(ifofile);
+ return 0;
+ }
+
+ ifoRead_TXTDT_MGI(ifofile);
+ ifoRead_C_ADT(ifofile);
+ ifoRead_VOBU_ADMAP(ifofile);
+
+ return ifofile;
+ }
+
+ if(ifoRead_VTS(ifofile)) {
+
+ if(!ifoRead_VTS_PTT_SRPT(ifofile) || !ifoRead_PGCIT(ifofile)) {
+ fprintf(stderr, "libdvdread: Invalid title IFO (VTS_%02d_0.IFO).\n",
+ title);
+ ifoClose(ifofile);
+ return 0;
+ }
+
+
+ ifoRead_PGCI_UT(ifofile);
+ ifoRead_C_ADT(ifofile);
+ ifoRead_VOBU_ADMAP(ifofile);
+
+ if(!ifoRead_TITLE_C_ADT(ifofile) || !ifoRead_TITLE_VOBU_ADMAP(ifofile)) {
+ fprintf(stderr, "libdvdread: Invalid title IFO (VTS_%02d_0.IFO).\n",
+ title);
+ ifoClose(ifofile);
+ return 0;
+ }
+
+ return ifofile;
+ }
+
+ if(title) {
+ fprintf(stderr, "libdvdread: Invalid IFO for title %d (VTS_%02d_0.IFO).\n",
+ title, title);
+ } else {
+ fprintf(stderr, "libdvdread: Invalid IFO for VMGM (VIDEO_TS.IFO).\n");
+ }
+ ifoClose(ifofile);
+ return 0;
+}
+
+
+ifo_handle_t *ifoOpenVMGI(dvd_reader_t *dvd) {
+ ifo_handle_t *ifofile;
+
+ ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
+ if(!ifofile)
+ return 0;
+
+ memset(ifofile, 0, sizeof(ifo_handle_t));
+
+ ifofile->file = DVDOpenFile(dvd, 0, DVD_READ_INFO_FILE);
+ if(!ifofile->file) {
+ fprintf(stderr, "libdvdread: Can't open file VIDEO_TS.IFO.\n");
+ free(ifofile);
+ return 0;
+ }
+
+ if(ifoRead_VMG(ifofile))
+ return ifofile;
+
+ fprintf(stderr, "libdvdread: Invalid main menu IFO (VIDEO_TS.IFO).\n");
+ ifoClose(ifofile);
+ return 0;
+}
+
+
+ifo_handle_t *ifoOpenVTSI(dvd_reader_t *dvd, int title) {
+ ifo_handle_t *ifofile;
+
+ ifofile = (ifo_handle_t *)malloc(sizeof(ifo_handle_t));
+ if(!ifofile)
+ return 0;
+
+ memset(ifofile, 0, sizeof(ifo_handle_t));
+
+ if(title <= 0 || title > 99) {
+ fprintf(stderr, "libdvdread: ifoOpenVTSI invalid title (%d).\n", title);
+ free(ifofile);
+ return 0;
+ }
+
+ ifofile->file = DVDOpenFile(dvd, title, DVD_READ_INFO_FILE);
+ if(!ifofile->file) {
+ fprintf(stderr, "libdvdread: Can't open file VTS_%02d_0.IFO.\n", title);
+ free(ifofile);
+ return 0;
+ }
+
+ ifoRead_VTS(ifofile);
+ if(ifofile->vtsi_mat)
+ return ifofile;
+
+ fprintf(stderr, "libdvdread: Invalid IFO for title %d (VTS_%02d_0.IFO).\n",
+ title, title);
+ ifoClose(ifofile);
+ return 0;
+}
+
+
+void ifoClose(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ ifoFree_VOBU_ADMAP(ifofile);
+ ifoFree_TITLE_VOBU_ADMAP(ifofile);
+ ifoFree_C_ADT(ifofile);
+ ifoFree_TITLE_C_ADT(ifofile);
+ ifoFree_TXTDT_MGI(ifofile);
+ ifoFree_VTS_ATRT(ifofile);
+ ifoFree_PTL_MAIT(ifofile);
+ ifoFree_PGCI_UT(ifofile);
+ ifoFree_TT_SRPT(ifofile);
+ ifoFree_FP_PGC(ifofile);
+ ifoFree_PGCIT(ifofile);
+ ifoFree_VTS_PTT_SRPT(ifofile);
+
+ if(ifofile->vmgi_mat)
+ free(ifofile->vmgi_mat);
+
+ if(ifofile->vtsi_mat)
+ free(ifofile->vtsi_mat);
+
+ DVDCloseFile(ifofile->file);
+ ifofile->file = 0;
+ free(ifofile);
+ ifofile = 0;
+}
+
+
+static int ifoRead_VMG(ifo_handle_t *ifofile) {
+ vmgi_mat_t *vmgi_mat;
+
+ vmgi_mat = (vmgi_mat_t *)malloc(sizeof(vmgi_mat_t));
+ if(!vmgi_mat)
+ return 0;
+
+ ifofile->vmgi_mat = vmgi_mat;
+
+ if(!DVDFileSeek_(ifofile->file, 0)) {
+ free(ifofile->vmgi_mat);
+ ifofile->vmgi_mat = 0;
+ return 0;
+ }
+
+ if(!DVDReadBytes(ifofile->file, vmgi_mat, sizeof(vmgi_mat_t))) {
+ free(ifofile->vmgi_mat);
+ ifofile->vmgi_mat = 0;
+ return 0;
+ }
+
+ if(strncmp("DVDVIDEO-VMG", vmgi_mat->vmg_identifier, 12) != 0) {
+ free(ifofile->vmgi_mat);
+ ifofile->vmgi_mat = 0;
+ return 0;
+ }
+
+ B2N_32(vmgi_mat->vmg_last_sector);
+ B2N_32(vmgi_mat->vmgi_last_sector);
+ B2N_32(vmgi_mat->vmg_category);
+ B2N_16(vmgi_mat->vmg_nr_of_volumes);
+ B2N_16(vmgi_mat->vmg_this_volume_nr);
+ B2N_16(vmgi_mat->vmg_nr_of_title_sets);
+ B2N_64(vmgi_mat->vmg_pos_code);
+ B2N_32(vmgi_mat->vmgi_last_byte);
+ B2N_32(vmgi_mat->first_play_pgc);
+ B2N_32(vmgi_mat->vmgm_vobs);
+ B2N_32(vmgi_mat->tt_srpt);
+ B2N_32(vmgi_mat->vmgm_pgci_ut);
+ B2N_32(vmgi_mat->ptl_mait);
+ B2N_32(vmgi_mat->vts_atrt);
+ B2N_32(vmgi_mat->txtdt_mgi);
+ B2N_32(vmgi_mat->vmgm_c_adt);
+ B2N_32(vmgi_mat->vmgm_vobu_admap);
+ B2N_16(vmgi_mat->vmgm_audio_attr.lang_code);
+ B2N_16(vmgi_mat->vmgm_subp_attr.lang_code);
+
+
+ CHECK_ZERO(vmgi_mat->zero_1);
+ CHECK_ZERO(vmgi_mat->zero_2);
+ CHECK_ZERO(vmgi_mat->zero_3);
+ CHECK_ZERO(vmgi_mat->zero_4);
+ CHECK_ZERO(vmgi_mat->zero_5);
+ CHECK_ZERO(vmgi_mat->zero_6);
+ CHECK_ZERO(vmgi_mat->zero_7);
+ CHECK_ZERO(vmgi_mat->zero_8);
+ CHECK_ZERO(vmgi_mat->zero_9);
+ CHECK_ZERO(vmgi_mat->zero_10);
+ assert(vmgi_mat->vmg_last_sector != 0);
+ assert(vmgi_mat->vmgi_last_sector != 0);
+ assert(vmgi_mat->vmgi_last_sector * 2 <= vmgi_mat->vmg_last_sector);
+ assert(vmgi_mat->vmg_nr_of_volumes != 0);
+ assert(vmgi_mat->vmg_this_volume_nr != 0);
+ assert(vmgi_mat->vmg_this_volume_nr <= vmgi_mat->vmg_nr_of_volumes);
+ assert(vmgi_mat->disc_side == 1 || vmgi_mat->disc_side == 2);
+ assert(vmgi_mat->vmg_nr_of_title_sets != 0);
+ assert(vmgi_mat->vmgi_last_byte >= 341);
+ assert(vmgi_mat->vmgi_last_byte / DVD_BLOCK_LEN <=
+ vmgi_mat->vmgi_last_sector);
+ /* It seems that first_play_pgc might be optional. */
+ assert(vmgi_mat->first_play_pgc != 0 &&
+ vmgi_mat->first_play_pgc < vmgi_mat->vmgi_last_byte);
+ assert(vmgi_mat->vmgm_vobs == 0 ||
+ (vmgi_mat->vmgm_vobs > vmgi_mat->vmgi_last_sector &&
+ vmgi_mat->vmgm_vobs < vmgi_mat->vmg_last_sector));
+ assert(vmgi_mat->tt_srpt <= vmgi_mat->vmgi_last_sector);
+ assert(vmgi_mat->vmgm_pgci_ut <= vmgi_mat->vmgi_last_sector);
+ assert(vmgi_mat->ptl_mait <= vmgi_mat->vmgi_last_sector);
+ assert(vmgi_mat->vts_atrt <= vmgi_mat->vmgi_last_sector);
+ assert(vmgi_mat->txtdt_mgi <= vmgi_mat->vmgi_last_sector);
+ assert(vmgi_mat->vmgm_c_adt <= vmgi_mat->vmgi_last_sector);
+ assert(vmgi_mat->vmgm_vobu_admap <= vmgi_mat->vmgi_last_sector);
+
+ assert(vmgi_mat->nr_of_vmgm_audio_streams <= 1);
+ assert(vmgi_mat->nr_of_vmgm_subp_streams <= 1);
+
+ return 1;
+}
+
+
+static int ifoRead_VTS(ifo_handle_t *ifofile) {
+ vtsi_mat_t *vtsi_mat;
+ int i;
+
+ vtsi_mat = (vtsi_mat_t *)malloc(sizeof(vtsi_mat_t));
+ if(!vtsi_mat)
+ return 0;
+
+ ifofile->vtsi_mat = vtsi_mat;
+
+ if(!DVDFileSeek_(ifofile->file, 0)) {
+ free(ifofile->vtsi_mat);
+ ifofile->vtsi_mat = 0;
+ return 0;
+ }
+
+ if(!(DVDReadBytes(ifofile->file, vtsi_mat, sizeof(vtsi_mat_t)))) {
+ free(ifofile->vtsi_mat);
+ ifofile->vtsi_mat = 0;
+ return 0;
+ }
+
+ if(strncmp("DVDVIDEO-VTS", vtsi_mat->vts_identifier, 12) != 0) {
+ free(ifofile->vtsi_mat);
+ ifofile->vtsi_mat = 0;
+ return 0;
+ }
+
+ B2N_32(vtsi_mat->vts_last_sector);
+ B2N_32(vtsi_mat->vtsi_last_sector);
+ B2N_32(vtsi_mat->vts_category);
+ B2N_32(vtsi_mat->vtsi_last_byte);
+ B2N_32(vtsi_mat->vtsm_vobs);
+ B2N_32(vtsi_mat->vtstt_vobs);
+ B2N_32(vtsi_mat->vts_ptt_srpt);
+ B2N_32(vtsi_mat->vts_pgcit);
+ B2N_32(vtsi_mat->vtsm_pgci_ut);
+ B2N_32(vtsi_mat->vts_tmapt);
+ B2N_32(vtsi_mat->vtsm_c_adt);
+ B2N_32(vtsi_mat->vtsm_vobu_admap);
+ B2N_32(vtsi_mat->vts_c_adt);
+ B2N_32(vtsi_mat->vts_vobu_admap);
+ B2N_16(vtsi_mat->vtsm_audio_attr.lang_code);
+ B2N_16(vtsi_mat->vtsm_subp_attr.lang_code);
+ for(i = 0; i < 8; i++)
+ B2N_16(vtsi_mat->vts_audio_attr[i].lang_code);
+ for(i = 0; i < 32; i++)
+ B2N_16(vtsi_mat->vts_subp_attr[i].lang_code);
+
+
+ CHECK_ZERO(vtsi_mat->zero_1);
+ CHECK_ZERO(vtsi_mat->zero_2);
+ CHECK_ZERO(vtsi_mat->zero_3);
+ CHECK_ZERO(vtsi_mat->zero_4);
+ CHECK_ZERO(vtsi_mat->zero_5);
+ CHECK_ZERO(vtsi_mat->zero_6);
+ CHECK_ZERO(vtsi_mat->zero_7);
+ CHECK_ZERO(vtsi_mat->zero_8);
+ CHECK_ZERO(vtsi_mat->zero_9);
+ CHECK_ZERO(vtsi_mat->zero_10);
+ CHECK_ZERO(vtsi_mat->zero_11);
+ CHECK_ZERO(vtsi_mat->zero_12);
+ CHECK_ZERO(vtsi_mat->zero_13);
+ CHECK_ZERO(vtsi_mat->zero_14);
+ CHECK_ZERO(vtsi_mat->zero_15);
+ CHECK_ZERO(vtsi_mat->zero_16);
+ CHECK_ZERO(vtsi_mat->zero_17);
+ CHECK_ZERO(vtsi_mat->zero_18);
+ CHECK_ZERO(vtsi_mat->zero_19);
+ CHECK_ZERO(vtsi_mat->zero_20);
+ assert(vtsi_mat->vtsi_last_sector*2 <= vtsi_mat->vts_last_sector);
+ assert(vtsi_mat->vtsi_last_byte/DVD_BLOCK_LEN <= vtsi_mat->vtsi_last_sector);
+ assert(vtsi_mat->vtsm_vobs == 0 ||
+ (vtsi_mat->vtsm_vobs > vtsi_mat->vtsi_last_sector &&
+ vtsi_mat->vtsm_vobs < vtsi_mat->vts_last_sector));
+ assert(vtsi_mat->vtstt_vobs == 0 ||
+ (vtsi_mat->vtstt_vobs > vtsi_mat->vtsi_last_sector &&
+ vtsi_mat->vtstt_vobs < vtsi_mat->vts_last_sector));
+ assert(vtsi_mat->vts_ptt_srpt <= vtsi_mat->vtsi_last_sector);
+ assert(vtsi_mat->vts_pgcit <= vtsi_mat->vtsi_last_sector);
+ assert(vtsi_mat->vtsm_pgci_ut <= vtsi_mat->vtsi_last_sector);
+ assert(vtsi_mat->vts_tmapt <= vtsi_mat->vtsi_last_sector);
+ assert(vtsi_mat->vtsm_c_adt <= vtsi_mat->vtsi_last_sector);
+ assert(vtsi_mat->vtsm_vobu_admap <= vtsi_mat->vtsi_last_sector);
+ assert(vtsi_mat->vts_c_adt <= vtsi_mat->vtsi_last_sector);
+ assert(vtsi_mat->vts_vobu_admap <= vtsi_mat->vtsi_last_sector);
+
+ assert(vtsi_mat->nr_of_vtsm_audio_streams <= 1);
+ assert(vtsi_mat->nr_of_vtsm_subp_streams <= 1);
+
+ assert(vtsi_mat->nr_of_vts_audio_streams <= 8);
+ for(i = vtsi_mat->nr_of_vts_audio_streams; i < 8; i++)
+ CHECK_ZERO(vtsi_mat->vts_audio_attr[i]);
+
+ assert(vtsi_mat->nr_of_vts_subp_streams <= 32);
+ for(i = vtsi_mat->nr_of_vts_subp_streams; i < 32; i++)
+ CHECK_ZERO(vtsi_mat->vts_subp_attr[i]);
+
+ return 1;
+}
+
+
+static int ifoRead_PGC_COMMAND_TBL(ifo_handle_t *ifofile,
+ pgc_command_tbl_t *cmd_tbl,
+ unsigned int offset) {
+
+ memset(cmd_tbl, 0, sizeof(pgc_command_tbl_t));
+
+ if(!DVDFileSeek_(ifofile->file, offset))
+ return 0;
+
+ if(!(DVDReadBytes(ifofile->file, cmd_tbl, PGC_COMMAND_TBL_SIZE)))
+ return 0;
+
+ B2N_16(cmd_tbl->nr_of_pre);
+ B2N_16(cmd_tbl->nr_of_post);
+ B2N_16(cmd_tbl->nr_of_cell);
+
+ assert(cmd_tbl->nr_of_pre + cmd_tbl->nr_of_post + cmd_tbl->nr_of_cell<= 255);
+
+ if(cmd_tbl->nr_of_pre != 0) {
+ unsigned int pre_cmds_size = cmd_tbl->nr_of_pre * COMMAND_DATA_SIZE;
+ cmd_tbl->pre_cmds = (vm_cmd_t *)malloc(pre_cmds_size);
+ if(!cmd_tbl->pre_cmds)
+ return 0;
+
+ if(!(DVDReadBytes(ifofile->file, cmd_tbl->pre_cmds, pre_cmds_size))) {
+ free(cmd_tbl->pre_cmds);
+ return 0;
+ }
+ }
+
+ if(cmd_tbl->nr_of_post != 0) {
+ unsigned int post_cmds_size = cmd_tbl->nr_of_post * COMMAND_DATA_SIZE;
+ cmd_tbl->post_cmds = (vm_cmd_t *)malloc(post_cmds_size);
+ if(!cmd_tbl->post_cmds) {
+ if(cmd_tbl->pre_cmds)
+ free(cmd_tbl->pre_cmds);
+ return 0;
+ }
+ if(!(DVDReadBytes(ifofile->file, cmd_tbl->post_cmds, post_cmds_size))) {
+ if(cmd_tbl->pre_cmds)
+ free(cmd_tbl->pre_cmds);
+ free(cmd_tbl->post_cmds);
+ return 0;
+ }
+ }
+
+ if(cmd_tbl->nr_of_cell != 0) {
+ unsigned int cell_cmds_size = cmd_tbl->nr_of_cell * COMMAND_DATA_SIZE;
+ cmd_tbl->cell_cmds = (vm_cmd_t *)malloc(cell_cmds_size);
+ if(!cmd_tbl->cell_cmds) {
+ if(cmd_tbl->pre_cmds)
+ free(cmd_tbl->pre_cmds);
+ if(cmd_tbl->post_cmds)
+ free(cmd_tbl->post_cmds);
+ return 0;
+ }
+ if(!(DVDReadBytes(ifofile->file, cmd_tbl->cell_cmds, cell_cmds_size))) {
+ if(cmd_tbl->pre_cmds)
+ free(cmd_tbl->pre_cmds);
+ if(cmd_tbl->post_cmds)
+ free(cmd_tbl->post_cmds);
+ free(cmd_tbl->cell_cmds);
+ return 0;
+ }
+ }
+
+ /*
+ * Make a run over all the commands and see that we can interpret them all?
+ */
+ return 1;
+}
+
+
+static void ifoFree_PGC_COMMAND_TBL(pgc_command_tbl_t *cmd_tbl) {
+ if(cmd_tbl) {
+ if(cmd_tbl->nr_of_pre && cmd_tbl->pre_cmds)
+ free(cmd_tbl->pre_cmds);
+ if(cmd_tbl->nr_of_post && cmd_tbl->post_cmds)
+ free(cmd_tbl->post_cmds);
+ if(cmd_tbl->nr_of_cell && cmd_tbl->cell_cmds)
+ free(cmd_tbl->cell_cmds);
+ free(cmd_tbl);
+ }
+}
+
+static int ifoRead_PGC_PROGRAM_MAP(ifo_handle_t *ifofile,
+ pgc_program_map_t *program_map,
+ unsigned int nr, unsigned int offset) {
+ unsigned int size = nr * sizeof(pgc_program_map_t);
+
+ if(!DVDFileSeek_(ifofile->file, offset))
+ return 0;
+
+ if(!(DVDReadBytes(ifofile->file, program_map, size)))
+ return 0;
+
+ return 1;
+}
+
+static int ifoRead_CELL_PLAYBACK_TBL(ifo_handle_t *ifofile,
+ cell_playback_t *cell_playback,
+ unsigned int nr, unsigned int offset) {
+ unsigned int i;
+ unsigned int size = nr * sizeof(cell_playback_t);
+
+ if(!DVDFileSeek_(ifofile->file, offset))
+ return 0;
+
+ if(!(DVDReadBytes(ifofile->file, cell_playback, size)))
+ return 0;
+
+ for(i = 0; i < nr; i++) {
+ B2N_32(cell_playback[i].first_sector);
+ B2N_32(cell_playback[i].first_ilvu_end_sector);
+ B2N_32(cell_playback[i].last_vobu_start_sector);
+ B2N_32(cell_playback[i].last_sector);
+
+ /* Changed < to <= because this was false in the movie 'Pi'. */
+ assert(cell_playback[i].last_vobu_start_sector <=
+ cell_playback[i].last_sector);
+ assert(cell_playback[i].first_sector <=
+ cell_playback[i].last_vobu_start_sector);
+ }
+
+ return 1;
+}
+
+
+static int ifoRead_CELL_POSITION_TBL(ifo_handle_t *ifofile,
+ cell_position_t *cell_position,
+ unsigned int nr, unsigned int offset) {
+ unsigned int i;
+ unsigned int size = nr * sizeof(cell_position_t);
+
+ if(!DVDFileSeek_(ifofile->file, offset))
+ return 0;
+
+ if(!(DVDReadBytes(ifofile->file, cell_position, size)))
+ return 0;
+
+ for(i = 0; i < nr; i++) {
+ B2N_16(cell_position[i].vob_id_nr);
+ CHECK_ZERO(cell_position[i].zero_1);
+ }
+
+ return 1;
+}
+
+static int ifoRead_PGC(ifo_handle_t *ifofile, pgc_t *pgc, unsigned int offset) {
+ unsigned int i;
+
+ if(!DVDFileSeek_(ifofile->file, offset))
+ return 0;
+
+ if(!(DVDReadBytes(ifofile->file, pgc, PGC_SIZE)))
+ return 0;
+
+ B2N_16(pgc->next_pgc_nr);
+ B2N_16(pgc->prev_pgc_nr);
+ B2N_16(pgc->goup_pgc_nr);
+ B2N_16(pgc->command_tbl_offset);
+ B2N_16(pgc->program_map_offset);
+ B2N_16(pgc->cell_playback_offset);
+ B2N_16(pgc->cell_position_offset);
+
+ for(i = 0; i < 8; i++)
+ B2N_16(pgc->audio_control[i]);
+ for(i = 0; i < 32; i++)
+ B2N_32(pgc->subp_control[i]);
+ for(i = 0; i < 16; i++)
+ B2N_32(pgc->palette[i]);
+
+ CHECK_ZERO(pgc->zero_1);
+ assert(pgc->nr_of_programs <= pgc->nr_of_cells);
+
+ /* verify time (look at print_time) */
+ for(i = 0; i < 8; i++)
+ if(!pgc->audio_control[i] & 0x8000) /* The 'is present' bit */
+ CHECK_ZERO(pgc->audio_control[i]);
+ for(i = 0; i < 32; i++)
+ if(!pgc->subp_control[i] & 0x80000000) /* The 'is present' bit */
+ CHECK_ZERO(pgc->subp_control[i]);
+
+ /* Check that time is 0:0:0:0 also if nr_of_programs == 0 */
+ if(pgc->nr_of_programs == 0) {
+ CHECK_ZERO(pgc->still_time);
+ CHECK_ZERO(pgc->pg_playback_mode); // ??
+ assert(pgc->program_map_offset == 0);
+ assert(pgc->cell_playback_offset == 0);
+ assert(pgc->cell_position_offset == 0);
+ } else {
+ assert(pgc->program_map_offset != 0);
+ assert(pgc->cell_playback_offset != 0);
+ assert(pgc->cell_position_offset != 0);
+ }
+
+ if(pgc->command_tbl_offset != 0) {
+ pgc->command_tbl = malloc(sizeof(pgc_command_tbl_t));
+ if(!pgc->command_tbl)
+ return 0;
+
+ if(!ifoRead_PGC_COMMAND_TBL(ifofile, pgc->command_tbl,
+ offset + pgc->command_tbl_offset)) {
+ free(pgc->command_tbl);
+ return 0;
+ }
+ } else {
+ pgc->command_tbl = NULL;
+ }
+
+ if(pgc->program_map_offset != 0) {
+ pgc->program_map = malloc(pgc->nr_of_programs * sizeof(pgc_program_map_t));
+ if(!pgc->program_map) {
+ ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+ return 0;
+ }
+ if(!ifoRead_PGC_PROGRAM_MAP(ifofile, pgc->program_map,pgc->nr_of_programs,
+ offset + pgc->program_map_offset)) {
+ ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+ free(pgc->program_map);
+ return 0;
+ }
+ } else {
+ pgc->program_map = NULL;
+ }
+
+ if(pgc->cell_playback_offset != 0) {
+ pgc->cell_playback = malloc(pgc->nr_of_cells * sizeof(cell_playback_t));
+ if(!pgc->cell_playback) {
+ ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+ if(pgc->program_map)
+ free(pgc->program_map);
+ return 0;
+ }
+ if(!ifoRead_CELL_PLAYBACK_TBL(ifofile, pgc->cell_playback,
+ pgc->nr_of_cells,
+ offset + pgc->cell_playback_offset)) {
+ ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+ if(pgc->program_map)
+ free(pgc->program_map);
+ free(pgc->cell_playback);
+ return 0;
+ }
+ } else {
+ pgc->cell_playback = NULL;
+ }
+
+ if(pgc->cell_position_offset != 0) {
+ pgc->cell_position = malloc(pgc->nr_of_cells * sizeof(cell_position_t));
+ if(!pgc->cell_position) {
+ ifoFree_PGC(pgc);
+ return 0;
+ }
+ if(!ifoRead_CELL_POSITION_TBL(ifofile, pgc->cell_position,
+ pgc->nr_of_cells,
+ offset + pgc->cell_position_offset)) {
+ ifoFree_PGC(pgc);
+ return 0;
+ }
+ } else {
+ pgc->cell_position = NULL;
+ }
+
+ return 1;
+}
+
+int ifoRead_FP_PGC(ifo_handle_t *ifofile) {
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vmgi_mat)
+ return 0;
+
+ /* It seems that first_play_pgc might be optional after all. */
+ if(ifofile->vmgi_mat->first_play_pgc == 0) { /* mandatory */
+ ifofile->first_play_pgc = 0;
+ return 0; /* change this to a 1 if it's optional. */
+ }
+
+ ifofile->first_play_pgc = (pgc_t *)malloc(sizeof(pgc_t));
+ if(!ifofile->first_play_pgc)
+ return 0;
+
+ if(!ifoRead_PGC(ifofile, ifofile->first_play_pgc,
+ ifofile->vmgi_mat->first_play_pgc)) {
+ free(ifofile->first_play_pgc);
+ ifofile->first_play_pgc = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+static void ifoFree_PGC(pgc_t *pgc) {
+ if(pgc) {
+ ifoFree_PGC_COMMAND_TBL(pgc->command_tbl);
+ if(pgc->program_map)
+ free(pgc->program_map);
+ if(pgc->cell_playback)
+ free(pgc->cell_playback);
+ if(pgc->cell_position)
+ free(pgc->cell_position);
+ }
+}
+
+void ifoFree_FP_PGC(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ if(ifofile->first_play_pgc) {
+ ifoFree_PGC(ifofile->first_play_pgc);
+ free(ifofile->first_play_pgc);
+ ifofile->first_play_pgc = 0;
+ }
+}
+
+
+int ifoRead_TT_SRPT(ifo_handle_t *ifofile) {
+ tt_srpt_t *tt_srpt;
+ int i, info_length;
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vmgi_mat)
+ return 0;
+
+ if(ifofile->vmgi_mat->tt_srpt == 0) /* mandatory */
+ return 0;
+
+ if(!DVDFileSeek_(ifofile->file, ifofile->vmgi_mat->tt_srpt * DVD_BLOCK_LEN))
+ return 0;
+
+ tt_srpt = (tt_srpt_t *)malloc(sizeof(tt_srpt_t));
+ if(!tt_srpt)
+ return 0;
+
+ ifofile->tt_srpt = tt_srpt;
+
+ if(!(DVDReadBytes(ifofile->file, tt_srpt, TT_SRPT_SIZE))) {
+ fprintf(stderr, "libdvdread: Unable to read read TT_SRPT.\n");
+ free(tt_srpt);
+ return 0;
+ }
+
+ B2N_16(tt_srpt->nr_of_srpts);
+ B2N_32(tt_srpt->last_byte);
+
+ info_length = tt_srpt->last_byte + 1 - TT_SRPT_SIZE;
+
+ tt_srpt->title = (title_info_t *)malloc(info_length);
+ if(!tt_srpt->title) {
+ free(tt_srpt);
+ ifofile->tt_srpt = 0;
+ return 0;
+ }
+ if(!(DVDReadBytes(ifofile->file, tt_srpt->title, info_length))) {
+ fprintf(stderr, "libdvdread: Unable to read read TT_SRPT.\n");
+ ifoFree_TT_SRPT(ifofile);
+ return 0;
+ }
+
+ for(i = 0; i < tt_srpt->nr_of_srpts; i++) {
+ B2N_16(tt_srpt->title[i].nr_of_ptts);
+ B2N_16(tt_srpt->title[i].parental_id);
+ B2N_32(tt_srpt->title[i].title_set_sector);
+ }
+
+
+ CHECK_ZERO(tt_srpt->zero_1);
+ assert(tt_srpt->nr_of_srpts != 0);
+ assert(tt_srpt->nr_of_srpts < 100); // ??
+ assert((int)tt_srpt->nr_of_srpts * sizeof(title_info_t) <= info_length);
+
+ for(i = 0; i < tt_srpt->nr_of_srpts; i++) {
+ assert(tt_srpt->title[i].pb_ty.zero_1 == 0);
+ assert(tt_srpt->title[i].nr_of_angles != 0);
+ assert(tt_srpt->title[i].nr_of_angles < 10);
+ //assert(tt_srpt->title[i].nr_of_ptts != 0);
+ // XXX: this assertion breaks Ghostbusters:
+ assert(tt_srpt->title[i].nr_of_ptts < 1000); // ??
+ assert(tt_srpt->title[i].title_set_nr != 0);
+ assert(tt_srpt->title[i].title_set_nr < 100); // ??
+ assert(tt_srpt->title[i].vts_ttn != 0);
+ assert(tt_srpt->title[i].vts_ttn < 100); // ??
+ //assert(tt_srpt->title[i].title_set_sector != 0);
+ }
+
+ // Make this a function
+#if 0
+ if(memcmp((uint8_t *)tt_srpt->title +
+ tt_srpt->nr_of_srpts * sizeof(title_info_t),
+ my_friendly_zeros,
+ info_length - tt_srpt->nr_of_srpts * sizeof(title_info_t))) {
+ fprintf(stderr, "VMG_PTT_SRPT slack is != 0, ");
+ hexdump((uint8_t *)tt_srpt->title +
+ tt_srpt->nr_of_srpts * sizeof(title_info_t),
+ info_length - tt_srpt->nr_of_srpts * sizeof(title_info_t));
+ }
+#endif
+
+ return 1;
+}
+
+
+void ifoFree_TT_SRPT(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ if(ifofile->tt_srpt) {
+ free(ifofile->tt_srpt->title);
+ free(ifofile->tt_srpt);
+ ifofile->tt_srpt = 0;
+ }
+}
+
+
+int ifoRead_VTS_PTT_SRPT(ifo_handle_t *ifofile) {
+ vts_ptt_srpt_t *vts_ptt_srpt;
+ int info_length, i, j;
+ uint32_t *data;
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vtsi_mat)
+ return 0;
+
+ if(ifofile->vtsi_mat->vts_ptt_srpt == 0) /* mandatory */
+ return 0;
+
+ if(!DVDFileSeek_(ifofile->file,
+ ifofile->vtsi_mat->vts_ptt_srpt * DVD_BLOCK_LEN))
+ return 0;
+
+ vts_ptt_srpt = (vts_ptt_srpt_t *)malloc(sizeof(vts_ptt_srpt_t));
+ if(!vts_ptt_srpt)
+ return 0;
+
+ ifofile->vts_ptt_srpt = vts_ptt_srpt;
+
+ if(!(DVDReadBytes(ifofile->file, vts_ptt_srpt, VTS_PTT_SRPT_SIZE))) {
+ fprintf(stderr, "libdvdread: Unable to read PTT search table.\n");
+ free(vts_ptt_srpt);
+ return 0;
+ }
+
+ B2N_16(vts_ptt_srpt->nr_of_srpts);
+ B2N_32(vts_ptt_srpt->last_byte);
+
+ CHECK_ZERO(vts_ptt_srpt->zero_1);
+ assert(vts_ptt_srpt->nr_of_srpts != 0);
+ assert(vts_ptt_srpt->nr_of_srpts < 100); // ??
+
+ info_length = vts_ptt_srpt->last_byte + 1 - VTS_PTT_SRPT_SIZE;
+
+ data = (uint32_t *)malloc(info_length);
+ if(!data) {
+ free(vts_ptt_srpt);
+ ifofile->vts_ptt_srpt = 0;
+ return 0;
+ }
+ if(!(DVDReadBytes(ifofile->file, data, info_length))) {
+ fprintf(stderr, "libdvdread: Unable to read PTT search table.\n");
+ free(vts_ptt_srpt);
+ free(data);
+ ifofile->vts_ptt_srpt = 0;
+ return 0;
+ }
+
+ for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+ B2N_32(data[i]);
+ /* assert(data[i] + sizeof(ptt_info_t) <= vts_ptt_srpt->last_byte + 1);
+ Magic Knight Rayearth Daybreak is mastered very strange and has
+ Titles with 0 PTTs. They all have a data[i] offsets beyond the end of
+ of the vts_ptt_srpt structure. */
+ assert(data[i] + sizeof(ptt_info_t) <= vts_ptt_srpt->last_byte + 1 + 4);
+ }
+
+ vts_ptt_srpt->title = malloc(vts_ptt_srpt->nr_of_srpts * sizeof(ttu_t));
+ if(!vts_ptt_srpt->title) {
+ free(vts_ptt_srpt);
+ free(data);
+ ifofile->vts_ptt_srpt = 0;
+ return 0;
+ }
+ for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+ int n;
+ if(i < vts_ptt_srpt->nr_of_srpts - 1)
+ n = (data[i+1] - data[i]);
+ else
+ n = (vts_ptt_srpt->last_byte + 1 - data[i]);
+ /* assert(n > 0 && (n % 4) == 0);
+ Magic Knight Rayearth Daybreak is mastered very strange and has
+ Titles with 0 PTTs. */
+ if(n < 0) n = 0;
+ assert(n % 4 == 0);
+
+ vts_ptt_srpt->title[i].nr_of_ptts = n / 4;
+ vts_ptt_srpt->title[i].ptt = malloc(n * sizeof(ptt_info_t));
+ if(!vts_ptt_srpt->title[i].ptt) {
+ for(n = 0; n < i; n++)
+ free(vts_ptt_srpt->title[n].ptt);
+ free(vts_ptt_srpt);
+ free(data);
+ ifofile->vts_ptt_srpt = 0;
+ return 0;
+ }
+ for(j = 0; j < vts_ptt_srpt->title[i].nr_of_ptts; j++) {
+ /* The assert placed here because of Magic Knight Rayearth Daybreak */
+ assert(data[i] + sizeof(ptt_info_t) <= vts_ptt_srpt->last_byte + 1);
+ vts_ptt_srpt->title[i].ptt[j].pgcn
+ = *(uint16_t*)(((char *)data) + data[i] + 4*j - VTS_PTT_SRPT_SIZE);
+ vts_ptt_srpt->title[i].ptt[j].pgn
+ = *(uint16_t*)(((char *)data) + data[i] + 4*j + 2 - VTS_PTT_SRPT_SIZE);
+ }
+ }
+ free(data);
+
+ for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+ for(j = 0; j < vts_ptt_srpt->title[i].nr_of_ptts; j++) {
+ B2N_16(vts_ptt_srpt->title[i].ptt[j].pgcn);
+ B2N_16(vts_ptt_srpt->title[i].ptt[j].pgn);
+ }
+ }
+
+ for(i = 0; i < vts_ptt_srpt->nr_of_srpts; i++) {
+ assert(vts_ptt_srpt->title[i].nr_of_ptts < 1000); // ??
+ for(j = 0; j < vts_ptt_srpt->title[i].nr_of_ptts; j++) {
+ assert(vts_ptt_srpt->title[i].ptt[j].pgcn != 0 );
+ assert(vts_ptt_srpt->title[i].ptt[j].pgcn < 1000); // ??
+ assert(vts_ptt_srpt->title[i].ptt[j].pgn != 0);
+ assert(vts_ptt_srpt->title[i].ptt[j].pgn < 100); // ??
+ }
+ }
+
+ return 1;
+}
+
+
+void ifoFree_VTS_PTT_SRPT(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ if(ifofile->vts_ptt_srpt) {
+ int i;
+ for(i = 0; i < ifofile->vts_ptt_srpt->nr_of_srpts; i++)
+ free(ifofile->vts_ptt_srpt->title[i].ptt);
+ free(ifofile->vts_ptt_srpt->title);
+ free(ifofile->vts_ptt_srpt);
+ ifofile->vts_ptt_srpt = 0;
+ }
+}
+
+
+int ifoRead_PTL_MAIT(ifo_handle_t *ifofile) {
+ ptl_mait_t *ptl_mait;
+ int info_length;
+ unsigned int i;
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vmgi_mat)
+ return 0;
+
+ if(ifofile->vmgi_mat->ptl_mait == 0)
+ return 1;
+
+ if(!DVDFileSeek_(ifofile->file,
+ ifofile->vmgi_mat->ptl_mait * DVD_BLOCK_LEN))
+ return 0;
+
+ ptl_mait = (ptl_mait_t *)malloc(sizeof(ptl_mait_t));
+ if(!ptl_mait)
+ return 0;
+
+ ifofile->ptl_mait = ptl_mait;
+
+ if(!(DVDReadBytes(ifofile->file, ptl_mait, PTL_MAIT_SIZE))) {
+ free(ptl_mait);
+ ifofile->ptl_mait = 0;
+ return 0;
+ }
+
+ B2N_16(ptl_mait->nr_of_countries);
+ B2N_16(ptl_mait->nr_of_vtss);
+ B2N_32(ptl_mait->last_byte);
+
+ info_length = ptl_mait->last_byte + 1 - PTL_MAIT_SIZE;
+
+ assert(ptl_mait->nr_of_countries != 0);
+ assert(ptl_mait->nr_of_countries < 100); // ??
+ assert(ptl_mait->nr_of_vtss != 0);
+ assert(ptl_mait->nr_of_vtss < 100); // ??
+ assert(ptl_mait->nr_of_countries * PTL_MAIT_COUNTRY_SIZE <= info_length);
+
+ /* Change this to read and 'translate' the tables too.
+ I.e don't read so much here */
+ ptl_mait->countries = (ptl_mait_country_t *)malloc(info_length);
+ if(!ptl_mait->countries) {
+ free(ptl_mait);
+ ifofile->ptl_mait = 0;
+ return 0;
+ }
+ if(!(DVDReadBytes(ifofile->file, ptl_mait->countries, info_length))) {
+ fprintf(stderr, "libdvdread: Unable to read PTL_MAIT.\n");
+ ifoFree_PTL_MAIT(ifofile);
+ return 0;
+ }
+
+ for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+ B2N_16(ptl_mait->countries[i].country_code);
+ B2N_16(ptl_mait->countries[i].pf_ptl_mai_start_byte);
+ }
+
+ for(i = 0; i < ptl_mait->nr_of_countries; i++) {
+ CHECK_ZERO(ptl_mait->countries[i].zero_1);
+ CHECK_ZERO(ptl_mait->countries[i].zero_2);
+ assert(ptl_mait->countries[i].pf_ptl_mai_start_byte +
+ 8 * (ptl_mait->nr_of_vtss + 1) * 2 <= ptl_mait->last_byte + 1);
+ }
+
+ return 1;
+}
+
+
+void ifoFree_PTL_MAIT(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ if(ifofile->ptl_mait) {
+ free(ifofile->ptl_mait->countries);
+ free(ifofile->ptl_mait);
+ ifofile->ptl_mait = 0;
+ }
+}
+
+int ifoRead_TITLE_C_ADT(ifo_handle_t *ifofile) {
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vtsi_mat)
+ return 0;
+
+ if(ifofile->vtsi_mat->vts_c_adt == 0) /* mandatory */
+ return 0;
+
+ ifofile->vts_c_adt = (c_adt_t *)malloc(sizeof(c_adt_t));
+ if(!ifofile->vts_c_adt)
+ return 0;
+
+ if(!ifoRead_C_ADT_internal(ifofile, ifofile->vts_c_adt,
+ ifofile->vtsi_mat->vts_c_adt)) {
+ free(ifofile->vts_c_adt);
+ ifofile->vts_c_adt = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+int ifoRead_C_ADT(ifo_handle_t *ifofile) {
+ unsigned int sector;
+
+ if(!ifofile)
+ return 0;
+
+ if(ifofile->vmgi_mat) {
+ if(ifofile->vmgi_mat->vmgm_c_adt == 0)
+ return 1;
+ sector = ifofile->vmgi_mat->vmgm_c_adt;
+ } else if(ifofile->vtsi_mat) {
+ if(ifofile->vtsi_mat->vtsm_c_adt == 0)
+ return 1;
+ sector = ifofile->vtsi_mat->vtsm_c_adt;
+ } else {
+ return 0;
+ }
+
+ ifofile->menu_c_adt = (c_adt_t *)malloc(sizeof(c_adt_t));
+ if(!ifofile->menu_c_adt)
+ return 0;
+
+ if(!ifoRead_C_ADT_internal(ifofile, ifofile->menu_c_adt, sector)) {
+ free(ifofile->menu_c_adt);
+ ifofile->menu_c_adt = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+static int ifoRead_C_ADT_internal(ifo_handle_t *ifofile,
+ c_adt_t *c_adt, unsigned int sector) {
+ int i, info_length;
+
+ if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN))
+ return 0;
+
+ if(!(DVDReadBytes(ifofile->file, c_adt, C_ADT_SIZE)))
+ return 0;
+
+ B2N_16(c_adt->nr_of_vobs);
+ B2N_32(c_adt->last_byte);
+
+ info_length = c_adt->last_byte + 1 - C_ADT_SIZE;
+
+ CHECK_ZERO(c_adt->zero_1);
+ /* assert(c_adt->nr_of_vobs > 0);
+ Magic Knight Rayearth Daybreak is mastered very strange and has
+ Titles with a VOBS that has no cells. */
+ assert(info_length % sizeof(cell_adr_t) == 0);
+
+ /* assert(info_length / sizeof(cell_adr_t) >= c_adt->nr_of_vobs);
+ Enemy of the State region 2 (de) has Titles where nr_of_vobs field
+ is to high, they high ones are never referenced though. */
+ if(info_length / sizeof(cell_adr_t) < c_adt->nr_of_vobs) {
+ fprintf(stderr, "libdvdread: *C_ADT nr_of_vobs > avaiable info entries\n");
+ c_adt->nr_of_vobs = info_length / sizeof(cell_adr_t);
+ }
+
+ c_adt->cell_adr_table = (cell_adr_t *)malloc(info_length);
+ if(!c_adt->cell_adr_table)
+ return 0;
+
+ if(info_length &&
+ !(DVDReadBytes(ifofile->file, c_adt->cell_adr_table, info_length))) {
+ free(c_adt->cell_adr_table);
+ return 0;
+ }
+
+ for(i = 0; i < info_length/sizeof(cell_adr_t); i++) {
+ B2N_16(c_adt->cell_adr_table[i].vob_id);
+ B2N_32(c_adt->cell_adr_table[i].start_sector);
+ B2N_32(c_adt->cell_adr_table[i].last_sector);
+
+ CHECK_ZERO(c_adt->cell_adr_table[i].zero_1);
+ assert(c_adt->cell_adr_table[i].vob_id > 0);
+ assert(c_adt->cell_adr_table[i].vob_id <= c_adt->nr_of_vobs);
+ assert(c_adt->cell_adr_table[i].cell_id > 0);
+ assert(c_adt->cell_adr_table[i].start_sector <
+ c_adt->cell_adr_table[i].last_sector);
+ }
+
+ return 1;
+}
+
+
+static void ifoFree_C_ADT_internal(c_adt_t *c_adt) {
+ if(c_adt) {
+ free(c_adt->cell_adr_table);
+ free(c_adt);
+ }
+}
+
+void ifoFree_C_ADT(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ ifoFree_C_ADT_internal(ifofile->menu_c_adt);
+ ifofile->menu_c_adt = 0;
+}
+
+void ifoFree_TITLE_C_ADT(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ ifoFree_C_ADT_internal(ifofile->vts_c_adt);
+ ifofile->vts_c_adt = 0;
+}
+
+int ifoRead_TITLE_VOBU_ADMAP(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vtsi_mat)
+ return 0;
+
+ if(ifofile->vtsi_mat->vts_vobu_admap == 0) /* mandatory */
+ return 0;
+
+ ifofile->vts_vobu_admap = (vobu_admap_t *)malloc(sizeof(vobu_admap_t));
+ if(!ifofile->vts_vobu_admap)
+ return 0;
+
+ if(!ifoRead_VOBU_ADMAP_internal(ifofile, ifofile->vts_vobu_admap,
+ ifofile->vtsi_mat->vts_vobu_admap)) {
+ free(ifofile->vts_vobu_admap);
+ ifofile->vts_vobu_admap = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+int ifoRead_VOBU_ADMAP(ifo_handle_t *ifofile) {
+ unsigned int sector;
+
+ if(!ifofile)
+ return 0;
+
+ if(ifofile->vmgi_mat) {
+ if(ifofile->vmgi_mat->vmgm_vobu_admap == 0)
+ return 1;
+ sector = ifofile->vmgi_mat->vmgm_vobu_admap;
+ } else if(ifofile->vtsi_mat) {
+ if(ifofile->vtsi_mat->vtsm_vobu_admap == 0)
+ return 1;
+ sector = ifofile->vtsi_mat->vtsm_vobu_admap;
+ } else {
+ return 0;
+ }
+
+ ifofile->menu_vobu_admap = (vobu_admap_t *)malloc(sizeof(vobu_admap_t));
+ if(!ifofile->menu_vobu_admap)
+ return 0;
+
+ if(!ifoRead_VOBU_ADMAP_internal(ifofile, ifofile->menu_vobu_admap, sector)) {
+ free(ifofile->menu_vobu_admap);
+ ifofile->menu_vobu_admap = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+static int ifoRead_VOBU_ADMAP_internal(ifo_handle_t *ifofile,
+ vobu_admap_t *vobu_admap,
+ unsigned int sector) {
+ unsigned int i;
+ int info_length;
+
+ if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN))
+ return 0;
+
+ if(!(DVDReadBytes(ifofile->file, vobu_admap, VOBU_ADMAP_SIZE)))
+ return 0;
+
+ B2N_32(vobu_admap->last_byte);
+
+ info_length = vobu_admap->last_byte + 1 - VOBU_ADMAP_SIZE;
+ /* assert(info_length > 0);
+ Magic Knight Rayearth Daybreak is mastered very strange and has
+ Titles with a VOBS that has no VOBUs. */
+ assert(info_length % sizeof(uint32_t) == 0);
+
+ vobu_admap->vobu_start_sectors = (uint32_t *)malloc(info_length);
+ if(!vobu_admap->vobu_start_sectors) {
+ return 0;
+ }
+ if(info_length &&
+ !(DVDReadBytes(ifofile->file,
+ vobu_admap->vobu_start_sectors, info_length))) {
+ free(vobu_admap->vobu_start_sectors);
+ return 0;
+ }
+
+ for(i = 0; i < info_length/sizeof(uint32_t); i++)
+ B2N_32(vobu_admap->vobu_start_sectors[i]);
+
+ return 1;
+}
+
+
+static void ifoFree_VOBU_ADMAP_internal(vobu_admap_t *vobu_admap) {
+ if(vobu_admap) {
+ free(vobu_admap->vobu_start_sectors);
+ free(vobu_admap);
+ }
+}
+
+void ifoFree_VOBU_ADMAP(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ ifoFree_VOBU_ADMAP_internal(ifofile->menu_vobu_admap);
+ ifofile->menu_vobu_admap = 0;
+}
+
+void ifoFree_TITLE_VOBU_ADMAP(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ ifoFree_VOBU_ADMAP_internal(ifofile->vts_vobu_admap);
+ ifofile->vts_vobu_admap = 0;
+}
+
+int ifoRead_PGCIT(ifo_handle_t *ifofile) {
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vtsi_mat)
+ return 0;
+
+ if(ifofile->vtsi_mat->vts_pgcit == 0) /* mandatory */
+ return 0;
+
+ ifofile->vts_pgcit = (pgcit_t *)malloc(sizeof(pgcit_t));
+ if(!ifofile->vts_pgcit)
+ return 0;
+
+ if(!ifoRead_PGCIT_internal(ifofile, ifofile->vts_pgcit,
+ ifofile->vtsi_mat->vts_pgcit * DVD_BLOCK_LEN)) {
+ free(ifofile->vts_pgcit);
+ ifofile->vts_pgcit = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+static int ifoRead_PGCIT_internal(ifo_handle_t *ifofile, pgcit_t *pgcit,
+ unsigned int offset) {
+ int i, info_length;
+ uint8_t *data, *ptr;
+
+ if(!DVDFileSeek_(ifofile->file, offset))
+ return 0;
+
+ if(!(DVDReadBytes(ifofile->file, pgcit, PGCIT_SIZE)))
+ return 0;
+
+ B2N_16(pgcit->nr_of_pgci_srp);
+ B2N_32(pgcit->last_byte);
+
+ CHECK_ZERO(pgcit->zero_1);
+ /* assert(pgcit->nr_of_pgci_srp != 0);
+ Magic Knight Rayearth Daybreak is mastered very strange and has
+ Titles with 0 PTTs. */
+ assert(pgcit->nr_of_pgci_srp < 10000); // ?? seen max of 1338
+
+ info_length = pgcit->nr_of_pgci_srp * PGCI_SRP_SIZE;
+ data = malloc(info_length);
+ if(!data)
+ return 0;
+
+ if(info_length && !(DVDReadBytes(ifofile->file, data, info_length))) {
+ free(data);
+ return 0;
+ }
+
+ pgcit->pgci_srp = malloc(pgcit->nr_of_pgci_srp * sizeof(pgci_srp_t));
+ if(!pgcit->pgci_srp) {
+ free(data);
+ return 0;
+ }
+ ptr = data;
+ for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+ memcpy(&pgcit->pgci_srp[i], ptr, PGCI_LU_SIZE);
+ ptr += PGCI_LU_SIZE;
+ B2N_16(pgcit->pgci_srp[i].ptl_id_mask);
+ B2N_32(pgcit->pgci_srp[i].pgc_start_byte);
+ assert(pgcit->pgci_srp[i].unknown1 == 0);
+ }
+ free(data);
+
+ for(i = 0; i < pgcit->nr_of_pgci_srp; i++)
+ assert(pgcit->pgci_srp[i].pgc_start_byte + PGC_SIZE <= pgcit->last_byte+1);
+
+ for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+ pgcit->pgci_srp[i].pgc = malloc(sizeof(pgc_t));
+ if(!pgcit->pgci_srp[i].pgc) {
+ int j;
+ for(j = 0; j < i; j++) {
+ ifoFree_PGC(pgcit->pgci_srp[j].pgc);
+ free(pgcit->pgci_srp[j].pgc);
+ }
+ return 0;
+ }
+ if(!ifoRead_PGC(ifofile, pgcit->pgci_srp[i].pgc,
+ offset + pgcit->pgci_srp[i].pgc_start_byte)) {
+ int j;
+ for(j = 0; j < i; j++) {
+ ifoFree_PGC(pgcit->pgci_srp[j].pgc);
+ free(pgcit->pgci_srp[j].pgc);
+ }
+ free(pgcit->pgci_srp);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static void ifoFree_PGCIT_internal(pgcit_t *pgcit) {
+ if(pgcit) {
+ int i;
+ for(i = 0; i < pgcit->nr_of_pgci_srp; i++)
+ ifoFree_PGC(pgcit->pgci_srp[i].pgc);
+ free(pgcit->pgci_srp);
+ }
+}
+
+void ifoFree_PGCIT(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ if(ifofile->vts_pgcit) {
+ ifoFree_PGCIT_internal(ifofile->vts_pgcit);
+ free(ifofile->vts_pgcit);
+ ifofile->vts_pgcit = 0;
+ }
+}
+
+
+int ifoRead_PGCI_UT(ifo_handle_t *ifofile) {
+ pgci_ut_t *pgci_ut;
+ unsigned int sector;
+ unsigned int i;
+ int info_length;
+ uint8_t *data, *ptr;
+
+ if(!ifofile)
+ return 0;
+
+ if(ifofile->vmgi_mat) {
+ if(ifofile->vmgi_mat->vmgm_pgci_ut == 0)
+ return 1;
+ sector = ifofile->vmgi_mat->vmgm_pgci_ut;
+ } else if(ifofile->vtsi_mat) {
+ if(ifofile->vtsi_mat->vtsm_pgci_ut == 0)
+ return 1;
+ sector = ifofile->vtsi_mat->vtsm_pgci_ut;
+ } else {
+ return 0;
+ }
+
+ ifofile->pgci_ut = (pgci_ut_t *)malloc(sizeof(pgci_ut_t));
+ if(!ifofile->pgci_ut)
+ return 0;
+
+ if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN)) {
+ free(ifofile->pgci_ut);
+ ifofile->pgci_ut = 0;
+ return 0;
+ }
+
+ if(!(DVDReadBytes(ifofile->file, ifofile->pgci_ut, PGCI_UT_SIZE))) {
+ free(ifofile->pgci_ut);
+ ifofile->pgci_ut = 0;
+ return 0;
+ }
+
+ pgci_ut = ifofile->pgci_ut;
+
+ B2N_16(pgci_ut->nr_of_lus);
+ B2N_32(pgci_ut->last_byte);
+
+ CHECK_ZERO(pgci_ut->zero_1);
+ assert(pgci_ut->nr_of_lus != 0);
+ assert(pgci_ut->nr_of_lus < 100); // ?? 3-4 ?
+ assert((uint32_t)pgci_ut->nr_of_lus * PGCI_LU_SIZE < pgci_ut->last_byte);
+
+ info_length = pgci_ut->nr_of_lus * PGCI_LU_SIZE;
+ data = malloc(info_length);
+ if(!data) {
+ free(pgci_ut);
+ ifofile->pgci_ut = 0;
+ return 0;
+ }
+ if(!(DVDReadBytes(ifofile->file, data, info_length))) {
+ free(data);
+ free(pgci_ut);
+ ifofile->pgci_ut = 0;
+ return 0;
+ }
+
+ pgci_ut->lu = malloc(pgci_ut->nr_of_lus * sizeof(pgci_lu_t));
+ if(!pgci_ut->lu) {
+ free(data);
+ free(pgci_ut);
+ ifofile->pgci_ut = 0;
+ return 0;
+ }
+ ptr = data;
+ for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+ memcpy(&pgci_ut->lu[i], ptr, PGCI_LU_SIZE);
+ ptr += PGCI_LU_SIZE;
+ B2N_16(pgci_ut->lu[i].lang_code);
+ B2N_32(pgci_ut->lu[i].lang_start_byte);
+ }
+ free(data);
+
+ for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+ CHECK_ZERO(pgci_ut->lu[i].zero_1);
+ // Maybe this is only defined for v1.1 and later titles?
+ /* If the bits in 'lu[i].exists' are enumerated abcd efgh then:
+ VTS_x_yy.IFO VIDEO_TS.IFO
+ a == 0x83 "Root" 0x82 "Title"
+ b == 0x84 "Subpicture"
+ c == 0x85 "Audio"
+ d == 0x86 "Angle"
+ e == 0x87 "PTT"
+ */
+ assert((pgci_ut->lu[i].exists & 0x07) == 0);
+ }
+
+ for(i = 0; i < pgci_ut->nr_of_lus; i++) {
+ pgci_ut->lu[i].pgcit = malloc(sizeof(pgcit_t));
+ if(!pgci_ut->lu[i].pgcit) {
+ unsigned int j;
+ for(j = 0; j < i; j++) {
+ ifoFree_PGCIT_internal(pgci_ut->lu[j].pgcit);
+ free(pgci_ut->lu[j].pgcit);
+ }
+ free(pgci_ut->lu);
+ free(pgci_ut);
+ ifofile->pgci_ut = 0;
+ return 0;
+ }
+ if(!ifoRead_PGCIT_internal(ifofile, pgci_ut->lu[i].pgcit,
+ sector * DVD_BLOCK_LEN
+ + pgci_ut->lu[i].lang_start_byte)) {
+ unsigned int j;
+ for(j = 0; j < i; j++) {
+ ifoFree_PGCIT_internal(pgci_ut->lu[j].pgcit);
+ free(pgci_ut->lu[j].pgcit);
+ }
+ free(pgci_ut->lu[i].pgcit);
+ free(pgci_ut->lu);
+ free(pgci_ut);
+ ifofile->pgci_ut = 0;
+ return 0;
+ }
+ // FIXME: Iterate and verify that all menus that should exists accordingly
+ // to pgci_ut->lu[i].exists really do?
+ }
+
+ return 1;
+}
+
+
+void ifoFree_PGCI_UT(ifo_handle_t *ifofile) {
+ unsigned int i;
+
+ if(!ifofile)
+ return;
+
+ if(ifofile->pgci_ut) {
+ for(i = 0; i < ifofile->pgci_ut->nr_of_lus; i++) {
+ ifoFree_PGCIT_internal(ifofile->pgci_ut->lu[i].pgcit);
+ free(ifofile->pgci_ut->lu[i].pgcit);
+ }
+ free(ifofile->pgci_ut->lu);
+ free(ifofile->pgci_ut);
+ ifofile->pgci_ut = 0;
+ }
+}
+
+static int ifoRead_VTS_ATTRIBUTES(ifo_handle_t *ifofile,
+ vts_attributes_t *vts_attributes,
+ unsigned int offset) {
+ unsigned int i;
+
+ if(!DVDFileSeek_(ifofile->file, offset))
+ return 0;
+
+ if(!(DVDReadBytes(ifofile->file, vts_attributes, sizeof(vts_attributes_t))))
+ return 0;
+
+ B2N_32(vts_attributes->last_byte);
+ B2N_32(vts_attributes->vts_cat);
+ B2N_16(vts_attributes->vtsm_audio_attr.lang_code);
+ B2N_16(vts_attributes->vtsm_subp_attr.lang_code);
+ for(i = 0; i < 8; i++)
+ B2N_16(vts_attributes->vtstt_audio_attr[i].lang_code);
+ for(i = 0; i < 32; i++)
+ B2N_16(vts_attributes->vtstt_subp_attr[i].lang_code);
+
+ CHECK_ZERO(vts_attributes->zero_1);
+ CHECK_ZERO(vts_attributes->zero_2);
+ CHECK_ZERO(vts_attributes->zero_3);
+ CHECK_ZERO(vts_attributes->zero_4);
+ CHECK_ZERO(vts_attributes->zero_5);
+ CHECK_ZERO(vts_attributes->zero_6);
+ CHECK_ZERO(vts_attributes->zero_7);
+ assert(vts_attributes->nr_of_vtsm_audio_streams <= 1);
+ assert(vts_attributes->nr_of_vtsm_subp_streams <= 1);
+ assert(vts_attributes->nr_of_vtstt_audio_streams <= 8);
+ for(i = vts_attributes->nr_of_vtstt_audio_streams; i < 8; i++)
+ CHECK_ZERO(vts_attributes->vtstt_audio_attr[i]);
+ assert(vts_attributes->nr_of_vtstt_subp_streams <= 32);
+ {
+ unsigned int nr_coded;
+ assert(vts_attributes->last_byte + 1 >= VTS_ATTRIBUTES_MIN_SIZE);
+ nr_coded = (vts_attributes->last_byte + 1 - VTS_ATTRIBUTES_MIN_SIZE)/6;
+ // This is often nr_coded = 70, how do you know how many there really are?
+ if(nr_coded > 32) { // We haven't read more from disk/file anyway
+ nr_coded = 32;
+ }
+ assert(vts_attributes->nr_of_vtstt_subp_streams <= nr_coded);
+ for(i = vts_attributes->nr_of_vtstt_subp_streams; i < nr_coded; i++)
+ CHECK_ZERO(vts_attributes->vtstt_subp_attr[i]);
+ }
+
+ return 1;
+}
+
+
+
+int ifoRead_VTS_ATRT(ifo_handle_t *ifofile) {
+ vts_atrt_t *vts_atrt;
+ unsigned int i, info_length, sector;
+ uint32_t *data;
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vmgi_mat)
+ return 0;
+
+ if(ifofile->vmgi_mat->vts_atrt == 0) /* mandatory */
+ return 0;
+
+ sector = ifofile->vmgi_mat->vts_atrt;
+ if(!DVDFileSeek_(ifofile->file, sector * DVD_BLOCK_LEN))
+ return 0;
+
+ vts_atrt = (vts_atrt_t *)malloc(sizeof(vts_atrt_t));
+ if(!vts_atrt)
+ return 0;
+
+ ifofile->vts_atrt = vts_atrt;
+
+ if(!(DVDReadBytes(ifofile->file, vts_atrt, VTS_ATRT_SIZE))) {
+ free(vts_atrt);
+ ifofile->vts_atrt = 0;
+ return 0;
+ }
+
+ B2N_16(vts_atrt->nr_of_vtss);
+ B2N_32(vts_atrt->last_byte);
+
+ CHECK_ZERO(vts_atrt->zero_1);
+ assert(vts_atrt->nr_of_vtss != 0);
+ assert(vts_atrt->nr_of_vtss < 100); //??
+ assert((uint32_t)vts_atrt->nr_of_vtss * (4 + VTS_ATTRIBUTES_MIN_SIZE) +
+ VTS_ATRT_SIZE < vts_atrt->last_byte + 1);
+
+ info_length = vts_atrt->nr_of_vtss * sizeof(uint32_t);
+ data = (uint32_t *)malloc(info_length);
+ if(!data) {
+ free(vts_atrt);
+ ifofile->vts_atrt = 0;
+ return 0;
+ }
+ if(!(DVDReadBytes(ifofile->file, data, info_length))) {
+ free(data);
+ free(vts_atrt);
+ ifofile->vts_atrt = 0;
+ return 0;
+ }
+
+ for(i = 0; i < vts_atrt->nr_of_vtss; i++) {
+ B2N_32(data[i]);
+ assert(data[i] + VTS_ATTRIBUTES_MIN_SIZE < vts_atrt->last_byte + 1);
+ }
+
+ info_length = vts_atrt->nr_of_vtss * sizeof(vts_attributes_t);
+ vts_atrt->vts = (vts_attributes_t *)malloc(info_length);
+ if(!vts_atrt->vts) {
+ free(data);
+ free(vts_atrt);
+ ifofile->vts_atrt = 0;
+ return 0;
+ }
+ for(i = 0; i < vts_atrt->nr_of_vtss; i++) {
+ unsigned int offset = data[i];
+ if(!ifoRead_VTS_ATTRIBUTES(ifofile, &(vts_atrt->vts[i]),
+ (sector * DVD_BLOCK_LEN) + offset)) {
+ free(data);
+ free(vts_atrt);
+ ifofile->vts_atrt = 0;
+ return 0;
+ }
+
+ // This assert cant be in ifoRead_VTS_ATTRIBUTES
+ assert(offset + vts_atrt->vts[i].last_byte <= vts_atrt->last_byte + 1);
+ // Is this check correct?
+ }
+ free(data);
+
+ return 1;
+}
+
+
+void ifoFree_VTS_ATRT(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ if(ifofile->vts_atrt) {
+ free(ifofile->vts_atrt->vts);
+ free(ifofile->vts_atrt);
+ ifofile->vts_atrt = 0;
+ }
+}
+
+
+int ifoRead_TXTDT_MGI(ifo_handle_t *ifofile) {
+ txtdt_mgi_t *txtdt_mgi;
+
+ if(!ifofile)
+ return 0;
+
+ if(!ifofile->vmgi_mat)
+ return 0;
+
+ /* Return successfully if there is nothing to read. */
+ if(ifofile->vmgi_mat->txtdt_mgi == 0)
+ return 1;
+
+ if(!DVDFileSeek_(ifofile->file,
+ ifofile->vmgi_mat->txtdt_mgi * DVD_BLOCK_LEN))
+ return 0;
+
+ txtdt_mgi = (txtdt_mgi_t *)malloc(sizeof(txtdt_mgi_t));
+ if(!txtdt_mgi) {
+ return 0;
+ }
+ ifofile->txtdt_mgi = txtdt_mgi;
+
+ if(!(DVDReadBytes(ifofile->file, txtdt_mgi, TXTDT_MGI_SIZE))) {
+ fprintf(stderr, "libdvdread: Unable to read TXTDT_MGI.\n");
+ free(txtdt_mgi);
+ ifofile->txtdt_mgi = 0;
+ return 0;
+ }
+
+ // fprintf(stderr, "-- Not done yet --\n");
+ return 1;
+}
+
+void ifoFree_TXTDT_MGI(ifo_handle_t *ifofile) {
+ if(!ifofile)
+ return;
+
+ if(ifofile->txtdt_mgi) {
+ free(ifofile->txtdt_mgi);
+ ifofile->txtdt_mgi = 0;
+ }
+}
+
diff --git a/src/input/libdvdread/ifo_read.h b/src/input/libdvdread/ifo_read.h
new file mode 100644
index 000000000..dcf65a347
--- /dev/null
+++ b/src/input/libdvdread/ifo_read.h
@@ -0,0 +1,217 @@
+#ifndef IFO_READ_H_INCLUDED
+#define IFO_READ_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001, 2002 Björn Englund <d4bjorn@dtek.chalmers.se>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <dvdread/ifo_types.h>
+#include <dvdread/dvd_reader.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * handle = ifoOpen(dvd, title);
+ *
+ * Opens an IFO and reads in all the data for the IFO file corresponding to the
+ * given title. If title 0 is given, the video manager IFO file is read.
+ * Returns a handle to a completely parsed structure.
+ */
+ifo_handle_t *ifoOpen(dvd_reader_t *, int );
+
+/**
+ * handle = ifoOpenVMGI(dvd);
+ *
+ * Opens an IFO and reads in _only_ the vmgi_mat data. This call can be used
+ * together with the calls below to read in each segment of the IFO file on
+ * demand.
+ */
+ifo_handle_t *ifoOpenVMGI(dvd_reader_t *);
+
+/**
+ * handle = ifoOpenVTSI(dvd, title);
+ *
+ * Opens an IFO and reads in _only_ the vtsi_mat data. This call can be used
+ * together with the calls below to read in each segment of the IFO file on
+ * demand.
+ */
+ifo_handle_t *ifoOpenVTSI(dvd_reader_t *, int);
+
+/**
+ * ifoClose(ifofile);
+ * Cleans up the IFO information. This will free all data allocated for the
+ * substructures.
+ */
+void ifoClose(ifo_handle_t *);
+
+/**
+ * The following functions are for reading only part of the VMGI/VTSI files.
+ * Returns 1 if the data was successfully read and 0 on error.
+ */
+
+/**
+ * okay = ifoRead_PLT_MAIT(ifofile);
+ *
+ * Read in the Parental Management Information table, filling the
+ * ifofile->ptl_mait structure and its substructures. This data is only
+ * located in the video manager information file. This fills the
+ * ifofile->ptl_mait structure and all its substructures.
+ */
+int ifoRead_PTL_MAIT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_VTS_ATRT(ifofile);
+ *
+ * Read in the attribute table for the main menu vob, filling the
+ * ifofile->vts_atrt structure and its substructures. Only located in the
+ * video manager information file. This fills in the ifofile->vts_atrt
+ * structure and all its substructures.
+ */
+int ifoRead_VTS_ATRT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_TT_SRPT(ifofile);
+ *
+ * Reads the title info for the main menu, filling the ifofile->tt_srpt
+ * structure and its substructures. This data is only located in the video
+ * manager information file. This structure is mandatory in the IFO file.
+ */
+int ifoRead_TT_SRPT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_VTS_PTT_SRPT(ifofile);
+ *
+ * Reads in the part of title search pointer table, filling the
+ * ifofile->vts_ptt_srpt structure and its substructures. This data is only
+ * located in the video title set information file. This structure is
+ * mandatory, and must be included in the VTSI file.
+ */
+int ifoRead_VTS_PTT_SRPT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_FP_PGC(ifofile);
+ *
+ * Reads in the first play program chain data, filling the
+ * ifofile->first_play_pgc structure. This data is only located in the video
+ * manager information file. This structure is mandatory, and must be included
+ * in the VMGI file. **Possibly this is only optional.**
+ */
+int ifoRead_FP_PGC(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_PGCIT(ifofile);
+ *
+ * Reads in the program chain information table for the video title set. Fills
+ * in the ifofile->vts_pgcit structure and its substructures, which includes
+ * the data for each program chain in the set. This data is only located in
+ * the video title set information file. This structure is mandatory, and must
+ * be included in the VTSI file.
+ */
+int ifoRead_PGCIT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_PGCI_UT(ifofile);
+ *
+ * Reads in the menu PGCI unit table for the menu VOB. For the video manager,
+ * this corresponds to the VIDEO_TS.VOB file, and for each title set, this
+ * corresponds to the VTS_XX_0.VOB file. This data is located in both the
+ * video manager and video title set information files. For VMGI files, this
+ * fills the ifofile->vmgi_pgci_ut structure and all its substructures. For
+ * VTSI files, this fills the ifofile->vtsm_pgci_ut structure.
+ */
+int ifoRead_PGCI_UT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_C_ADT(ifofile);
+ *
+ * Reads in the cell address table for the menu VOB. For the video manager,
+ * this corresponds to the VIDEO_TS.VOB file, and for each title set, this
+ * corresponds to the VTS_XX_0.VOB file. This data is located in both the
+ * video manager and video title set information files. For VMGI files, this
+ * fills the ifofile->vmgm_c_adt structure and all its substructures. For VTSI
+ * files, this fills the ifofile->vtsm_c_adt structure.
+ */
+int ifoRead_C_ADT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_TITLE_C_ADT(ifofile);
+ *
+ * Reads in the cell address table for the video title set corresponding to
+ * this IFO file. This data is only located in the video title set information
+ * file. This structure is mandatory, and must be included in the VTSI file.
+ * This call fills the ifofile->vts_c_adt structure and its substructures.
+ */
+int ifoRead_TITLE_C_ADT(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_VOBU_ADMAP(ifofile);
+ *
+ * Reads in the VOBU address map for the menu VOB. For the video manager, this
+ * corresponds to the VIDEO_TS.VOB file, and for each title set, this
+ * corresponds to the VTS_XX_0.VOB file. This data is located in both the
+ * video manager and video title set information files. For VMGI files, this
+ * fills the ifofile->vmgm_vobu_admap structure and all its substructures. For
+ * VTSI files, this fills the ifofile->vtsm_vobu_admap structure.
+ */
+int ifoRead_VOBU_ADMAP(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_TITLE_VOBU_ADMAP(ifofile);
+ *
+ * Reads in the VOBU address map for the associated video title set. This data
+ * is only located in the video title set information file. This structure is
+ * mandatory, and must be included in the VTSI file. Fills the
+ * ifofile->vts_vobu_admap structure and its substructures.
+ */
+int ifoRead_TITLE_VOBU_ADMAP(ifo_handle_t *);
+
+/**
+ * okay = ifoRead_TXTDT_MGI(ifofile);
+ *
+ * Reads in the text data strings for the DVD. Fills the ifofile->txtdt_mgi
+ * structure and all its substructures. This data is only located in the video
+ * manager information file. This structure is mandatory, and must be included
+ * in the VMGI file.
+ */
+int ifoRead_TXTDT_MGI(ifo_handle_t *);
+
+/**
+ * The following functions are used for freeing parsed sections of the
+ * ifo_handle_t structure and the allocated substructures. The free calls
+ * below are safe: they will not mind if you attempt to free part of an IFO
+ * file which was not read in or which does not exist.
+ */
+void ifoFree_PTL_MAIT(ifo_handle_t *);
+void ifoFree_VTS_ATRT(ifo_handle_t *);
+void ifoFree_TT_SRPT(ifo_handle_t *);
+void ifoFree_VTS_PTT_SRPT(ifo_handle_t *);
+void ifoFree_FP_PGC(ifo_handle_t *);
+void ifoFree_PGCIT(ifo_handle_t *);
+void ifoFree_PGCI_UT(ifo_handle_t *);
+void ifoFree_C_ADT(ifo_handle_t *);
+void ifoFree_TITLE_C_ADT(ifo_handle_t *);
+void ifoFree_VOBU_ADMAP(ifo_handle_t *);
+void ifoFree_TITLE_VOBU_ADMAP(ifo_handle_t *);
+void ifoFree_TXTDT_MGI(ifo_handle_t *);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* IFO_READ_H_INCLUDED */
diff --git a/src/input/libdvdread/ifo_types.h b/src/input/libdvdread/ifo_types.h
new file mode 100644
index 000000000..1fd5c64b1
--- /dev/null
+++ b/src/input/libdvdread/ifo_types.h
@@ -0,0 +1,747 @@
+#ifndef IFO_TYPES_H_INCLUDED
+#define IFO_TYPES_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001 Björn Englund <d4bjorn@dtek.chalmers.se>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <inttypes.h>
+#include <dvdread/dvd_reader.h>
+
+
+#undef ATTRIBUTE_PACKED
+#undef PRAGMA_PACK_BEGIN
+#undef PRAGMA_PACK_END
+
+#if defined(__GNUC__)
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
+#define ATTRIBUTE_PACKED __attribute__ ((packed))
+#define PRAGMA_PACK 0
+#endif
+#endif
+
+#if !defined(ATTRIBUTE_PACKED)
+#define ATTRIBUTE_PACKED
+#define PRAGMA_PACK 1
+#endif
+
+#if PRAGMA_PACK
+#pragma pack(1)
+#endif
+
+
+/**
+ * Common
+ *
+ * The following structures are used in both the VMGI and VTSI.
+ */
+
+
+/**
+ * DVD Time Information.
+ */
+typedef struct {
+ uint8_t hour;
+ uint8_t minute;
+ uint8_t second;
+ uint8_t frame_u; // The two high bits are the frame rate.
+} ATTRIBUTE_PACKED dvd_time_t;
+
+/**
+ * Type to store per-command data.
+ */
+typedef struct {
+ uint8_t bytes[8];
+} ATTRIBUTE_PACKED vm_cmd_t;
+#define COMMAND_DATA_SIZE 8
+
+
+/**
+ * Video Attributes.
+ */
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+ unsigned int mpeg_version : 2;
+ unsigned int video_format : 2;
+ unsigned int display_aspect_ratio : 2;
+ unsigned int permitted_df : 2;
+
+ unsigned int line21_cc_1 : 1;
+ unsigned int line21_cc_2 : 1;
+ unsigned int unknown1 : 2;
+
+ unsigned int picture_size : 2;
+ unsigned int letterboxed : 1;
+ unsigned int film_mode : 1;
+#else
+ unsigned int permitted_df : 2;
+ unsigned int display_aspect_ratio : 2;
+ unsigned int video_format : 2;
+ unsigned int mpeg_version : 2;
+
+ unsigned int film_mode : 1;
+ unsigned int letterboxed : 1;
+ unsigned int picture_size : 2;
+
+ unsigned int unknown1 : 2;
+ unsigned int line21_cc_2 : 1;
+ unsigned int line21_cc_1 : 1;
+#endif
+} ATTRIBUTE_PACKED video_attr_t;
+
+/**
+ * Audio Attributes. (Incomplete/Wrong?)
+ */
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+ unsigned int audio_format : 3;
+ unsigned int multichannel_extension : 1;
+ unsigned int lang_type : 2;
+ unsigned int application_mode : 2;
+
+ unsigned int quantization : 2;
+ unsigned int sample_frequency : 2;
+ unsigned int unknown1 : 1;
+ unsigned int channels : 3;
+#else
+ unsigned int application_mode : 2;
+ unsigned int lang_type : 2;
+ unsigned int multichannel_extension : 1;
+ unsigned int audio_format : 3;
+
+ unsigned int channels : 3;
+ unsigned int unknown1 : 1;
+ unsigned int sample_frequency : 2;
+ unsigned int quantization : 2;
+#endif
+ uint16_t lang_code;
+ uint8_t lang_code2; // ??
+ uint8_t lang_extension;
+ uint16_t unknown2;
+} ATTRIBUTE_PACKED audio_attr_t;
+
+/**
+ * Subpicture Attributes.(Incomplete/Wrong)
+ */
+typedef struct {
+ /*
+ * type: 0 not specified
+ * 1 language
+ * 2 other
+ * coding mode: 0 run length
+ * 1 extended
+ * 2 other
+ * language: indicates language if type == 1
+ * lang extension: if type == 1 contains the lang extension
+ */
+ uint8_t type;
+ uint8_t zero1;
+ uint16_t lang_code;
+ uint8_t lang_extension;
+ uint8_t zero2;
+} ATTRIBUTE_PACKED subp_attr_t;
+
+
+
+/**
+ * PGC Command Table.
+ */
+typedef struct {
+ uint16_t nr_of_pre;
+ uint16_t nr_of_post;
+ uint16_t nr_of_cell;
+ uint16_t zero_1;
+ vm_cmd_t *pre_cmds;
+ vm_cmd_t *post_cmds;
+ vm_cmd_t *cell_cmds;
+} ATTRIBUTE_PACKED pgc_command_tbl_t;
+#define PGC_COMMAND_TBL_SIZE 8
+
+/**
+ * PGC Program Map
+ */
+typedef uint8_t pgc_program_map_t;
+
+/**
+ * Cell Playback Information.
+ */
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+ unsigned int block_mode : 2;
+ unsigned int block_type : 2;
+ unsigned int seamless_play : 1;
+ unsigned int interleaved : 1;
+ unsigned int stc_discontinuity: 1;
+ unsigned int seamless_angle : 1;
+
+ unsigned int unknown1 : 1;
+ unsigned int restricted : 1;
+ unsigned int unknown2 : 6;
+#else
+ unsigned int seamless_angle : 1;
+ unsigned int stc_discontinuity: 1;
+ unsigned int interleaved : 1;
+ unsigned int seamless_play : 1;
+ unsigned int block_type : 2;
+ unsigned int block_mode : 2;
+
+ unsigned int unknown2 : 6;
+ unsigned int restricted : 1;
+ unsigned int unknown1 : 1;
+#endif
+ uint8_t still_time;
+ uint8_t cell_cmd_nr;
+ dvd_time_t playback_time;
+ uint32_t first_sector;
+ uint32_t first_ilvu_end_sector;
+ uint32_t last_vobu_start_sector;
+ uint32_t last_sector;
+} ATTRIBUTE_PACKED cell_playback_t;
+
+#define BLOCK_TYPE_NONE 0x0
+#define BLOCK_TYPE_ANGLE_BLOCK 0x1
+
+#define BLOCK_MODE_NOT_IN_BLOCK 0x0
+#define BLOCK_MODE_FIRST_CELL 0x1
+#define BLOCK_MODE_IN_BLOCK 0x2
+#define BLOCK_MODE_LAST_CELL 0x3
+
+/**
+ * Cell Position Information.
+ */
+typedef struct {
+ uint16_t vob_id_nr;
+ uint8_t zero_1;
+ uint8_t cell_nr;
+} ATTRIBUTE_PACKED cell_position_t;
+
+/**
+ * User Operations.
+ */
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+ unsigned int zero : 7; // 25-31
+ unsigned int video_pres_mode_change : 1; // 24
+
+ unsigned int karaoke_audio_pres_mode_change : 1; // 23
+ unsigned int angle_change : 1; // 22
+ unsigned int subpic_stream_change : 1; // 21
+ unsigned int audio_stream_change : 1; // 20
+ unsigned int pause_on : 1; // 19
+ unsigned int still_off : 1; // 18
+ unsigned int button_select_or_activate : 1; // 17
+ unsigned int resume : 1; // 16
+
+ unsigned int chapter_menu_call : 1; // 15
+ unsigned int angle_menu_call : 1; // 14
+ unsigned int audio_menu_call : 1; // 13
+ unsigned int subpic_menu_call : 1; // 12
+ unsigned int root_menu_call : 1; // 11
+ unsigned int title_menu_call : 1; // 10
+ unsigned int backward_scan : 1; // 9
+ unsigned int forward_scan : 1; // 8
+
+ unsigned int next_pg_search : 1; // 7
+ unsigned int prev_or_top_pg_search : 1; // 6
+ unsigned int time_or_chapter_search : 1; // 5
+ unsigned int go_up : 1; // 4
+ unsigned int stop : 1; // 3
+ unsigned int title_play : 1; // 2
+ unsigned int chapter_search_or_play : 1; // 1
+ unsigned int title_or_time_play : 1; // 0
+#else
+ unsigned int video_pres_mode_change : 1; // 24
+ unsigned int zero : 7; // 25-31
+
+ unsigned int resume : 1; // 16
+ unsigned int button_select_or_activate : 1; // 17
+ unsigned int still_off : 1; // 18
+ unsigned int pause_on : 1; // 19
+ unsigned int audio_stream_change : 1; // 20
+ unsigned int subpic_stream_change : 1; // 21
+ unsigned int angle_change : 1; // 22
+ unsigned int karaoke_audio_pres_mode_change : 1; // 23
+
+ unsigned int forward_scan : 1; // 8
+ unsigned int backward_scan : 1; // 9
+ unsigned int title_menu_call : 1; // 10
+ unsigned int root_menu_call : 1; // 11
+ unsigned int subpic_menu_call : 1; // 12
+ unsigned int audio_menu_call : 1; // 13
+ unsigned int angle_menu_call : 1; // 14
+ unsigned int chapter_menu_call : 1; // 15
+
+ unsigned int title_or_time_play : 1; // 0
+ unsigned int chapter_search_or_play : 1; // 1
+ unsigned int title_play : 1; // 2
+ unsigned int stop : 1; // 3
+ unsigned int go_up : 1; // 4
+ unsigned int time_or_chapter_search : 1; // 5
+ unsigned int prev_or_top_pg_search : 1; // 6
+ unsigned int next_pg_search : 1; // 7
+#endif
+} ATTRIBUTE_PACKED user_ops_t;
+
+/**
+ * Program Chain Information.
+ */
+typedef struct {
+ uint16_t zero_1;
+ uint8_t nr_of_programs;
+ uint8_t nr_of_cells;
+ dvd_time_t playback_time;
+ user_ops_t prohibited_ops;
+ uint16_t audio_control[8]; /* New type? */
+ uint32_t subp_control[32]; /* New type? */
+ uint16_t next_pgc_nr;
+ uint16_t prev_pgc_nr;
+ uint16_t goup_pgc_nr;
+ uint8_t still_time;
+ uint8_t pg_playback_mode;
+ uint32_t palette[16]; /* New type struct {zero_1, Y, Cr, Cb} ? */
+ uint16_t command_tbl_offset;
+ uint16_t program_map_offset;
+ uint16_t cell_playback_offset;
+ uint16_t cell_position_offset;
+ pgc_command_tbl_t *command_tbl;
+ pgc_program_map_t *program_map;
+ cell_playback_t *cell_playback;
+ cell_position_t *cell_position;
+} ATTRIBUTE_PACKED pgc_t;
+#define PGC_SIZE 236
+
+/**
+ * Program Chain Information Search Pointer.
+ */
+typedef struct {
+ uint8_t entry_id;
+#ifdef WORDS_BIGENDIAN
+ unsigned int block_mode : 2;
+ unsigned int block_type : 2;
+ unsigned int unknown1 : 4;
+#else
+ unsigned int unknown1 : 4;
+ unsigned int block_type : 2;
+ unsigned int block_mode : 2;
+#endif
+ uint16_t ptl_id_mask;
+ uint32_t pgc_start_byte;
+ pgc_t *pgc;
+} ATTRIBUTE_PACKED pgci_srp_t;
+#define PGCI_SRP_SIZE 8
+
+/**
+ * Program Chain Information Table.
+ */
+typedef struct {
+ uint16_t nr_of_pgci_srp;
+ uint16_t zero_1;
+ uint32_t last_byte;
+ pgci_srp_t *pgci_srp;
+} ATTRIBUTE_PACKED pgcit_t;
+#define PGCIT_SIZE 8
+
+/**
+ * Menu PGCI Language Unit.
+ */
+typedef struct {
+ uint16_t lang_code;
+ uint8_t zero_1;
+ uint8_t exists;
+ uint32_t lang_start_byte;
+ pgcit_t *pgcit;
+} ATTRIBUTE_PACKED pgci_lu_t;
+#define PGCI_LU_SIZE 8
+
+/**
+ * Menu PGCI Unit Table.
+ */
+typedef struct {
+ uint16_t nr_of_lus;
+ uint16_t zero_1;
+ uint32_t last_byte;
+ pgci_lu_t *lu;
+} ATTRIBUTE_PACKED pgci_ut_t;
+#define PGCI_UT_SIZE 8
+
+/**
+ * Cell Address Information.
+ */
+typedef struct {
+ uint16_t vob_id;
+ uint8_t cell_id;
+ uint8_t zero_1;
+ uint32_t start_sector;
+ uint32_t last_sector;
+} ATTRIBUTE_PACKED cell_adr_t;
+
+/**
+ * Cell Address Table.
+ */
+typedef struct {
+ uint16_t nr_of_vobs; /* VOBs */
+ uint16_t zero_1;
+ uint32_t last_byte;
+ cell_adr_t *cell_adr_table;
+} ATTRIBUTE_PACKED c_adt_t;
+#define C_ADT_SIZE 8
+
+/**
+ * VOBU Address Map.
+ */
+typedef struct {
+ uint32_t last_byte;
+ uint32_t *vobu_start_sectors;
+} ATTRIBUTE_PACKED vobu_admap_t;
+#define VOBU_ADMAP_SIZE 4
+
+
+
+
+/**
+ * VMGI
+ *
+ * The following structures relate to the Video Manager.
+ */
+
+/**
+ * Video Manager Information Management Table.
+ */
+typedef struct {
+ char vmg_identifier[12];
+ uint32_t vmg_last_sector;
+ uint8_t zero_1[12];
+ uint32_t vmgi_last_sector;
+ uint8_t zero_2;
+ uint8_t specification_version;
+ uint32_t vmg_category;
+ uint16_t vmg_nr_of_volumes;
+ uint16_t vmg_this_volume_nr;
+ uint8_t disc_side;
+ uint8_t zero_3[19];
+ uint16_t vmg_nr_of_title_sets; /* Number of VTSs. */
+ char provider_identifier[32];
+ uint64_t vmg_pos_code;
+ uint8_t zero_4[24];
+ uint32_t vmgi_last_byte;
+ uint32_t first_play_pgc;
+ uint8_t zero_5[56];
+ uint32_t vmgm_vobs; /* sector */
+ uint32_t tt_srpt; /* sector */
+ uint32_t vmgm_pgci_ut; /* sector */
+ uint32_t ptl_mait; /* sector */
+ uint32_t vts_atrt; /* sector */
+ uint32_t txtdt_mgi; /* sector */
+ uint32_t vmgm_c_adt; /* sector */
+ uint32_t vmgm_vobu_admap; /* sector */
+ uint8_t zero_6[32];
+
+ video_attr_t vmgm_video_attr;
+ uint8_t zero_7;
+ uint8_t nr_of_vmgm_audio_streams; // should be 0 or 1
+ audio_attr_t vmgm_audio_attr;
+ audio_attr_t zero_8[7];
+ uint8_t zero_9[17];
+ uint8_t nr_of_vmgm_subp_streams; // should be 0 or 1
+ subp_attr_t vmgm_subp_attr;
+ subp_attr_t zero_10[27]; /* XXX: how much 'padding' here? */
+} ATTRIBUTE_PACKED vmgi_mat_t;
+
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+ unsigned int zero_1 : 1;
+ unsigned int multi_or_random_pgc_title : 1; // 0 == one sequential pgc title
+ unsigned int jlc_exists_in_cell_cmd : 1;
+ unsigned int jlc_exists_in_prepost_cmd : 1;
+ unsigned int jlc_exists_in_button_cmd : 1;
+ unsigned int jlc_exists_in_tt_dom : 1;
+ unsigned int chapter_search_or_play : 1; // UOP 1
+ unsigned int title_or_time_play : 1; // UOP 0
+#else
+ unsigned int title_or_time_play : 1; // UOP 0
+ unsigned int chapter_search_or_play : 1; // UOP 1
+ unsigned int jlc_exists_in_tt_dom : 1;
+ unsigned int jlc_exists_in_button_cmd : 1;
+ unsigned int jlc_exists_in_prepost_cmd : 1;
+ unsigned int jlc_exists_in_cell_cmd : 1;
+ unsigned int multi_or_random_pgc_title : 1; // 0 == one sequential pgc title
+ unsigned int zero_1 : 1;
+#endif
+} ATTRIBUTE_PACKED playback_type_t;
+
+/**
+ * Title Information.
+ */
+typedef struct {
+ playback_type_t pb_ty;
+ uint8_t nr_of_angles;
+ uint16_t nr_of_ptts;
+ uint16_t parental_id;
+ uint8_t title_set_nr;
+ uint8_t vts_ttn;
+ uint32_t title_set_sector;
+} ATTRIBUTE_PACKED title_info_t;
+
+/**
+ * PartOfTitle Search Pointer Table.
+ */
+typedef struct {
+ uint16_t nr_of_srpts;
+ uint16_t zero_1;
+ uint32_t last_byte;
+ title_info_t *title;
+} ATTRIBUTE_PACKED tt_srpt_t;
+#define TT_SRPT_SIZE 8
+
+/**
+ * Parental Management Information Unit Table.
+ */
+typedef struct {
+ uint16_t country_code;
+ uint16_t zero_1;
+ uint16_t pf_ptl_mai_start_byte;
+ uint16_t zero_2;
+ /* uint16_t *pf_ptl_mai // table of nr_of_vtss+1 x 8 */
+} ATTRIBUTE_PACKED ptl_mait_country_t;
+#define PTL_MAIT_COUNTRY_SIZE 8
+
+/**
+ * Parental Management Information Table.
+ */
+typedef struct {
+ uint16_t nr_of_countries;
+ uint16_t nr_of_vtss;
+ uint32_t last_byte;
+ ptl_mait_country_t *countries;
+} ATTRIBUTE_PACKED ptl_mait_t;
+#define PTL_MAIT_SIZE 8
+
+/**
+ * Video Title Set Attributes.
+ */
+typedef struct {
+ uint32_t last_byte;
+ uint32_t vts_cat;
+
+ video_attr_t vtsm_vobs_attr;
+ uint8_t zero_1;
+ uint8_t nr_of_vtsm_audio_streams; // should be 0 or 1
+ audio_attr_t vtsm_audio_attr;
+ audio_attr_t zero_2[7];
+ uint8_t zero_3[16];
+ uint8_t zero_4;
+ uint8_t nr_of_vtsm_subp_streams; // should be 0 or 1
+ subp_attr_t vtsm_subp_attr;
+ subp_attr_t zero_5[27];
+
+ uint8_t zero_6[2];
+
+ video_attr_t vtstt_vobs_video_attr;
+ uint8_t zero_7;
+ uint8_t nr_of_vtstt_audio_streams;
+ audio_attr_t vtstt_audio_attr[8];
+ uint8_t zero_8[16];
+ uint8_t zero_9;
+ uint8_t nr_of_vtstt_subp_streams;
+ subp_attr_t vtstt_subp_attr[32];
+} ATTRIBUTE_PACKED vts_attributes_t;
+#define VTS_ATTRIBUTES_SIZE 542
+#define VTS_ATTRIBUTES_MIN_SIZE 356
+
+/**
+ * Video Title Set Attribute Table.
+ */
+typedef struct {
+ uint16_t nr_of_vtss;
+ uint16_t zero_1;
+ uint32_t last_byte;
+ vts_attributes_t *vts;
+} ATTRIBUTE_PACKED vts_atrt_t;
+#define VTS_ATRT_SIZE 8
+
+/**
+ * Text Data. (Incomplete)
+ */
+typedef struct {
+ uint32_t last_byte; /* offsets are relative here */
+ uint16_t offsets[100]; /* == nr_of_srpts + 1 (first is disc title) */
+#if 0
+ uint16_t unknown; // 0x48 ?? 0x48 words (16bit) info following
+ uint16_t zero_1;
+
+ uint8_t type_of_info;//?? 01 == disc, 02 == Title, 04 == Title part
+ uint8_t unknown1;
+ uint8_t unknown2;
+ uint8_t unknown3;
+ uint8_t unknown4;//?? allways 0x30 language?, text format?
+ uint8_t unknown5;
+ uint16_t offset; // from first
+
+ char text[12]; // ended by 0x09
+#endif
+} ATTRIBUTE_PACKED txtdt_t;
+
+/**
+ * Text Data Language Unit. (Incomplete)
+ */
+typedef struct {
+ uint16_t lang_code;
+ uint16_t unknown; /* 0x0001, title 1? disc 1? side 1? */
+ uint32_t txtdt_start_byte; /* prt, rel start of vmg_txtdt_mgi */
+ txtdt_t *txtdt;
+} ATTRIBUTE_PACKED txtdt_lu_t;
+#define TXTDT_LU_SIZE 8
+
+/**
+ * Text Data Manager Information. (Incomplete)
+ */
+typedef struct {
+ char disc_name[14]; /* how many bytes?? */
+ uint16_t nr_of_language_units; /* 32bit?? */
+ uint32_t last_byte;
+ txtdt_lu_t *lu;
+} ATTRIBUTE_PACKED txtdt_mgi_t;
+#define TXTDT_MGI_SIZE 20
+
+
+/**
+ * VTS
+ *
+ * Structures relating to the Video Title Set (VTS).
+ */
+
+/**
+ * Video Title Set Information Management Table.
+ */
+typedef struct {
+ char vts_identifier[12];
+ uint32_t vts_last_sector;
+ uint8_t zero_1[12];
+ uint32_t vtsi_last_sector;
+ uint8_t zero_2;
+ uint8_t specification_version;
+ uint32_t vts_category;
+ uint16_t zero_3;
+ uint16_t zero_4;
+ uint8_t zero_5;
+ uint8_t zero_6[19];
+ uint16_t zero_7;
+ uint8_t zero_8[32];
+ uint64_t zero_9;
+ uint8_t zero_10[24];
+ uint32_t vtsi_last_byte;
+ uint32_t zero_11;
+ uint8_t zero_12[56];
+ uint32_t vtsm_vobs; /* sector */
+ uint32_t vtstt_vobs; /* sector */
+ uint32_t vts_ptt_srpt; /* sector */
+ uint32_t vts_pgcit; /* sector */
+ uint32_t vtsm_pgci_ut; /* sector */
+ uint32_t vts_tmapt; /* sector */ // XXX: FIXME TODO Implement
+ uint32_t vtsm_c_adt; /* sector */
+ uint32_t vtsm_vobu_admap; /* sector */
+ uint32_t vts_c_adt; /* sector */
+ uint32_t vts_vobu_admap; /* sector */
+ uint8_t zero_13[24];
+
+ video_attr_t vtsm_video_attr;
+ uint8_t zero_14;
+ uint8_t nr_of_vtsm_audio_streams; // should be 0 or 1
+ audio_attr_t vtsm_audio_attr;
+ audio_attr_t zero_15[7];
+ uint8_t zero_16[17];
+ uint8_t nr_of_vtsm_subp_streams; // should be 0 or 1
+ subp_attr_t vtsm_subp_attr;
+ subp_attr_t zero_17[27];
+ uint8_t zero_18[2];
+
+ video_attr_t vts_video_attr;
+ uint8_t zero_19;
+ uint8_t nr_of_vts_audio_streams;
+ audio_attr_t vts_audio_attr[8];
+ uint8_t zero_20[17];
+ uint8_t nr_of_vts_subp_streams;
+ subp_attr_t vts_subp_attr[32];
+ /* XXX: how much 'padding' here, if any? */
+} ATTRIBUTE_PACKED vtsi_mat_t;
+
+/**
+ * PartOfTitle Unit Information.
+ */
+typedef struct {
+ uint16_t pgcn;
+ uint16_t pgn;
+} ATTRIBUTE_PACKED ptt_info_t;
+
+/**
+ * PartOfTitle Information.
+ */
+typedef struct {
+ uint16_t nr_of_ptts;
+ ptt_info_t *ptt;
+} ATTRIBUTE_PACKED ttu_t;
+
+/**
+ * PartOfTitle Search Pointer Table.
+ */
+typedef struct {
+ uint16_t nr_of_srpts;
+ uint16_t zero_1;
+ uint32_t last_byte;
+ ttu_t *title;
+} ATTRIBUTE_PACKED vts_ptt_srpt_t;
+#define VTS_PTT_SRPT_SIZE 8
+
+
+#if PRAGMA_PACK
+#pragma pack()
+#endif
+
+
+/**
+ * The following structure defines an IFO file. The structure is divided into
+ * two parts, the VMGI, or Video Manager Information, which is read from the
+ * VIDEO_TS.[IFO,BUP] file, and the VTSI, or Video Title Set Information, which
+ * is read in from the VTS_XX_0.[IFO,BUP] files.
+ */
+typedef struct {
+ dvd_file_t *file;
+
+ /* VMGI */
+ vmgi_mat_t *vmgi_mat;
+ tt_srpt_t *tt_srpt;
+ pgc_t *first_play_pgc;
+ ptl_mait_t *ptl_mait;
+ vts_atrt_t *vts_atrt;
+ txtdt_mgi_t *txtdt_mgi;
+
+ /* Common */
+ pgci_ut_t *pgci_ut;
+ c_adt_t *menu_c_adt;
+ vobu_admap_t *menu_vobu_admap;
+
+ /* VTSI */
+ vtsi_mat_t *vtsi_mat;
+ vts_ptt_srpt_t *vts_ptt_srpt;
+ pgcit_t *vts_pgcit;
+ int *vts_tmapt; // FIXME add/correct the type
+ c_adt_t *vts_c_adt;
+ vobu_admap_t *vts_vobu_admap;
+} ifo_handle_t;
+
+#endif /* IFO_TYPES_H_INCLUDED */
diff --git a/src/input/libdvdread/nav_print.c b/src/input/libdvdread/nav_print.c
new file mode 100644
index 000000000..3c9630cc1
--- /dev/null
+++ b/src/input/libdvdread/nav_print.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2000, 2001, 2002 Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * Much of the contents in this file is based on VOBDUMP.
+ *
+ * VOBDUMP: a program for examining DVD .VOB filse
+ *
+ * Copyright 1998, 1999 Eric Smith <eric@brouhaha.com>
+ *
+ * VOBDUMP is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. Note that I am not
+ * granting permission to redistribute or modify VOBDUMP under the
+ * terms of any later version of the General Public License.
+ *
+ * This program is distributed in the hope that it will be useful (or
+ * at least amusing), but WITHOUT ANY WARRANTY; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "config.h" // Needed for WORDS_BIGENDIAN
+#include "nav_types.h"
+#include "nav_print.h"
+
+
+static void print_time(dvd_time_t *dtime) {
+ const char *rate;
+ assert((dtime->hour>>4) < 0xa && (dtime->hour&0xf) < 0xa);
+ assert((dtime->minute>>4) < 0x7 && (dtime->minute&0xf) < 0xa);
+ assert((dtime->second>>4) < 0x7 && (dtime->second&0xf) < 0xa);
+ assert((dtime->frame_u&0xf) < 0xa);
+
+ printf("%02x:%02x:%02x.%02x",
+ dtime->hour,
+ dtime->minute,
+ dtime->second,
+ dtime->frame_u & 0x3f);
+ switch((dtime->frame_u & 0xc0) >> 6) {
+ case 1:
+ rate = "25.00";
+ break;
+ case 3:
+ rate = "29.97";
+ break;
+ default:
+ rate = "(please send a bug report)";
+ break;
+ }
+ printf(" @ %s fps", rate);
+}
+
+
+static void navPrint_PCI_GI(pci_gi_t *pci_gi) {
+ int i;
+
+ printf("pci_gi:\n");
+ printf("nv_pck_lbn 0x%08x\n", pci_gi->nv_pck_lbn);
+ printf("vobu_cat 0x%04x\n", pci_gi->vobu_cat);
+ printf("vobu_uop_ctl 0x%08x\n", *(uint32_t*)&pci_gi->vobu_uop_ctl);
+ printf("vobu_s_ptm 0x%08x\n", pci_gi->vobu_s_ptm);
+ printf("vobu_e_ptm 0x%08x\n", pci_gi->vobu_e_ptm);
+ printf("vobu_se_e_ptm 0x%08x\n", pci_gi->vobu_se_e_ptm);
+ printf("e_eltm ");
+ print_time(&pci_gi->e_eltm);
+ printf("\n");
+
+ printf("vobu_isrc \"");
+ for(i = 0; i < 32; i++) {
+ char c = pci_gi->vobu_isrc[i];
+ if((c >= ' ') && (c <= '~'))
+ printf("%c", c);
+ else
+ printf(".");
+ }
+ printf("\"\n");
+}
+
+static void navPrint_NSML_AGLI(nsml_agli_t *nsml_agli) {
+ int i, j = 0;
+
+ for(i = 0; i < 9; i++)
+ j |= nsml_agli->nsml_agl_dsta[i];
+ if(j == 0)
+ return;
+
+ printf("nsml_agli:\n");
+ for(i = 0; i < 9; i++)
+ if(nsml_agli->nsml_agl_dsta[i])
+ printf("nsml_agl_c%d_dsta 0x%08x\n", i + 1,
+ nsml_agli->nsml_agl_dsta[i]);
+}
+
+static void navPrint_HL_GI(hl_gi_t *hl_gi, int *btngr_ns, int *btn_ns) {
+
+ if((hl_gi->hli_ss & 0x03) == 0)
+ return;
+
+ printf("hl_gi:\n");
+ printf("hli_ss 0x%01x\n", hl_gi->hli_ss & 0x03);
+ printf("hli_s_ptm 0x%08x\n", hl_gi->hli_s_ptm);
+ printf("hli_e_ptm 0x%08x\n", hl_gi->hli_e_ptm);
+ printf("btn_se_e_ptm 0x%08x\n", hl_gi->btn_se_e_ptm);
+
+ *btngr_ns = hl_gi->btngr_ns;
+ printf("btngr_ns %d\n", hl_gi->btngr_ns);
+ printf("btngr%d_dsp_ty 0x%02x\n", 1, hl_gi->btngr1_dsp_ty);
+ printf("btngr%d_dsp_ty 0x%02x\n", 2, hl_gi->btngr2_dsp_ty);
+ printf("btngr%d_dsp_ty 0x%02x\n", 3, hl_gi->btngr3_dsp_ty);
+
+ printf("btn_ofn %d\n", hl_gi->btn_ofn);
+ *btn_ns = hl_gi->btn_ns;
+ printf("btn_ns %d\n", hl_gi->btn_ns);
+ printf("nsl_btn_ns %d\n", hl_gi->nsl_btn_ns);
+ printf("fosl_btnn %d\n", hl_gi->fosl_btnn);
+ printf("foac_btnn %d\n", hl_gi->foac_btnn);
+}
+
+static void navPrint_BTN_COLIT(btn_colit_t *btn_colit) {
+ int i, j;
+
+ j = 0;
+ for(i = 0; i < 6; i++)
+ j |= btn_colit->btn_coli[i/2][i&1];
+ if(j == 0)
+ return;
+
+ printf("btn_colit:\n");
+ for(i = 0; i < 3; i++)
+ for(j = 0; j < 2; j++)
+ printf("btn_cqoli %d %s_coli: %08x\n",
+ i, (j == 0) ? "sl" : "ac",
+ btn_colit->btn_coli[i][j]);
+}
+
+static void navPrint_BTNIT(btni_t *btni_table, int btngr_ns, int btn_ns) {
+ int i, j;
+
+ printf("btnit:\n");
+ printf("btngr_ns: %i\n", btngr_ns);
+ printf("btn_ns: %i\n", btn_ns);
+
+ if(btngr_ns == 0)
+ return;
+
+ for(i = 0; i < btngr_ns; i++) {
+ for(j = 0; j < (36 / btngr_ns); j++) {
+ if(j < btn_ns) {
+ btni_t *btni = &btni_table[(36 / btngr_ns) * i + j];
+
+ printf("group %d btni %d: ", i+1, j+1);
+ printf("btn_coln %d, auto_action_mode %d\n",
+ btni->btn_coln, btni->auto_action_mode);
+ printf("coords (%d, %d) .. (%d, %d)\n",
+ btni->x_start, btni->y_start, btni->x_end, btni->y_end);
+
+ printf("up %d, ", btni->up);
+ printf("down %d, ", btni->down);
+ printf("left %d, ", btni->left);
+ printf("right %d\n", btni->right);
+
+ // ifoPrint_COMMAND(&btni->cmd);
+ printf("\n");
+ }
+ }
+ }
+}
+
+static void navPrint_HLI(hli_t *hli) {
+ int btngr_ns = 0, btn_ns = 0;
+
+ printf("hli:\n");
+ navPrint_HL_GI(&hli->hl_gi, & btngr_ns, & btn_ns);
+ navPrint_BTN_COLIT(&hli->btn_colit);
+ navPrint_BTNIT(hli->btnit, btngr_ns, btn_ns);
+}
+
+void navPrint_PCI(pci_t *pci) {
+ printf("pci packet:\n");
+ navPrint_PCI_GI(&pci->pci_gi);
+ navPrint_NSML_AGLI(&pci->nsml_agli);
+ navPrint_HLI(&pci->hli);
+}
+
+static void navPrint_DSI_GI(dsi_gi_t *dsi_gi) {
+ printf("dsi_gi:\n");
+ printf("nv_pck_scr 0x%08x\n", dsi_gi->nv_pck_scr);
+ printf("nv_pck_lbn 0x%08x\n", dsi_gi->nv_pck_lbn );
+ printf("vobu_ea 0x%08x\n", dsi_gi->vobu_ea);
+ printf("vobu_1stref_ea 0x%08x\n", dsi_gi->vobu_1stref_ea);
+ printf("vobu_2ndref_ea 0x%08x\n", dsi_gi->vobu_2ndref_ea);
+ printf("vobu_3rdref_ea 0x%08x\n", dsi_gi->vobu_3rdref_ea);
+ printf("vobu_vob_idn 0x%04x\n", dsi_gi->vobu_vob_idn);
+ printf("vobu_c_idn 0x%02x\n", dsi_gi->vobu_c_idn);
+ printf("c_eltm ");
+ print_time(&dsi_gi->c_eltm);
+ printf("\n");
+}
+
+static void navPrint_SML_PBI(sml_pbi_t *sml_pbi) {
+ printf("sml_pbi:\n");
+ printf("category 0x%04x\n", sml_pbi->category);
+ if(sml_pbi->category & 0x8000)
+ printf("VOBU is in preunit\n");
+ if(sml_pbi->category & 0x4000)
+ printf("VOBU is in ILVU\n");
+ if(sml_pbi->category & 0x2000)
+ printf("VOBU at the beginning of ILVU\n");
+ if(sml_pbi->category & 0x1000)
+ printf("VOBU at end of PREU of ILVU\n");
+
+ printf("ilvu_ea 0x%08x\n", sml_pbi->ilvu_ea);
+ printf("nxt_ilvu_sa 0x%08x\n", sml_pbi->ilvu_sa);
+ printf("nxt_ilvu_size 0x%04x\n", sml_pbi->size);
+
+ printf("vob_v_s_s_ptm 0x%08x\n", sml_pbi->vob_v_s_s_ptm);
+ printf("vob_v_e_e_ptm 0x%08x\n", sml_pbi->vob_v_e_e_ptm);
+
+ /* $$$ more code needed here */
+}
+
+static void navPrint_SML_AGLI(sml_agli_t *sml_agli) {
+ int i;
+ printf("sml_agli:\n");
+ for(i = 0; i < 9; i++) {
+ printf("agl_c%d address: 0x%08x size 0x%04x\n", i,
+ sml_agli->data[i].address, sml_agli->data[i].size);
+ }
+}
+
+static void navPrint_VOBU_SRI(vobu_sri_t *vobu_sri) {
+ int i;
+ int stime[19] = { 240, 120, 60, 20, 15, 14, 13, 12, 11,
+ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
+ printf("vobu_sri:\n");
+ printf("Next VOBU with Video %08x\n", vobu_sri->next_video);
+ for(i = 0; i < 19; i++) {
+ printf("%3.1f %08x ", stime[i]/2.0, vobu_sri->fwda[i]);
+ }
+ printf("\n");
+ printf("Next VOBU %08x\n", vobu_sri->next_vobu);
+ printf("--\n");
+ printf("Prev VOBU %08x\n", vobu_sri->prev_vobu);
+ for(i = 0; i < 19; i++) {
+ printf("%3.1f %08x ", stime[18 - i]/2.0, vobu_sri->bwda[i]);
+ }
+ printf("\n");
+ printf("Prev VOBU with Video %08x\n", vobu_sri->prev_video);
+}
+
+static void navPrint_SYNCI(synci_t *synci) {
+ int i;
+
+ printf("synci:\n");
+ /* $$$ more code needed here */
+ for(i = 0; i < 8; i++)
+ printf("%04x ", synci->a_synca[i]);
+ for(i = 0; i < 32; i++)
+ printf("%08x ", synci->sp_synca[i]);
+}
+
+void navPrint_DSI(dsi_t *dsi) {
+ printf("dsi packet:\n");
+ navPrint_DSI_GI(&dsi->dsi_gi);
+ navPrint_SML_PBI(&dsi->sml_pbi);
+ navPrint_SML_AGLI(&dsi->sml_agli);
+ navPrint_VOBU_SRI(&dsi->vobu_sri);
+ navPrint_SYNCI(&dsi->synci);
+}
+
+
diff --git a/src/input/libdvdread/nav_print.h b/src/input/libdvdread/nav_print.h
new file mode 100644
index 000000000..28aefc7e5
--- /dev/null
+++ b/src/input/libdvdread/nav_print.h
@@ -0,0 +1,42 @@
+#ifndef NAV_PRINT_H_INCLUDED
+#define NAV_PRINT_H_INCLUDED
+
+/*
+ * Copyright (C) 2001 Billy Biggs <vektor@dumbterm.net>,
+ * Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <dvdread/nav_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Prints information contained in the PCI to stdout.
+ */
+void navPrint_PCI(pci_t *);
+
+/**
+ * Prints information contained in the DSI to stdout.
+ */
+void navPrint_DSI(dsi_t *);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* NAV_PRINT_H_INCLUDED */
diff --git a/src/input/libdvdread/nav_read.c b/src/input/libdvdread/nav_read.c
new file mode 100644
index 000000000..ff0fd164c
--- /dev/null
+++ b/src/input/libdvdread/nav_read.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2000, 2001, 2002 Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "config.h" // Needed for WORDS_BIGENDIAN
+#include "bswap.h"
+#include "nav_types.h"
+#include "nav_read.h"
+
+void navRead_PCI(pci_t *pci, unsigned char *buffer) {
+ int i, j, k;
+
+ 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]);
+
+#if !defined(WORDS_BIGENDIAN)
+ /* pci hli btni */
+ for(i = 0; i < 36; i++) {
+ char tmp[6], swap;
+ memcpy(tmp, &(pci->hli.btnit[i]), 6);
+ /* This is a B2N_24() */
+ swap = tmp[0]; tmp[0] = tmp[2]; tmp[2] = swap;
+ /* This is a B2N_24() */
+ swap = tmp[3]; tmp[3] = tmp[5]; tmp[5] = swap;
+ memcpy(&(pci->hli.btnit[i]), tmp, 6);
+ }
+#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 */
+ 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 {
+ 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?
+ }
+ }
+ }
+}
+
+void navRead_DSI(dsi_t *dsi, unsigned char *buffer) {
+ int i;
+
+ assert(sizeof(dsi_t) == DSI_BYTES - 1); // -1 for substream id
+
+ memcpy(dsi, buffer, sizeof(dsi_t));
+
+ /* Endian conversions */
+
+ /* dsi dsi gi */
+ B2N_32(dsi->dsi_gi.nv_pck_scr);
+ B2N_32(dsi->dsi_gi.nv_pck_lbn);
+ B2N_32(dsi->dsi_gi.vobu_ea);
+ B2N_32(dsi->dsi_gi.vobu_1stref_ea);
+ B2N_32(dsi->dsi_gi.vobu_2ndref_ea);
+ B2N_32(dsi->dsi_gi.vobu_3rdref_ea);
+ B2N_16(dsi->dsi_gi.vobu_vob_idn);
+
+ /* dsi sml pbi */
+ B2N_16(dsi->sml_pbi.category);
+ B2N_32(dsi->sml_pbi.ilvu_ea);
+ B2N_32(dsi->sml_pbi.ilvu_sa);
+ B2N_16(dsi->sml_pbi.size);
+ B2N_32(dsi->sml_pbi.vob_v_s_s_ptm);
+ B2N_32(dsi->sml_pbi.vob_v_e_e_ptm);
+
+ /* dsi sml agli */
+ for(i = 0; i < 9; i++) {
+ B2N_32(dsi->sml_agli.data[ i ].address);
+ B2N_16(dsi->sml_agli.data[ i ].size);
+ }
+
+ /* dsi vobu sri */
+ B2N_32(dsi->vobu_sri.next_video);
+ for(i = 0; i < 19; i++)
+ B2N_32(dsi->vobu_sri.fwda[i]);
+ B2N_32(dsi->vobu_sri.next_vobu);
+ B2N_32(dsi->vobu_sri.prev_vobu);
+ for(i = 0; i < 19; i++)
+ B2N_32(dsi->vobu_sri.bwda[i]);
+ B2N_32(dsi->vobu_sri.prev_video);
+
+ /* dsi synci */
+ for(i = 0; i < 8; i++)
+ B2N_16(dsi->synci.a_synca[i]);
+ for(i = 0; i < 32; i++)
+ B2N_32(dsi->synci.sp_synca[i]);
+
+
+ /* Asserts */
+
+ /* dsi dsi gi */
+ assert(dsi->dsi_gi.zero1 == 0);
+}
+
diff --git a/src/input/libdvdread/nav_read.h b/src/input/libdvdread/nav_read.h
new file mode 100644
index 000000000..61a11ea55
--- /dev/null
+++ b/src/input/libdvdread/nav_read.h
@@ -0,0 +1,41 @@
+#ifndef NAV_READ_H_INCLUDED
+#define NAV_READ_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001 Håkan Hjort <d95hjort@dtek.chalmers.se>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <dvdread/nav_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Reads the PCI packet data pointed to into pci struct.
+ */
+void navRead_PCI(pci_t *, unsigned char *);
+
+/**
+ * Reads the DSI packet data pointed to into dsi struct.
+ */
+void navRead_DSI(dsi_t *, unsigned char *);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* NAV_READ_H_INCLUDED */
diff --git a/src/input/libdvdread/nav_types.h b/src/input/libdvdread/nav_types.h
new file mode 100644
index 000000000..bb74e29ed
--- /dev/null
+++ b/src/input/libdvdread/nav_types.h
@@ -0,0 +1,288 @@
+#ifndef NAV_TYPES_H_INCLUDED
+#define NAV_TYPES_H_INCLUDED
+
+/*
+ * Copyright (C) 2000, 2001 Håkan Hjort <d95hjort@dtek.chalmers.se>
+ *
+ * The data structures in this file should represent the layout of the
+ * pci and dsi packets as they are stored in the stream. Information
+ * found by reading the source to VOBDUMP is the base for the structure
+ * and names of these data types.
+ *
+ * VOBDUMP: a program for examining DVD .VOB files.
+ * Copyright 1998, 1999 Eric Smith <eric@brouhaha.com>
+ *
+ * VOBDUMP is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. Note that I am not
+ * granting permission to redistribute or modify VOBDUMP under the terms
+ * of any later version of the General Public License.
+ *
+ * This program is distributed in the hope that it will be useful (or at
+ * least amusing), but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <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;
+
+/**
+ * Button Information
+ */
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+ unsigned int btn_coln : 2;
+ unsigned int x_start : 10;
+ unsigned int zero1 : 2;
+ unsigned int x_end : 10;
+ unsigned int auto_action_mode : 2;
+ unsigned int y_start : 10;
+ unsigned int zero2 : 2;
+ unsigned int y_end : 10;
+
+ unsigned int zero3 : 2;
+ unsigned int up : 6;
+ unsigned int zero4 : 2;
+ unsigned int down : 6;
+ unsigned int zero5 : 2;
+ unsigned int left : 6;
+ unsigned int zero6 : 2;
+ unsigned int right : 6;
+#else
+ unsigned int x_end : 10;
+ unsigned int zero1 : 2;
+ unsigned int x_start : 10;
+ unsigned int btn_coln : 2;
+ unsigned int y_end : 10;
+ unsigned int zero2 : 2;
+ unsigned int y_start : 10;
+ unsigned int auto_action_mode : 2;
+
+ unsigned int up : 6;
+ unsigned int zero3 : 2;
+ unsigned int down : 6;
+ unsigned int zero4 : 2;
+ unsigned int left : 6;
+ unsigned int zero5 : 2;
+ unsigned int right : 6;
+ unsigned int zero6 : 2;
+#endif
+ vm_cmd_t cmd;
+} ATTRIBUTE_PACKED btni_t;
+
+/**
+ * Highlight Information
+ */
+typedef struct {
+ hl_gi_t hl_gi;
+ btn_colit_t btn_colit;
+ btni_t btnit[36];
+} ATTRIBUTE_PACKED hli_t;
+
+/**
+ * PCI packet
+ */
+typedef struct {
+ pci_gi_t pci_gi;
+ nsml_agli_t nsml_agli;
+ hli_t hli;
+ uint8_t zero1[189];
+} ATTRIBUTE_PACKED pci_t;
+
+
+
+
+/**
+ * DSI General Information
+ */
+typedef struct {
+ uint32_t nv_pck_scr;
+ uint32_t nv_pck_lbn;
+ uint32_t vobu_ea;
+ uint32_t vobu_1stref_ea;
+ uint32_t vobu_2ndref_ea;
+ uint32_t vobu_3rdref_ea;
+ uint16_t vobu_vob_idn;
+ uint8_t zero1;
+ uint8_t vobu_c_idn;
+ dvd_time_t c_eltm;
+} ATTRIBUTE_PACKED dsi_gi_t;
+
+/**
+ * Seamless Playback Information
+ */
+typedef struct {
+ uint16_t category; ///< category of seamless VOBU
+ uint32_t ilvu_ea; ///< end address of interleaved Unit (sectors)
+ uint32_t ilvu_sa; ///< start address of next interleaved unit (sectors)
+ uint16_t size; ///< size of next interleaved unit (sectors)
+ uint32_t vob_v_s_s_ptm; ///< video start ptm in vob
+ uint32_t vob_v_e_e_ptm; ///< video end ptm in vob
+ struct {
+ uint32_t stp_ptm1;
+ uint32_t stp_ptm2;
+ uint32_t gap_len1;
+ uint32_t gap_len2;
+ } vob_a[8];
+} ATTRIBUTE_PACKED sml_pbi_t;
+
+/**
+ * Seamless Angle Infromation for one angle
+ */
+typedef struct {
+ uint32_t address; ///< Sector offset to next ILVU, high bit is before/after
+ uint16_t size; ///< Byte size of the ILVU poited to by address.
+} ATTRIBUTE_PACKED sml_agl_data_t;
+
+/**
+ * Seamless Angle Infromation
+ */
+typedef struct {
+ sml_agl_data_t data[9];
+} ATTRIBUTE_PACKED sml_agli_t;
+
+/**
+ * VOBU Search Information
+ */
+typedef struct {
+ uint32_t next_video; ///< Next vobu that contains video
+ uint32_t fwda[19]; ///< Forwards, time
+ uint32_t next_vobu;
+ uint32_t prev_vobu;
+ uint32_t bwda[19]; ///< Backwards, time
+ uint32_t prev_video;
+} ATTRIBUTE_PACKED vobu_sri_t;
+
+#define SRI_END_OF_CELL 0x3fffffff
+
+/**
+ * Synchronous Information
+ */
+typedef struct {
+ uint16_t a_synca[8]; ///< Sector offset to first audio packet for this VOBU
+ uint32_t sp_synca[32]; ///< Sector offset to first subpicture packet
+} ATTRIBUTE_PACKED synci_t;
+
+/**
+ * DSI packet
+ */
+typedef struct {
+ dsi_gi_t dsi_gi;
+ sml_pbi_t sml_pbi;
+ sml_agli_t sml_agli;
+ vobu_sri_t vobu_sri;
+ synci_t synci;
+ uint8_t zero1[471];
+} ATTRIBUTE_PACKED dsi_t;
+
+
+#if PRAGMA_PACK
+#pragma pack()
+#endif
+
+#endif /* NAV_TYPES_H_INCLUDED */
diff --git a/src/input/read_cache.c b/src/input/read_cache.c
deleted file mode 100644
index fb9b27a13..000000000
--- a/src/input/read_cache.c
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2000-2001 the xine project
- *
- * This file is part of xine, a unix video player.
- *
- * xine 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.
- *
- * xine 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
- *
- * $Id: read_cache.c,v 1.8 2001/11/19 22:31:35 miguelfreitas Exp $
- */
-
-#include <sys/types.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#include "xineutils.h"
-
-#include "read_cache.h"
-
-#define NUM_BUFFERS 512
-#define NUM_MACRO_BUFFERS 32
-
-typedef struct macro_buf_s macro_buf_t;
-
-struct macro_buf_s {
-
- macro_buf_t *next;
-
- int ref;
- off_t adr;
-
- uint8_t *data;
- int size_valid; /* amount of valid data bytes in 'data' */
- read_cache_t *source;
-};
-
-struct read_cache_s {
-
- int fd;
-
- macro_buf_t *mbuf_pool_top;
- char *multi_base;
- buf_element_t *buf_pool_top;
-
- macro_buf_t *cur_mbuf;
-
- pthread_mutex_t lock;
- pthread_cond_t buf_pool_not_empty;
- pthread_cond_t mbuf_pool_not_empty;
-};
-
-/*
- * helper function to release buffer pool lock
- * in case demux thread is cancelled
- */
-
-static void cache_release_lock (void *arg) {
-
- pthread_mutex_t *mutex = (pthread_mutex_t *) arg;
-
- /* printf ("pool release lock\n"); */
-
- pthread_mutex_unlock (mutex);
-
-}
-
-static void buf_free (buf_element_t *buf) {
-
- macro_buf_t *mbuf = (macro_buf_t *) buf->source;
- read_cache_t *this = mbuf->source;
-
- pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
-
- pthread_cleanup_push( cache_release_lock, &this->lock);
-
- pthread_mutex_lock (&this->lock);
-
- /* free buffer */
-
- buf->next = this->buf_pool_top;
- this->buf_pool_top = buf;
-
- pthread_cond_signal (&this->buf_pool_not_empty);
-
- /* maybe free mbuf too */
-
- mbuf->ref--;
-
- if (!mbuf->ref && (mbuf != this->cur_mbuf)) {
-
- mbuf->next = this->mbuf_pool_top;
- this->mbuf_pool_top = mbuf;
-
- pthread_cond_signal (&this->mbuf_pool_not_empty);
- }
-
- pthread_cleanup_pop (0);
- pthread_mutex_unlock (&this->lock);
- /* needed because cancellation points defined by POSIX
- (eg. 'read') would leak allocated buffers */
- pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
-}
-
-read_cache_t *read_cache_new () {
-
- read_cache_t *this;
- int i;
- char *multi_buffer = NULL;
- int buf_size;
-
- this = (read_cache_t *) xine_xmalloc (sizeof (read_cache_t));
-
- pthread_mutex_init (&this->lock, NULL);
- pthread_cond_init (&this->buf_pool_not_empty, NULL);
- pthread_cond_init (&this->mbuf_pool_not_empty, NULL);
-
- /* init buffer pool */
-
- this->buf_pool_top = NULL;
- for (i = 0; i<NUM_BUFFERS; i++) {
- buf_element_t *buf;
-
- buf = xine_xmalloc (sizeof (buf_element_t));
-
- buf->max_size = 2048;
- buf->free_buffer = buf_free;
-
- buf->next = this->buf_pool_top;
- this->buf_pool_top = buf;
- }
-
- /* init macro buffer pool */
-
- buf_size = NUM_MACRO_BUFFERS * 2048 * 16;
- buf_size += 2048; /* alignment space */
- this->multi_base = xine_xmalloc (buf_size);
- multi_buffer = this->multi_base;
- while ((int) multi_buffer % 2048)
- multi_buffer++;
-
- this->mbuf_pool_top = NULL;
- for (i = 0; i<NUM_MACRO_BUFFERS; i++) {
- macro_buf_t *mbuf;
-
- mbuf = xine_xmalloc (sizeof (macro_buf_t));
-
- mbuf->data = (uint8_t *)multi_buffer;
- multi_buffer += 2048*16;
- mbuf->source = this;
-
- mbuf->next = this->mbuf_pool_top;
- this->mbuf_pool_top = mbuf;
- }
-
- return this;
-}
-
-void read_cache_set_fd (read_cache_t *this, int fd) {
- this->fd = fd;
-}
-
-
-buf_element_t *read_cache_read_block (read_cache_t *this,
- off_t pos) {
-
- macro_buf_t *mbuf;
- buf_element_t *buf;
- off_t madr;
- int badr;
- int bytes_read;
-
- pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
-
- pthread_cleanup_push( cache_release_lock, &this->lock);
-
- pthread_mutex_lock (&this->lock);
-
- /* address calculations */
-
- madr = pos & (~ (off_t) 0x7FFF);
- badr = pos & ((off_t) 0x7FFF);
-
- /* find or create macroblock that contains this block */
-
- if ( !this->cur_mbuf || (this->cur_mbuf->adr != madr) ||
- (this->cur_mbuf->size_valid <= badr) ) {
-
- if (this->cur_mbuf && (!this->cur_mbuf->ref)) {
-
- mbuf = this->cur_mbuf;
-
- } else {
-
- this->cur_mbuf = NULL;
-
- while (this->mbuf_pool_top==NULL) {
- pthread_cond_wait (&this->mbuf_pool_not_empty, &this->lock);
- }
-
- mbuf = this->mbuf_pool_top;
-
- this->mbuf_pool_top = this->mbuf_pool_top->next;
- }
-
- mbuf->adr = madr;
- mbuf->ref = 0;
- mbuf->size_valid = 0;
-
- this->cur_mbuf = mbuf;
-
- if (lseek (this->fd, madr, SEEK_SET) < 0) {
- fprintf(stderr, "read_cache: can't seek to offset %lld (%s)\n",
- (long long)madr, strerror (errno));
- } else {
- pthread_testcancel();
- if ((bytes_read = read (this->fd, mbuf->data, 2048*16)) != 2048*16) {
- if (bytes_read < 0) /* reading encrypted dvd without authentication? */
- fprintf(stderr, "read_cache: read error (%s)\n", strerror (errno));
- else
- fprintf(stderr, "read_cache: short read (%d != %d)\n", bytes_read, 2048*16);
- }
- mbuf->size_valid = bytes_read;
- }
-
- } else {
- mbuf = this->cur_mbuf;
- }
-
- /* check for read errors */
- if ( badr > mbuf->size_valid ) {
-
- buf = NULL;
-
- } else {
-
- /* create buf */
-
- while (this->buf_pool_top==NULL) {
- pthread_cond_wait (&this->buf_pool_not_empty, &this->lock);
- }
-
- buf = this->buf_pool_top;
-
- this->buf_pool_top = this->buf_pool_top->next;
-
- buf->mem = mbuf->data + badr;
- buf->content = buf->mem;
- buf->source = mbuf;
-
- mbuf->ref++;
- }
-
- pthread_cleanup_pop (0);
- pthread_mutex_unlock (&this->lock);
- pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
-
- return buf;
-}
-
-void read_cache_free (read_cache_t *this) {
-
- buf_element_t *buf;
- macro_buf_t *mbuf;
-
- buf = this->buf_pool_top;
- while (buf) {
- buf_element_t *next = buf->next;
-
- free(buf);
- buf = next;
- }
-
- mbuf = this->mbuf_pool_top;
- while (mbuf) {
- macro_buf_t *mnext = mbuf->next;
-
- free (mbuf);
- mbuf = mnext;
- }
-
- free (this->multi_base);
-
- free (this);
-}
-