diff options
author | Rich J Wareham <richwareham@users.sourceforge.net> | 2002-08-08 17:49:20 +0000 |
---|---|---|
committer | Rich J Wareham <richwareham@users.sourceforge.net> | 2002-08-08 17:49:20 +0000 |
commit | 892715d418f93897aa941bf8aaee69e74628099f (patch) | |
tree | b0954b9c584b9a79a83fcb186d40bb947e5af430 | |
parent | 2f9f3c0ec0e38ca2c6cbfea9d6a4f8fe5fde78b9 (diff) | |
download | xine-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
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, ¤t, &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, ¤t, &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(¤t_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(¤t_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, ¤t, &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); -} - |