summaryrefslogtreecommitdiffstats
path: root/libdvdnav
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-02-17 00:32:19 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-02-17 00:32:19 +0000
commit0d382a262c0638d0f572fc37193ccc5ed3dc895f (patch)
tree8578dcddfce4191f3f7a142a37769df7add48475 /libdvdnav
downloadk9copy-0d382a262c0638d0f572fc37193ccc5ed3dc895f.tar.gz
k9copy-0d382a262c0638d0f572fc37193ccc5ed3dc895f.zip
Added old abandoned version of k9copy
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/k9copy@1091546 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'libdvdnav')
-rw-r--r--libdvdnav/Makefile.am14
-rw-r--r--libdvdnav/common.h16
-rw-r--r--libdvdnav/compat.h58
-rw-r--r--libdvdnav/decoder.c777
-rw-r--r--libdvdnav/decoder.h118
-rw-r--r--libdvdnav/dvd_types.h281
-rw-r--r--libdvdnav/dvdnav.c1093
-rw-r--r--libdvdnav/dvdnav.h630
-rw-r--r--libdvdnav/dvdnav_events.h241
-rw-r--r--libdvdnav/dvdnav_internal.h199
-rw-r--r--libdvdnav/dvdread.c74
-rw-r--r--libdvdnav/dvdread.h28
-rw-r--r--libdvdnav/highlight.c512
-rw-r--r--libdvdnav/navigation.c231
-rw-r--r--libdvdnav/read_cache.c347
-rw-r--r--libdvdnav/read_cache.h51
-rw-r--r--libdvdnav/remap.c257
-rw-r--r--libdvdnav/remap.h33
-rw-r--r--libdvdnav/searching.c567
-rw-r--r--libdvdnav/settings.c121
-rw-r--r--libdvdnav/vm.c1861
-rw-r--r--libdvdnav/vm.h181
-rw-r--r--libdvdnav/vmcmd.c539
-rw-r--r--libdvdnav/vmcmd.h33
24 files changed, 8262 insertions, 0 deletions
diff --git a/libdvdnav/Makefile.am b/libdvdnav/Makefile.am
new file mode 100644
index 0000000..13baa2d
--- /dev/null
+++ b/libdvdnav/Makefile.am
@@ -0,0 +1,14 @@
+
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libk9dvdnav.la
+libk9dvdnav_la_SOURCES = decoder.c decoder.h dvdnav.c dvdnav_events.h dvdnav.h \
+ dvdnav_internal.h highlight.c navigation.c read_cache.c read_cache.h remap.c remap.h \
+ searching.c settings.c vm.c vmcmd.c vmcmd.h vm.h compat.h dvd_types.h
+
+
+noinst_HEADERS = common.h dvdread.h
+
+
+
+INCLUDES = -I$(top_srcdir)/dvdread
diff --git a/libdvdnav/common.h b/libdvdnav/common.h
new file mode 100644
index 0000000..c54924e
--- /dev/null
+++ b/libdvdnav/common.h
@@ -0,0 +1,16 @@
+#include "config.h"
+#ifndef __STDC_LIMIT_MACROS
+ #define __STDC_LIMIT_MACROS
+#endif
+
+#if defined(HAVE_INTTYPES_H)
+ #include <inttypes.h>
+#elif defined( HAVE_STDINT_H)
+ #include <stdint.h>
+#endif
+
+#include "ifo_types.h"
+#include "dvd_reader.h"
+#include "ifo_read.h"
+#include "nav_read.h"
+#include "nav_types.h"
diff --git a/libdvdnav/compat.h b/libdvdnav/compat.h
new file mode 100644
index 0000000..08b2957
--- /dev/null
+++ b/libdvdnav/compat.h
@@ -0,0 +1,58 @@
+/*
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * $Id: compat.h,v 1.3 2003/04/20 21:13:26 guenter Exp $
+ *
+ */
+
+#ifndef XINE_COMPAT_H
+#define XINE_COMPAT_H
+
+#include <limits.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined _MSC_VER
+#define __XINE_FUNCTION__ __FILE__
+#elif defined __GNUC__
+#define __XINE_FUNCTION__ __FUNCTION__
+#else
+#define __XINE_FUNCTION__ __func__
+#endif
+
+#ifndef NAME_MAX
+#define XINE_NAME_MAX 256
+#else
+#define XINE_NAME_MAX NAME_MAX
+#endif
+
+#ifndef PATH_MAX
+#define XINE_PATH_MAX 768
+#else
+#define XINE_PATH_MAX PATH_MAX
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libdvdnav/decoder.c b/libdvdnav/decoder.c
new file mode 100644
index 0000000..1c21f60
--- /dev/null
+++ b/libdvdnav/decoder.c
@@ -0,0 +1,777 @@
+/*
+ * Copyright (C) 2000, 2001 Martin Norb�k, H�an Hjort
+ * 2002-2004 the dvdnav project
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * $Id: decoder.c,v 1.14 2004/03/16 11:43:38 mroi Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "common.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h> /* For memset */
+
+#include "dvdnav_internal.h"
+
+uint32_t vm_getbits(command_t *command, int32_t start, int32_t count) {
+ uint64_t result = 0;
+ uint64_t bit_mask = 0;
+ uint64_t examining = 0;
+ int32_t bits;
+
+ if (count == 0) return 0;
+
+ if ( ((start - count) < -1) ||
+ (count > 32) ||
+ (start > 63) ||
+ (count < 0) ||
+ (start < 0) ) {
+ fprintf(MSG_OUT, "libdvdnav: Bad call to vm_getbits. Parameter out of range\n");
+ abort();
+ }
+ /* all ones, please */
+ bit_mask = ~bit_mask;
+ bit_mask >>= 63 - start;
+ bits = start + 1 - count;
+ 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(MSG_OUT, "libdvdnav: Getting counter %d\n",reg);*/
+ gettimeofday(&current_time, NULL);
+ time_offset.tv_sec = current_time.tv_sec - registers->GPRM_time[reg].tv_sec;
+ time_offset.tv_usec = current_time.tv_usec - registers->GPRM_time[reg].tv_usec;
+ if (time_offset.tv_usec < 0) {
+ time_offset.tv_sec--;
+ time_offset.tv_usec += 1000000;
+ }
+ result = (uint16_t) (time_offset.tv_sec & 0xffff);
+ registers->GPRM[reg]=result;
+ return result;
+
+ } else {
+ /* Register mode */
+ return registers->GPRM[reg];
+ }
+
+}
+
+static void set_GPRM(registers_t* registers, uint8_t reg, uint16_t value) {
+ if (registers->GPRM_mode[reg] & 0x01) {
+ struct timeval current_time;
+ /* Counter mode */
+ /* fprintf(MSG_OUT, "libdvdnav: Setting counter %d\n",reg); */
+ gettimeofday(&current_time, NULL);
+ registers->GPRM_time[reg] = current_time;
+ registers->GPRM_time[reg].tv_sec -= value;
+ }
+ registers->GPRM[reg] = value;
+}
+
+/* Eval register code, can either be system or general register.
+ SXXX_XXXX, where S is 1 if it is system register. */
+static uint16_t eval_reg(command_t* command, uint8_t reg) {
+ if(reg & 0x80) {
+ if ((reg & 0x1f) == 20) {
+ fprintf(MSG_OUT, "libdvdnav: Suspected RCE Region Protection!!!\n");
+ }
+ 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 start) {
+ if(imm) { /* immediate */
+ return vm_getbits(command, start, 16);
+ } else {
+ return eval_reg(command, vm_getbits(command, (start - 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 */
+static uint16_t eval_reg_or_data_2(command_t* command,
+ int32_t imm, int32_t start) {
+ if(imm) /* immediate */
+ return vm_getbits(command, (start - 1), 7);
+ else
+ return get_GPRM(command->registers, (vm_getbits(command, (start - 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(MSG_OUT, "libdvdnav: 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, 54, 3);
+ if(op) {
+ return eval_compare(op, eval_reg(command, vm_getbits(command, 39, 8)),
+ eval_reg_or_data(command, vm_getbits(command, 55, 1), 31));
+ }
+ 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, 54, 3);
+ if(op) {
+ return eval_compare(op, eval_reg(command, vm_getbits(command, 15, 8)),
+ eval_reg(command, vm_getbits(command, 7, 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, 54, 3);
+ if(op) {
+ return eval_compare(op, eval_reg(command, vm_getbits(command, 47, 8)),
+ eval_reg_or_data(command, vm_getbits(command, 55, 1), 15));
+ }
+ 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, 54, 3);
+ if(op) {
+ return eval_compare(op, eval_reg(command, vm_getbits(command, 51, 4)),
+ eval_reg_or_data(command, vm_getbits(command, 55, 1), 31));
+ }
+ 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, 51, 4)) {
+ case 0: /* NOP */
+ line = 0;
+ return cond ? line : 0;
+ case 1: /* Goto line */
+ line = vm_getbits(command, 7, 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, 7, 8);
+ level = vm_getbits(command, 11, 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, 15, 6);
+ uint8_t linkop = vm_getbits(command, 4, 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, 51, 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, 14, 15);
+ return cond;
+ case 5:
+ return_values->command = LinkPTTN;
+ return_values->data1 = vm_getbits(command, 9, 10);
+ return_values->data2 = vm_getbits(command, 15, 6);
+ return cond;
+ case 6:
+ return_values->command = LinkPGN;
+ return_values->data1 = vm_getbits(command, 6, 7);
+ return_values->data2 = vm_getbits(command, 15, 6);
+ return cond;
+ case 7:
+ return_values->command = LinkCN;
+ return_values->data1 = vm_getbits(command, 7, 8);
+ return_values->data2 = vm_getbits(command, 15, 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, 51, 4)) {
+ case 1:
+ return_values->command = Exit;
+ return cond;
+ case 2:
+ return_values->command = JumpTT;
+ return_values->data1 = vm_getbits(command, 22, 7);
+ return cond;
+ case 3:
+ return_values->command = JumpVTS_TT;
+ return_values->data1 = vm_getbits(command, 22, 7);
+ return cond;
+ case 5:
+ return_values->command = JumpVTS_PTT;
+ return_values->data1 = vm_getbits(command, 22, 7);
+ return_values->data2 = vm_getbits(command, 41, 10);
+ return cond;
+ case 6:
+ switch(vm_getbits(command, 23, 2)) {
+ case 0:
+ return_values->command = JumpSS_FP;
+ return cond;
+ case 1:
+ return_values->command = JumpSS_VMGM_MENU;
+ return_values->data1 = vm_getbits(command, 19, 4);
+ return cond;
+ case 2:
+ return_values->command = JumpSS_VTSM;
+ return_values->data1 = vm_getbits(command, 31, 8);
+ return_values->data2 = vm_getbits(command, 39, 8);
+ return_values->data3 = vm_getbits(command, 19, 4);
+ return cond;
+ case 3:
+ return_values->command = JumpSS_VMGM_PGC;
+ return_values->data1 = vm_getbits(command, 46, 15);
+ return cond;
+ }
+ break;
+ case 8:
+ switch(vm_getbits(command, 23, 2)) {
+ case 0:
+ return_values->command = CallSS_FP;
+ return_values->data1 = vm_getbits(command, 31, 8);
+ return cond;
+ case 1:
+ return_values->command = CallSS_VMGM_MENU;
+ return_values->data1 = vm_getbits(command, 19, 4);
+ return_values->data2 = vm_getbits(command, 31, 8);
+ return cond;
+ case 2:
+ return_values->command = CallSS_VTSM;
+ return_values->data1 = vm_getbits(command, 19, 4);
+ return_values->data2 = vm_getbits(command, 31, 8);
+ return cond;
+ case 3:
+ return_values->command = CallSS_VMGM_PGC;
+ return_values->data1 = vm_getbits(command, 46, 15);
+ return_values->data2 = vm_getbits(command, 31, 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, 59, 4)) {
+ case 1: /* Set system reg 1 &| 2 &| 3 (Audio, Subp. Angle) */
+ for(i = 1; i <= 3; i++) {
+ if(vm_getbits(command, 63 - ((2 + i)*8), 1)) {
+ data = eval_reg_or_data_2(command, vm_getbits(command, 60, 1), (47 - (i*8)));
+ 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, 60, 1), 47);
+ data2 = vm_getbits(command, 23, 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, 60, 1), 47);
+ data2 = vm_getbits(command, 19, 4);
+ if(vm_getbits(command, 23, 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, 60, 1), 31); /* Not system reg!! */
+ if(cond) {
+ command->registers->SPRM[8] = data;
+ }
+ break;
+ }
+ if(vm_getbits(command, 51, 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, 0xffff); /* Avoid that divide by zero! */
+ }
+ break;
+ case 7:
+ if (data != 0) {
+ set_GPRM(command->registers, reg, (get_GPRM(command->registers, reg) % data) );
+ } else {
+ set_GPRM(command->registers, reg, 0xffff); /* Avoid that divide by zero! */
+ }
+ break;
+ case 8: /* SPECIAL CASE - RND! Return numbers between 1 and data. */
+ set_GPRM(command->registers, reg, 1 + ((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, 59, 4);
+ uint8_t reg = vm_getbits(command, 35, 4); /* FIXME: This is different from vmcmd.c!!! */
+ uint8_t reg2 = vm_getbits(command, 19, 4);
+ uint16_t data = eval_reg_or_data(command, vm_getbits(command, 60, 1), 31);
+
+ 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, 59, 4);
+ uint8_t reg = vm_getbits(command, 51, 4);
+ uint8_t reg2 = vm_getbits(command, 35, 4); /* FIXME: This is different from vmcmd.c!!! */
+ uint16_t data = eval_reg_or_data(command, vm_getbits(command, 60, 1), 47);
+
+ 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, 63, 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(MSG_OUT, "libdvdnav: Unknown Instruction!\n");
+ abort();
+ }
+ break;
+ case 1: /* Link/jump instructions */
+ if(vm_getbits(&command, 60, 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, 51, 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) */
+ /* FIXME: These are wrong. Need to be updated from vmcmd.c */
+ 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 */
+ /* FIXME: These are wrong. Need to be updated from vmcmd.c */
+ 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(MSG_OUT, "libdvdnav: WARNING: Unknown Command=%x\n", vm_getbits(&command, 63, 3));
+ abort();
+ }
+ /* Check if there are bits not yet examined */
+
+ if(command.instruction & ~ command.examined) {
+ fprintf(MSG_OUT, "libdvdnav: decoder.c: [WARNING, unknown bits:");
+ fprintf(MSG_OUT, " %08llx", (command.instruction & ~ command.examined) );
+ fprintf(MSG_OUT, "]\n");
+ }
+
+ 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(MSG_OUT, "libdvdnav: Registers before transaction\n");
+ vm_print_registers( registers );
+ fprintf(MSG_OUT, "libdvdnav: Full list of commands to execute\n");
+ for(i = 0; i < num_commands; i++)
+ vm_print_cmd(i, &commands[i]);
+ fprintf(MSG_OUT, "libdvdnav: --------------------------------------------\n");
+ fprintf(MSG_OUT, "libdvdnav: Single stepping commands\n");
+#endif
+
+ i = 0;
+ while(i < num_commands && total < 100000) {
+ int32_t line;
+
+#ifdef TRACE
+ vm_print_cmd(i, &commands[i]);
+#endif
+
+ line = eval_command(&commands[i].bytes[0], registers, return_values);
+
+ if (line < 0) { /* Link command */
+#ifdef TRACE
+ fprintf(MSG_OUT, "libdvdnav: Registers after transaction\n");
+ vm_print_registers( registers );
+ fprintf(MSG_OUT, "libdvdnav: 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(MSG_OUT, "libdvdnav: Registers after transaction\n");
+ vm_print_registers( registers );
+#endif
+ return 0;
+}
+
+#ifdef TRACE
+
+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 vm_print_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(MSG_OUT, "libdvdnav: %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(MSG_OUT, "libdvdnav: %s %d\n", cmd, value.data1);
+ break;
+ case LinkPTTN:
+ case LinkPGN:
+ case LinkCN:
+ fprintf(MSG_OUT, "libdvdnav: %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(MSG_OUT, "libdvdnav: %s\n", cmd);
+ break;
+ case JumpVTS_PTT:
+ fprintf(MSG_OUT, "libdvdnav: %s %d:%d\n", cmd, value.data1, value.data2);
+ break;
+ case JumpSS_VTSM:
+ fprintf(MSG_OUT, "libdvdnav: %s vts %d title %d menu %d\n",
+ cmd, value.data1, value.data2, value.data3);
+ break;
+ case CallSS_FP:
+ fprintf(MSG_OUT, "libdvdnav: %s resume cell %d\n", cmd, value.data1);
+ break;
+ case CallSS_VMGM_MENU: /* == 2 -> Title Menu */
+ case CallSS_VTSM:
+ fprintf(MSG_OUT, "libdvdnav: %s %d resume cell %d\n", cmd, value.data1, value.data2);
+ break;
+ case CallSS_VMGM_PGC:
+ fprintf(MSG_OUT, "libdvdnav: %s %d resume cell %d\n", cmd, value.data1, value.data2);
+ break;
+ }
+ }
+
+void vm_print_registers( registers_t *registers ) {
+ int32_t i;
+ fprintf(MSG_OUT, "libdvdnav: # ");
+ for(i = 0; i < 24; i++)
+ fprintf(MSG_OUT, " %2d |", i);
+ fprintf(MSG_OUT, "\nlibdvdnav: SRPMS: ");
+ for(i = 0; i < 24; i++)
+ fprintf(MSG_OUT, "%04x|", registers->SPRM[i]);
+ fprintf(MSG_OUT, "\nlibdvdnav: GRPMS: ");
+ for(i = 0; i < 16; i++)
+ fprintf(MSG_OUT, "%04x|", get_GPRM(registers, i) );
+ fprintf(MSG_OUT, "\nlibdvdnav: Gmode: ");
+ for(i = 0; i < 16; i++)
+ fprintf(MSG_OUT, "%04x|", registers->GPRM_mode[i]);
+ fprintf(MSG_OUT, "\nlibdvdnav: Gtime: ");
+ for(i = 0; i < 16; i++)
+ fprintf(MSG_OUT, "%04lx|", registers->GPRM_time[i].tv_sec & 0xffff);
+ fprintf(MSG_OUT, "\n");
+}
+
+#endif
+
diff --git a/libdvdnav/decoder.h b/libdvdnav/decoder.h
new file mode 100644
index 0000000..d00ceaa
--- /dev/null
+++ b/libdvdnav/decoder.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2000, 2001 Martin Norb�k, H�an 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * $Id: decoder.h,v 1.9 2004/03/16 11:43:38 mroi Exp $
+ *
+ */
+
+#ifndef DECODER_H_INCLUDED
+#define DECODER_H_INCLUDED
+
+#include "common.h"
+#include <sys/time.h>
+
+/* vm_cmd_t */
+#include "dvdnav_internal.h"
+
+/* link command types */
+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;
+
+/* a link's data set */
+typedef struct {
+ link_cmd_t command;
+ uint16_t data1;
+ uint16_t data2;
+ uint16_t data3;
+} link_t;
+
+/* the VM registers */
+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;
+
+/* a VM command data set */
+typedef struct {
+ uint64_t instruction;
+ uint64_t examined;
+ registers_t *registers;
+} command_t;
+
+/* the big VM function, executing the given commands and writing
+ * the link where to continue, the return value indicates if a jump
+ * has been performed */
+int32_t vmEval_CMD(vm_cmd_t commands[], int32_t num_commands,
+ registers_t *registers, link_t *return_values);
+
+/* extracts some bits from the command */
+uint32_t vm_getbits(command_t* command, int32_t start, int32_t count);
+
+#ifdef TRACE
+/* for debugging: prints a link in readable form */
+void vm_print_link(link_t value);
+
+/* for debugging: dumps VM registers */
+void vm_print_registers( registers_t *registers );
+#endif
+
+#endif /* DECODER_H_INCLUDED */
diff --git a/libdvdnav/dvd_types.h b/libdvdnav/dvd_types.h
new file mode 100644
index 0000000..dc9aeb1
--- /dev/null
+++ b/libdvdnav/dvd_types.h
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2000, 2001 Bj�n Englund, H�an 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * $Id: dvd_types.h,v 1.4 2003/04/29 15:58:30 jcdutton Exp $
+ *
+ */
+
+/*
+ * Various useful structs and enums for DVDs.
+ */
+
+#ifndef DVD_H_INCLUDED
+#define DVD_H_INCLUDED
+
+#include "common.h"
+
+
+/*
+ * DVD Menu ID
+ * (see dvdnav_menu_call())
+ */
+typedef enum {
+ /* When used in VTS domain, DVD_MENU_Escape behaves like DVD_MENU_Root,
+ * but from within a menu domain, DVD_MENU_Escape resumes playback. */
+ DVD_MENU_Escape = 0,
+ DVD_MENU_Title = 2,
+ DVD_MENU_Root = 3,
+ DVD_MENU_Subpicture = 4,
+ DVD_MENU_Audio = 5,
+ DVD_MENU_Angle = 6,
+ DVD_MENU_Part = 7
+} DVDMenuID_t;
+
+
+/*
+ * Structure containing info on highlight areas
+ * (see dvdnav_get_highlight_area())
+ */
+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 */
+
+ /* button number for the SPU decoder/overlaying engine */
+ uint32_t buttonN;
+} dvdnav_highlight_area_t;
+
+
+/* the following types are currently unused */
+
+#if 0
+
+/* 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;
+
+/* User operation permissions */
+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];
+
+/* Navigation */
+typedef int DVDStream_t;
+typedef int DVDPTT_t;
+typedef int DVDTitle_t;
+
+/* Angle number (1-9 or default?) */
+typedef int DVDAngle_t;
+
+/* Timecode */
+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,
+ DVD_AUDIO_APP_MODE_Karaoke = 1,
+ DVD_AUDIO_APP_MODE_Surround = 2,
+ DVD_AUDIO_APP_MODE_Other = 3
+} DVDAudioAppMode_t;
+
+/* The audio format */
+typedef enum {
+ DVD_AUDIO_FORMAT_AC3 = 0,
+ DVD_AUDIO_FORMAT_MPEG1 = 1,
+ DVD_AUDIO_FORMAT_MPEG1_DRC = 2,
+ DVD_AUDIO_FORMAT_MPEG2 = 3,
+ DVD_AUDIO_FORMAT_MPEG2_DRC = 4,
+ DVD_AUDIO_FORMAT_LPCM = 5,
+ DVD_AUDIO_FORMAT_DTS = 6,
+ DVD_AUDIO_FORMAT_SDDS = 7,
+ DVD_AUDIO_FORMAT_Other = 8
+} DVDAudioFormat_t;
+
+/* Audio language extension */
+typedef enum {
+ DVD_AUDIO_LANG_EXT_NotSpecified = 0,
+ DVD_AUDIO_LANG_EXT_NormalCaptions = 1,
+ DVD_AUDIO_LANG_EXT_VisuallyImpaired = 2,
+ DVD_AUDIO_LANG_EXT_DirectorsComments1 = 3,
+ DVD_AUDIO_LANG_EXT_DirectorsComments2 = 4
+} 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;
+
+/* Display mode */
+typedef enum {
+ DVD_DISPLAY_MODE_ContentDefault = 0,
+ DVD_DISPLAY_MODE_16x9 = 1,
+ DVD_DISPLAY_MODE_4x3PanScan = 2,
+ DVD_DISPLAY_MODE_4x3Letterboxed = 3
+} DVDDisplayMode_t;
+
+/* Audio attributes */
+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 int DVDAudioSampleFreq_t;
+typedef int DVDAudioSampleQuant_t;
+typedef int DVDChannelNumber_t;
+
+/* Subpicture attributes */
+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;
+
+/* Video attributes */
+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;
+typedef int DVDVideoCompression_t;
+
+#endif
+
+#endif /* DVD_H_INCLUDED */
diff --git a/libdvdnav/dvdnav.c b/libdvdnav/dvdnav.c
new file mode 100644
index 0000000..00ffd7d
--- /dev/null
+++ b/libdvdnav/dvdnav.c
@@ -0,0 +1,1093 @@
+/*
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * $Id: dvdnav.c,v 1.34 2005/10/15 14:04:05 jcdutton Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/*
+#define LOG_DEBUG
+*/
+#include "common.h"
+#include "dvdnav_internal.h"
+#include "read_cache.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+
+#include "remap.h"
+#include "dvdread.h"
+
+static dvdnav_status_t dvdnav_clear(dvdnav_t * this) {
+ /* clear everything except file, vm, mutex, readahead */
+
+ if (this->file) DVDCloseFile(this->file);
+ this->file = NULL;
+
+ memset(&this->pci,0,sizeof(this->pci));
+ memset(&this->dsi,0,sizeof(this->dsi));
+ this->last_cmd_nav_lbn = SRI_END_OF_CELL;
+
+ /* Set initial values of flags */
+ this->position_current.still = 0;
+ this->skip_still = 0;
+ this->sync_wait = 0;
+ this->sync_wait_skip = 0;
+ this->spu_clut_changed = 0;
+ this->started = 0;
+
+ dvdnav_read_cache_clear(this->cache);
+
+ return DVDNAV_STATUS_OK;
+}
+
+
+dvdnav_status_t dvdnav_open(dvdnav_t** dest, const char *path,dvd_reader_t *dvd) {
+ dvdnav_t *this;
+ struct timeval time;
+
+ /* Create a new structure */
+ fprintf(MSG_OUT, "libdvdnav: Using dvdnav version %s from http://xine.sf.net\n", VERSION);
+
+ (*dest) = NULL;
+ this = (dvdnav_t*)malloc(sizeof(dvdnav_t));
+ if(!this)
+ return DVDNAV_STATUS_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.");
+ pthread_mutex_destroy(&this->vm_lock);
+ free(this);
+ return DVDNAV_STATUS_ERR;
+ }
+ this->vm->openedDvd=dvd;
+ if(!vm_reset(this->vm, path)) {
+ printerr("Error starting the VM / opening the DVD device.");
+ pthread_mutex_destroy(&this->vm_lock);
+ vm_free_vm(this->vm);
+ free(this);
+ return DVDNAV_STATUS_ERR;
+ }
+
+ /* Set the path. FIXME: Is a deep copy 'right' */
+ strncpy(this->path, path, MAX_PATH_LEN);
+
+ /* 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);
+
+ /* Start the read-ahead cache. */
+ this->cache = dvdnav_read_cache_new(this);
+
+ /* Seed the random numbers. So that the DVD VM Command rand()
+ * gives a different start value each time a DVD is played. */
+ gettimeofday(&time, NULL);
+ srand(time.tv_usec);
+
+ dvdnav_clear(this);
+
+ (*dest) = this;
+ return DVDNAV_STATUS_OK;
+}
+
+
+
+dvdnav_status_t dvdnav_close(dvdnav_t *this) {
+
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: close:called\n");
+#endif
+
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ if (this->file) {
+ DVDCloseFile(this->file);
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: close:file closing\n");
+#endif
+ this->file = NULL;
+ }
+
+ /* Free the VM */
+ if(this->vm)
+ vm_free_vm(this->vm);
+
+ 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 DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_reset(dvdnav_t *this) {
+ dvdnav_status_t result;
+
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: reset:called\n");
+#endif
+
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: reseting vm\n");
+#endif
+ if(!vm_reset(this->vm, NULL)) {
+ printerr("Error restarting the VM.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: clearing dvdnav\n");
+#endif
+ result = dvdnav_clear(this);
+
+ pthread_mutex_unlock(&this->vm_lock);
+ return result;
+}
+
+dvdnav_status_t dvdnav_path(dvdnav_t *this, const char** path) {
+
+ if(!this || !path) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ (*path) = this->path;
+
+ return DVDNAV_STATUS_OK;
+}
+
+const char* dvdnav_err_to_string(dvdnav_t *this) {
+
+ if(!this)
+ return "Hey! You gave me a NULL pointer you naughty person!";
+
+ return this->err_str;
+}
+
+/* converts a dvd_time_t to PTS ticks */
+int64_t dvdnav_convert_time(dvd_time_t *time) {
+ int64_t result;
+ int64_t frames;
+
+ result = (time->hour >> 4 ) * 10 * 60 * 60 * 90000;
+ result += (time->hour & 0x0f) * 60 * 60 * 90000;
+ result += (time->minute >> 4 ) * 10 * 60 * 90000;
+ result += (time->minute & 0x0f) * 60 * 90000;
+ result += (time->second >> 4 ) * 10 * 90000;
+ result += (time->second & 0x0f) * 90000;
+
+ frames = ((time->frame_u & 0x30) >> 4) * 10;
+ frames += ((time->frame_u & 0x0f) ) ;
+
+ if (time->frame_u & 0x80)
+ result += frames * 3000;
+ else
+ result += frames * 3600;
+
+ return result;
+}
+
+/*
+ * Returns 1 if block contains NAV packet, 0 otherwise.
+ * Processes 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.
+ */
+static int32_t dvdnav_decode_packet(dvdnav_t *this, uint8_t *p, dsi_t *nav_dsi, pci_t *nav_pci) {
+ int32_t bMpeg1 = 0;
+ uint32_t nHeaderLen;
+ uint32_t nPacketLen;
+ uint32_t nStreamID;
+
+ if (p[3] == 0xBA) { /* program stream pack header */
+ int32_t nStuffingBytes;
+
+ 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 */
+ 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(MSG_OUT, "libdvdnav: 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 */
+#if 0
+ int32_t i;
+ fprintf(MSG_OUT, "libdvdnav: nav packet=%u\n",p-p_start-6);
+ for(i=0;i<80;i++)
+ fprintf(MSG_OUT, "%02x ",p[i-6]);
+ fprintf(MSG_OUT, "\n");
+#endif
+
+ 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;
+ 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
+ */
+static int32_t dvdnav_get_vobu(dvdnav_t *this, dsi_t *nav_dsi, pci_t *nav_pci, dvdnav_vobu_t *vobu) {
+ uint32_t next;
+ int32_t 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
+ */
+
+#if 0
+ /* Old code -- may still be useful 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;
+ }
+#else
+ /* Relative offset from vobu_start */
+ vobu->vobu_next = ( nav_dsi->vobu_sri.next_vobu & 0x3fffffff );
+#endif
+
+ vm_get_angle_info(this->vm, &angle, &num_angle);
+
+ /* FIMXE: The angle reset doesn't work for some reason for the moment */
+#if 0
+ if((num_angle < angle) && (angle != 1)) {
+ fprintf(MSG_OUT, "libdvdnav: angle ends!\n");
+
+ /* This is to switch back to angle one when we
+ * finish with angles. */
+ dvdnav_angle_change(this, 1);
+ }
+#endif
+
+ if(num_angle != 0) {
+
+ if((next = nav_pci->nsml_agli.nsml_agl_dsta[angle-1]) != 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((next = nav_dsi->sml_agli.data[angle-1].address) != 0) {
+ 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;
+}
+
+/*
+ * These are the main get_next_block function which actually get the media stream video and audio etc.
+ *
+ * There are two versions: The second one is using the zero-copy read ahead cache and therefore
+ * hands out pointers targetting directly into the cache.
+ * The first one uses a memcopy to fill this cache block into the application provided memory.
+ * The benefit of this first one is that no special memory management is needed. The application is
+ * the only one responsible of allocating and freeing the memory associated with the pointer.
+ * The drawback is the additional memcopy.
+ */
+
+dvdnav_status_t dvdnav_get_next_block(dvdnav_t *this, uint8_t *buf,
+ int32_t *event, int32_t *len) {
+ unsigned char *block;
+ dvdnav_status_t status;
+
+ block = buf;
+ status = dvdnav_get_next_cache_block(this, &block, event, len);
+ if (status == DVDNAV_STATUS_OK && 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, uint8_t **buf,
+ int32_t *event, int32_t *len) {
+ dvd_state_t *state;
+ int32_t result;
+
+ if(!this || !event || !len || !buf || !*buf) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+
+ if(!this->started) {
+ /* Start the VM */
+ if (!vm_start(this->vm)) {
+ printerr("Encrypted or faulty DVD");
+ return DVDNAV_STATUS_ERR;
+ }
+ this->started = 1;
+ }
+
+ state = &(this->vm->state);
+ (*event) = DVDNAV_NOP;
+ (*len) = 0;
+
+ /* Check the STOP flag */
+ if(this->vm->stopped) {
+ vm_stop(this->vm);
+ (*event) = DVDNAV_STOP;
+ this->started = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+
+ vm_position_get(this->vm, &this->position_next);
+
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: POS-NEXT ");
+ vm_position_print(this->vm, &this->position_next);
+ fprintf(MSG_OUT, "libdvdnav: POS-CUR ");
+ vm_position_print(this->vm, &this->position_current);
+#endif
+
+ /* did we hop? */
+ if(this->position_current.hop_channel != this->position_next.hop_channel) {
+ (*event) = DVDNAV_HOP_CHANNEL;
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: HOP_CHANNEL\n");
+#endif
+ if (this->position_next.hop_channel - this->position_current.hop_channel >= HOP_SEEK) {
+ int32_t num_angles = 0, current;
+
+ /* we seeked -> check for multiple angles */
+ vm_get_angle_info(this->vm, &current, &num_angles);
+ if (num_angles > 1) {
+ int32_t result, block;
+ /* we have to skip the first VOBU when seeking in a multiangle feature,
+ * because it might belong to the wrong angle */
+ block = this->position_next.cell_start + this->position_next.block;
+ result = dvdnav_read_cache_block(this->cache, block, 1, buf);
+ if(result <= 0) {
+ printerr("Error reading NAV packet.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ /* Decode nav into pci and dsi. Then get next VOBU info. */
+ if(!dvdnav_decode_packet(this, *buf, &this->dsi, &this->pci)) {
+ printerr("Expected NAV packet but none found.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ dvdnav_get_vobu(this, &this->dsi, &this->pci, &this->vobu);
+ /* skip to next, if there is a next */
+ if (this->vobu.vobu_next != SRI_END_OF_CELL) {
+ this->vobu.vobu_start += this->vobu.vobu_next;
+ this->vobu.vobu_next = 0;
+ }
+ /* update VM state */
+ this->vm->state.blockN = this->vobu.vobu_start - this->position_next.cell_start;
+ }
+ }
+ this->position_current.hop_channel = this->position_next.hop_channel;
+ /* update VOBU info */
+ this->vobu.vobu_start = this->position_next.cell_start + this->position_next.block;
+ this->vobu.vobu_next = 0;
+ /* Make blockN == vobu_length to do expected_nav */
+ this->vobu.vobu_length = 0;
+ this->vobu.blockN = 0;
+ this->sync_wait = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+
+ /* Check the HIGHLIGHT flag */
+ if(this->position_current.button != this->position_next.button) {
+ dvdnav_highlight_event_t *hevent = (dvdnav_highlight_event_t *)*buf;
+
+ (*event) = DVDNAV_HIGHLIGHT;
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: HIGHLIGHT\n");
+#endif
+ (*len) = sizeof(dvdnav_highlight_event_t);
+ hevent->display = 1;
+ hevent->buttonN = this->position_next.button;
+ this->position_current.button = this->position_next.button;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+
+ /* Check the WAIT flag */
+ if(this->sync_wait) {
+ (*event) = DVDNAV_WAIT;
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: WAIT\n");
+#endif
+ (*len) = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_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;
+ int32_t vtsN;
+ dvdnav_vts_change_event_t *vts_event = (dvdnav_vts_change_event_t *)*buf;
+
+ if(this->file) {
+ DVDCloseFile(this->file);
+ this->file = NULL;
+ }
+
+ vts_event->old_vtsN = this->position_current.vts;
+ vts_event->old_domain = this->position_current.domain;
+
+ /* Use the 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 DVDNAV_STATUS_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 DVDNAV_STATUS_ERR;
+ }
+
+ /* File opened successfully so return a VTS change event */
+ (*event) = DVDNAV_VTS_CHANGE;
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: VTS_CHANGE\n");
+#endif
+ (*len) = sizeof(dvdnav_vts_change_event_t);
+
+ 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 DVDNAV_STATUS_OK;
+ }
+
+ /* Check if the cell changed */
+ if( (this->position_current.cell != this->position_next.cell) ||
+ (this->position_current.cell_restart != this->position_next.cell_restart) ||
+ (this->position_current.cell_start != this->position_next.cell_start) ) {
+ dvdnav_cell_change_event_t *cell_event = (dvdnav_cell_change_event_t *)*buf;
+ int32_t first_cell_nr, last_cell_nr, i;
+ dvd_state_t *state = &this->vm->state;
+
+ (*event) = DVDNAV_CELL_CHANGE;
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: CELL_CHANGE\n");
+#endif
+ (*len) = sizeof(dvdnav_cell_change_event_t);
+
+ cell_event->cellN = state->cellN;
+ cell_event->pgN = state->pgN;
+ cell_event->cell_length =
+ dvdnav_convert_time(&state->pgc->cell_playback[state->cellN-1].playback_time);
+
+ cell_event->pg_length = 0;
+ /* Find start cell of program. */
+ first_cell_nr = state->pgc->program_map[state->pgN-1];
+ /* Find end cell of program */
+ 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;
+ for (i = first_cell_nr; i <= last_cell_nr; i++)
+ cell_event->pg_length +=
+ dvdnav_convert_time(&state->pgc->cell_playback[i - 1].playback_time);
+ cell_event->pgc_length = dvdnav_convert_time(&state->pgc->playback_time);
+
+ cell_event->cell_start = 0;
+ for (i = 1; i < state->cellN; i++)
+ cell_event->cell_start +=
+ dvdnav_convert_time(&state->pgc->cell_playback[i - 1].playback_time);
+
+ cell_event->pg_start = 0;
+ for (i = 1; i < state->pgc->program_map[state->pgN-1]; i++)
+ cell_event->pg_start +=
+ dvdnav_convert_time(&state->pgc->cell_playback[i - 1].playback_time);
+
+ this->position_current.cell = this->position_next.cell;
+ this->position_current.cell_restart = this->position_next.cell_restart;
+ this->position_current.cell_start = this->position_next.cell_start;
+ this->position_current.block = this->position_next.block;
+
+ /* vobu info is used for mid cell resumes */
+ this->vobu.vobu_start = this->position_next.cell_start + this->position_next.block;
+ this->vobu.vobu_next = 0;
+ /* Make blockN == vobu_length to do expected_nav */
+ this->vobu.vobu_length = 0;
+ this->vobu.blockN = 0;
+
+ /* update the spu palette at least on PGC changes */
+ this->spu_clut_changed = 1;
+ 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 DVDNAV_STATUS_OK;
+ }
+
+ /* has the CLUT changed? */
+ if(this->spu_clut_changed) {
+ (*event) = DVDNAV_SPU_CLUT_CHANGE;
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: SPU_CLUT_CHANGE\n");
+#endif
+ (*len) = 16 * sizeof(uint32_t);
+ memcpy(*buf, &(state->pgc->palette), 16 * sizeof(uint32_t));
+ this->spu_clut_changed = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+
+ /* has the SPU channel changed? */
+ if(this->position_current.spu_channel != this->position_next.spu_channel) {
+ dvdnav_spu_stream_change_event_t *stream_change = (dvdnav_spu_stream_change_event_t *)*buf;
+
+ (*event) = DVDNAV_SPU_STREAM_CHANGE;
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "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);
+ this->position_current.spu_channel = this->position_next.spu_channel;
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE stream_id_wide=%d\n",stream_change->physical_wide);
+ fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE stream_id_letterbox=%d\n",stream_change->physical_letterbox);
+ fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE stream_id_pan_scan=%d\n",stream_change->physical_pan_scan);
+ fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE returning DVDNAV_STATUS_OK\n");
+#endif
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+
+ /* has the audio channel changed? */
+ if(this->position_current.audio_channel != this->position_next.audio_channel) {
+ dvdnav_audio_stream_change_event_t *stream_change = (dvdnav_audio_stream_change_event_t *)*buf;
+
+ (*event) = DVDNAV_AUDIO_STREAM_CHANGE;
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: AUDIO_STREAM_CHANGE\n");
+#endif
+ (*len) = sizeof(dvdnav_audio_stream_change_event_t);
+ stream_change->physical = vm_get_audio_active_stream( this->vm );
+ this->position_current.audio_channel = this->position_next.audio_channel;
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: AUDIO_STREAM_CHANGE stream_id=%d returning DVDNAV_STATUS_OK\n",stream_change->physical);
+#endif
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+
+ /* Check the STILLFRAME flag */
+ if(this->position_current.still != 0) {
+ dvdnav_still_event_t *still_event = (dvdnav_still_event_t *)*buf;
+
+ (*event) = DVDNAV_STILL_FRAME;
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: STILL_FRAME\n");
+#endif
+ (*len) = sizeof(dvdnav_still_event_t);
+ still_event->length = this->position_current.still;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+
+ /* Have we reached the end of a VOBU? */
+ if (this->vobu.blockN >= this->vobu.vobu_length) {
+
+ /* Have we reached the end of a cell? */
+ if(this->vobu.vobu_next == SRI_END_OF_CELL) {
+ /* End of Cell from NAV DSI info */
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: Still set to %x\n", this->position_next.still);
+#endif
+ this->position_current.still = this->position_next.still;
+
+ /* we are about to leave a cell, so a lot of state changes could occur;
+ * under certain conditions, the application should get in sync with us before this,
+ * otherwise it might show stills or menus too shortly */
+ if ((this->position_current.still || this->pci.hli.hl_gi.hli_ss) && !this->sync_wait_skip) {
+ this->sync_wait = 1;
+ } else {
+ if( this->position_current.still == 0 || this->skip_still ) {
+ /* no active cell still -> get us to the next cell */
+ vm_get_next_cell(this->vm);
+ this->position_current.still = 0; /* still gets activated at end of cell */
+ this->skip_still = 0;
+ this->sync_wait_skip = 0;
+ }
+ }
+ /* handle related state changes in next iteration */
+ (*event) = DVDNAV_NOP;
+ (*len) = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+
+ /* Perform remapping jump if necessary (this is always a
+ * VOBU boundary). */
+ if (this->vm->map) {
+ this->vobu.vobu_next = remap_block( this->vm->map,
+ this->vm->state.domain, this->vm->state.TTN_REG,
+ this->vm->state.pgN,
+ this->vobu.vobu_start, this->vobu.vobu_next);
+ }
+
+ /* at the start of the next VOBU -> expecting NAV packet */
+ 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 DVDNAV_STATUS_ERR;
+ }
+ /* Decode nav into pci and dsi. Then get next VOBU info. */
+ if(!dvdnav_decode_packet(this, *buf, &this->dsi, &this->pci)) {
+ printerr("Expected NAV packet but none found.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ /* 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->vm->state.blockN = this->vobu.vobu_start - this->position_current.cell_start;
+
+ dvdnav_get_vobu(this, &this->dsi, &this->pci, &this->vobu);
+ this->vobu.blockN = 0;
+ /* Give the cache a hint about the size of next VOBU.
+ * This improves pre-caching, because the VOBU will almost certainly be read entirely.
+ */
+ dvdnav_pre_cache_blocks(this->cache, this->vobu.vobu_start+1, this->vobu.vobu_length+1);
+
+ /* release NAV menu filter, when we reach the same NAV packet again */
+ if (this->last_cmd_nav_lbn == this->pci.pci_gi.nv_pck_lbn)
+ this->last_cmd_nav_lbn = SRI_END_OF_CELL;
+
+ /* Successfully got a NAV packet */
+ (*event) = DVDNAV_NAV_PACKET;
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: NAV_PACKET\n");
+#endif
+ (*len) = 2048;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_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 DVDNAV_STATUS_ERR;
+ }
+
+ this->vobu.blockN++;
+ 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 DVDNAV_STATUS_ERR;
+ }
+ (*event) = DVDNAV_BLOCK_OK;
+ (*len) = 2048;
+
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_get_title_string(dvdnav_t *this, const char **title_str) {
+
+ if(!this || !title_str) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ (*title_str) = this->vm->dvd_name;
+ return DVDNAV_STATUS_OK;
+}
+
+uint8_t dvdnav_get_video_aspect(dvdnav_t *this) {
+ uint8_t retval;
+
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return -1;
+ }
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return -1;
+ }
+
+ 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;
+
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return -1;
+ }
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return -1;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ retval = (uint8_t)vm_get_video_scale_permission(this->vm);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return retval;
+}
+
+uint16_t dvdnav_audio_stream_to_lang(dvdnav_t *this, uint8_t stream) {
+ audio_attr_t attr;
+
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return -1;
+ }
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ 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;
+}
+
+uint16_t dvdnav_spu_stream_to_lang(dvdnav_t *this, uint8_t stream) {
+ subp_attr_t attr;
+
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return -1;
+ }
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ 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_audio_logical_stream(dvdnav_t *this, uint8_t audio_num) {
+ int8_t retval;
+
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return -1;
+ }
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return -1;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ if (!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return -1;
+ }
+ retval = vm_get_audio_stream(this->vm, audio_num);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return retval;
+}
+
+int8_t dvdnav_get_spu_logical_stream(dvdnav_t *this, uint8_t subp_num) {
+ int8_t retval;
+
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return -1;
+ }
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return -1;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ if (!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return -1;
+ }
+ retval = vm_get_subp_stream(this->vm, subp_num, 0);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return retval;
+}
+
+int8_t dvdnav_get_active_audio_stream(dvdnav_t *this) {
+ int8_t retval;
+
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return -1;
+ }
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return -1;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ if (!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return -1;
+ }
+ retval = vm_get_audio_active_stream(this->vm);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return retval;
+}
+
+int8_t dvdnav_get_active_spu_stream(dvdnav_t *this) {
+ int8_t retval;
+
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return -1;
+ }
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return -1;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ if (!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return -1;
+ }
+ retval = vm_get_subp_active_stream(this->vm, 0);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return retval;
+}
+
+static int8_t dvdnav_is_domain(dvdnav_t *this, domain_t domain) {
+ int8_t retval;
+
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return -1;
+ }
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return -1;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ retval = (this->vm->state.domain == domain);
+ 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, int32_t angle) {
+ int32_t num, current;
+
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ vm_get_angle_info(this->vm, &current, &num);
+ /* Set angle SPRM if valid */
+ if((angle > 0) && (angle <= num)) {
+ this->vm->state.AGL_REG = angle;
+ } else {
+ printerr("Passed an invalid angle number.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *this, int32_t *current_angle,
+ int32_t *number_of_angles) {
+ if(!this || !current_angle || !number_of_angles) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ vm_get_angle_info(this->vm, current_angle, number_of_angles);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return DVDNAV_STATUS_OK;
+}
+
+pci_t* dvdnav_get_current_nav_pci(dvdnav_t *this) {
+ if(!this) return 0;
+ return &this->pci;
+}
+
+dsi_t* dvdnav_get_current_nav_dsi(dvdnav_t *this) {
+ if(!this) return 0;
+ return &this->dsi;
+}
+
+uint32_t dvdnav_get_next_still_flag(dvdnav_t *this) {
+ if(!this) return -1;
+ return this->position_next.still;
+}
+
diff --git a/libdvdnav/dvdnav.h b/libdvdnav/dvdnav.h
new file mode 100644
index 0000000..1e39d0b
--- /dev/null
+++ b/libdvdnav/dvdnav.h
@@ -0,0 +1,630 @@
+/*
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * $Id: dvdnav.h,v 1.15 2005/10/14 21:02:16 miguelfreitas Exp $
+ *
+ */
+
+/*
+ * This is the main header file applications should include if they want
+ * to access dvdnav functionality.
+ */
+
+#ifndef DVDNAV_H_INCLUDED
+#define DVDNAV_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "common.h"
+# include "dvdnav_events.h"
+# include "dvd_types.h"
+
+
+
+
+/*********************************************************************
+ * dvdnav data types *
+ *********************************************************************/
+
+/*
+ * Opaque data-type can be viewed as a 'DVD handle'. You should get
+ * a pointer to a dvdnav_t from the dvdnav_open() function.
+ * Never call free() on the pointer, you have to give it back with
+ * dvdnav_close().
+ */
+typedef struct dvdnav_s dvdnav_t;
+
+/* Status as reported by most of libdvdnav's functions */
+typedef int32_t dvdnav_status_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().
+ */
+#define DVDNAV_STATUS_ERR 0
+#define DVDNAV_STATUS_OK 1
+
+
+/*********************************************************************
+ * initialisation & housekeeping functions *
+ *********************************************************************/
+
+/*
+ * These functions allow you to open a DVD device and associate it
+ * with a dvdnav_t.
+ */
+
+/*
+ * Attempts to open the DVD drive at the specified path and pre-cache
+ * the CSS-keys. libdvdread is used to access the DVD, so any source
+ * supported by libdvdread can be given with "path". Currently,
+ * libdvdread can access: DVD drives, DVD image files, DVD file-by-file
+ * copies.
+ *
+ * The resulting dvdnav_t handle will be written to *dest.
+ */
+dvdnav_status_t dvdnav_open(dvdnav_t **dest, const char *path,dvd_reader_t *dvd);
+
+/*
+ * Closes a dvdnav_t previously opened with dvdnav_open(), freeing any
+ * memory associated with it.
+ */
+dvdnav_status_t dvdnav_close(dvdnav_t *self);
+
+/*
+ * Resets the DVD virtual machine and cache buffers.
+ */
+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 *path to NULL
+ * on error.
+ */
+dvdnav_status_t dvdnav_path(dvdnav_t *self, const char **path);
+
+/*
+ * Returns a human-readable string describing the last error.
+ */
+const char* dvdnav_err_to_string(dvdnav_t *self);
+
+/* converts a dvd_time_t to PTS ticks */
+int64_t dvdnav_convert_time(dvd_time_t *time);
+
+/*********************************************************************
+ * changing and reading DVD player characteristics *
+ *********************************************************************/
+
+/*
+ * These functions allow you to manipulate the various global characteristics
+ * of the DVD playback engine.
+ */
+
+/*
+ * Sets 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.
+ *
+ * This has _nothing_ to do with the region setting of the DVD drive.
+ */
+dvdnav_status_t dvdnav_set_region_mask(dvdnav_t *self, int32_t region_mask);
+
+/*
+ * Returns the region mask (bit 0 set implies region 1, bit 1 set implies
+ * region 2, etc) of the virtual machine.
+ *
+ * This has _nothing_ to do with the region setting of the DVD drive.
+ */
+dvdnav_status_t dvdnav_get_region_mask(dvdnav_t *self, int32_t *region_mask);
+
+/*
+ * Specify whether read-ahead caching should be used. You may not want this if your
+ * decoding engine does its own buffering.
+ *
+ * The default read-ahead cache does not use an additional thread for the reading
+ * (see read_cache.c for a threaded cache, but note that this code is currently
+ * unmaintained). It prebuffers on VOBU level by reading ahead several buffers
+ * on every read request. The speed of this prebuffering has been optimized to
+ * also work on slow DVD drives.
+ *
+ * If in addition you want to prevent memcpy's to improve performance, have a look
+ * at dvdnav_get_next_cache_block().
+ */
+dvdnav_status_t dvdnav_set_readahead_flag(dvdnav_t *self, int32_t read_ahead_flag);
+
+/*
+ * Query whether read-ahead caching/buffering will be used.
+ */
+dvdnav_status_t dvdnav_get_readahead_flag(dvdnav_t *self, int32_t *read_ahead_flag);
+
+/*
+ * Specify whether the positioning works PGC or PG based.
+ * Programs (PGs) on DVDs are similar to Chapters and a program chain (PGC)
+ * usually covers a whole feature. This affects the behaviour of the
+ * functions dvdnav_get_position() and dvdnav_sector_search(). See there.
+ * Default is PG based positioning.
+ */
+dvdnav_status_t dvdnav_set_PGC_positioning_flag(dvdnav_t *self, int32_t pgc_based_flag);
+
+/*
+ * Query whether positioning is PG or PGC based.
+ */
+dvdnav_status_t dvdnav_get_PGC_positioning_flag(dvdnav_t *self, int32_t *pgc_based_flag);
+
+
+/*********************************************************************
+ * 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.
+ *
+ * See the dvdnav_events.h header for information on the various events.
+ */
+dvdnav_status_t dvdnav_get_next_block(dvdnav_t *self, uint8_t *buf,
+ int32_t *event, int32_t *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 pointers must _never_ be freed but instead returned to the library via
+ * dvdnav_free_cache_block().
+ */
+dvdnav_status_t dvdnav_get_next_cache_block(dvdnav_t *self, uint8_t **buf,
+ int32_t *event, int32_t *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.
+ */
+dvdnav_status_t dvdnav_free_cache_block(dvdnav_t *self, unsigned char *buf);
+
+/*
+ * If we are currently in a still-frame this function skips it.
+ *
+ * See also the DVDNAV_STILL_FRAME event.
+ */
+dvdnav_status_t dvdnav_still_skip(dvdnav_t *self);
+
+/*
+ * If we are currently in WAIT state, that is: the application is required to
+ * wait for its fifos to become empty, calling this signals libdvdnav that this
+ * is achieved and that it can continue.
+ *
+ * See also the DVDNAV_WAIT event.
+ */
+dvdnav_status_t dvdnav_wait_skip(dvdnav_t *self);
+
+/*
+ * Returns the still time from the currently playing cell.
+ * The still time is given in seconds with 0xff meaning an indefinite still.
+ *
+ * This function can be used to detect still frames before they are reached.
+ * Some players might need this to prepare for a frame to be shown for a
+ * longer time than usual.
+ */
+uint32_t dvdnav_get_next_still_flag(dvdnav_t *self);
+
+/*
+ * Stops playback. The next event obtained with one of the get_next_block
+ * functions will be a DVDNAV_STOP event.
+ *
+ * It is not required to call this before dvdnav_close().
+ */
+dvdnav_status_t dvdnav_stop(dvdnav_t *self);
+
+
+/*********************************************************************
+ * title/part navigation *
+ *********************************************************************/
+
+/*
+ * Returns the number of titles on the disk.
+ */
+dvdnav_status_t dvdnav_get_number_of_titles(dvdnav_t *self, int32_t *titles);
+
+/*
+ * Returns the number of parts within the given title.
+ */
+dvdnav_status_t dvdnav_get_number_of_parts(dvdnav_t *self, int32_t title, int32_t *parts);
+
+/*
+ * Plays the specified title of the DVD from its beginning (that is: part 1).
+ */
+dvdnav_status_t dvdnav_title_play(dvdnav_t *self, int32_t title);
+
+/*
+ * Plays the specified title, starting from the specified part.
+ */
+dvdnav_status_t dvdnav_part_play(dvdnav_t *self, int32_t title, int32_t part);
+
+/*
+ * Play the specified amount of parts of the specified title of
+ * the DVD then STOP.
+ *
+ * Currently unimplemented!
+ */
+dvdnav_status_t dvdnav_part_play_auto_stop(dvdnav_t *self, int32_t title,
+ int32_t part, int32_t parts_to_play);
+
+/*
+ * Play the specified title starting from the specified time.
+ *
+ * Currently unimplemented!
+ */
+dvdnav_status_t dvdnav_time_play(dvdnav_t *self, int32_t title,
+ uint64_t time);
+
+/*
+ * Stop playing the current position and jump to the specified menu.
+ *
+ * See also DVDMenuID_t from libdvdread
+ */
+dvdnav_status_t dvdnav_menu_call(dvdnav_t *self, DVDMenuID_t menu);
+
+/*
+ * Return the title number and part currently being played.
+ * A title of 0 indicates, we are in a menu. In this case, part
+ * is set to the current menu's ID.
+ */
+dvdnav_status_t dvdnav_current_title_info(dvdnav_t *self, int32_t *title,
+ int32_t *part);
+
+/*
+ * Return the current position (in blocks) within the current
+ * title and the length (in blocks) of said title.
+ *
+ * Current implementation is wrong and likely to behave unpredictably!
+ * Use is discouraged!
+ */
+dvdnav_status_t dvdnav_get_position_in_title(dvdnav_t *self,
+ uint32_t *pos,
+ uint32_t *len);
+
+/*
+ * This function is only available for compatibility reasons.
+ *
+ * Stop playing the current position and start playback of the current title
+ * from the specified part.
+ */
+dvdnav_status_t dvdnav_part_search(dvdnav_t *self, int32_t part);
+
+
+/*********************************************************************
+ * program chain/program navigation *
+ *********************************************************************/
+
+/*
+ * Stop playing the current position and start playback from the last
+ * VOBU boundary before the given sector. The sector number is not
+ * meant to be an absolute physical DVD sector, but a relative sector
+ * in the current program. This function cannot leave the current
+ * program and will fail, if asked to do so.
+ *
+ * If program chain based positioning is enabled
+ * (see dvdnav_set_PGC_positioning_flag()), this will seek to the relative
+ * sector inside the current program chain.
+ *
+ * 'origin' can be one of SEEK_SET, SEEK_CUR, SEEK_END as defined in
+ * fcntl.h.
+ */
+dvdnav_status_t dvdnav_sector_search(dvdnav_t *self,
+ uint64_t offset, int32_t origin);
+
+/*
+ * Stop playing the current position and start playback of the title
+ * from the specified timecode.
+ *
+ * Currently unimplemented!
+ */
+dvdnav_status_t dvdnav_time_search(dvdnav_t *self,
+ uint64_t time);
+
+/*
+ * Stop playing current position and play the "GoUp"-program chain.
+ * (which generally leads to the title menu or a higer-level menu).
+ */
+dvdnav_status_t dvdnav_go_up(dvdnav_t *self);
+
+/*
+ * Stop playing the current position and start playback at the
+ * previous program (if it exists).
+ */
+dvdnav_status_t dvdnav_prev_pg_search(dvdnav_t *self);
+
+/*
+ * Stop playing the current position and start playback at the
+ * first program.
+ */
+dvdnav_status_t dvdnav_top_pg_search(dvdnav_t *self);
+
+/*
+ * Stop playing the current position and start playback at the
+ * next program (if it exists).
+ */
+dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *self);
+
+/*
+ * Return the current position (in blocks) within the current
+ * program and the length (in blocks) of current program.
+ *
+ * If program chain based positioning is enabled
+ * (see dvdnav_set_PGC_positioning_flag()), this will return the
+ * relative position in and the length of the current program chain.
+ */
+dvdnav_status_t dvdnav_get_position(dvdnav_t *self, uint32_t *pos,
+ uint32_t *len);
+
+
+/*********************************************************************
+ * menu highlights *
+ *********************************************************************/
+
+/*
+ * Most functions related to highlights take a NAV PCI packet as a parameter.
+ * While you can get the such a packet from libdvdnav, for players with internal
+ * FIFOs, this will result in errors, because due to the FIFO length, libdvdnav will
+ * be ahead in the stream compared to what the user is seeing on screen.
+ * Therefore, player applications who have a NAV packet available, which is
+ * better in sync with the actual playback should always pass this one to these
+ * functions.
+ */
+
+/*
+ * Get the currently highlighted button
+ * number (1..36) or 0 if no button is highlighted.
+ */
+dvdnav_status_t dvdnav_get_current_highlight(dvdnav_t *self, int32_t *button);
+
+/*
+ * Returns the Presentation Control Information (PCI) structure associated
+ * with the current position.
+ *
+ * Read the general notes above.
+ * See also libdvdreads nav_types.h for definition of pci_t.
+ */
+pci_t* dvdnav_get_current_nav_pci(dvdnav_t *self);
+
+/*
+ * Returns the DSI (data search information) structure associated
+ * with the current position.
+ *
+ * Read the general notes above.
+ * See also libdvdreads nav_types.h for definition of dsi_t.
+ */
+dsi_t* dvdnav_get_current_nav_dsi(dvdnav_t *self);
+
+/*
+ * Get the area associated with a certain button.
+ */
+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).
+ */
+dvdnav_status_t dvdnav_upper_button_select(dvdnav_t *self, pci_t *pci);
+dvdnav_status_t dvdnav_lower_button_select(dvdnav_t *self, pci_t *pci);
+dvdnav_status_t dvdnav_right_button_select(dvdnav_t *self, pci_t *pci);
+dvdnav_status_t dvdnav_left_button_select(dvdnav_t *self, pci_t *pci);
+
+/*
+ * Activate ("press") the currently highlighted button.
+ */
+dvdnav_status_t dvdnav_button_activate(dvdnav_t *self, pci_t *pci);
+
+/*
+ * Highlight a specific button.
+ */
+dvdnav_status_t dvdnav_button_select(dvdnav_t *self, pci_t *pci, int32_t button);
+
+/*
+ * Activate ("press") specified button.
+ */
+dvdnav_status_t dvdnav_button_select_and_activate(dvdnav_t *self, pci_t *pci, int32_t button);
+
+/*
+ * Activate (press) a button and execute specified command.
+ */
+dvdnav_status_t dvdnav_button_activate_cmd(dvdnav_t *self, int32_t button, vm_cmd_t *cmd);
+
+/*
+ * Select button at specified video frame coordinates.
+ */
+dvdnav_status_t dvdnav_mouse_select(dvdnav_t *self, pci_t *pci, int32_t x, int32_t y);
+
+/*
+ * Activate ("press") button at specified video frame coordinates.
+ */
+dvdnav_status_t dvdnav_mouse_activate(dvdnav_t *self, pci_t *pci, int32_t x, int32_t y);
+
+
+/*********************************************************************
+ * languages *
+ *********************************************************************/
+
+/*
+ * The language codes expected by these functions are two character
+ * codes as defined in ISO639.
+ */
+
+/*
+ * Set which menu language we should use per default.
+ */
+dvdnav_status_t dvdnav_menu_language_select(dvdnav_t *self,
+ char *code);
+
+/*
+ * Set which audio language we should use per default.
+ */
+dvdnav_status_t dvdnav_audio_language_select(dvdnav_t *self,
+ char *code);
+
+/*
+ * Set which spu language we should use per default.
+ */
+dvdnav_status_t dvdnav_spu_language_select(dvdnav_t *self,
+ char *code);
+
+
+/*********************************************************************
+ * obtaining stream attributes *
+ *********************************************************************/
+
+/*
+ * Return a string describing the title of the DVD.
+ * This is an ID string encoded on the disc by the author. In many cases
+ * this is a descriptive string such as `THE_MATRIX' but sometimes is sigularly
+ * uninformative such as `PDVD-011421'. Some DVD authors even forget to set this,
+ * so you may also read the default of the authoring software they used, like
+ * `DVDVolume'.
+ */
+dvdnav_status_t dvdnav_get_title_string(dvdnav_t *self, const char **title_str);
+
+/*
+ * Get video aspect code.
+ * The aspect code does only change on VTS boundaries.
+ * See the DVDNAV_VTS_CHANGE event.
+ *
+ * 0 -- 4:3, 2 -- 16:9
+ */
+uint8_t dvdnav_get_video_aspect(dvdnav_t *self);
+
+/*
+ * Get video scaling permissions.
+ * The scaling permission does only change on VTS boundaries.
+ * See the DVDNAV_VTS_CHANGE event.
+ *
+ * bit0 set = deny letterboxing, bit1 set = deny pan&scan
+ */
+uint8_t dvdnav_get_video_scale_permission(dvdnav_t *self);
+
+/*
+ * Converts a *logical* audio stream id into language code
+ * (returns 0xffff if no such stream).
+ */
+uint16_t dvdnav_audio_stream_to_lang(dvdnav_t *self, uint8_t stream);
+
+/*
+ * Converts a *logical* subpicture stream id into country code
+ * (returns 0xffff if no such stream).
+ */
+uint16_t dvdnav_spu_stream_to_lang(dvdnav_t *self, uint8_t stream);
+
+/*
+ * Converts a *physical* (MPEG) audio stream id into a logical stream number.
+ */
+int8_t dvdnav_get_audio_logical_stream(dvdnav_t *self, uint8_t audio_num);
+
+/*
+ * Converts a *physical* (MPEG) subpicture stream id into a logical stream number.
+ */
+int8_t dvdnav_get_spu_logical_stream(dvdnav_t *self, uint8_t subp_num);
+
+/*
+ * Get active audio stream.
+ */
+int8_t dvdnav_get_active_audio_stream(dvdnav_t *self);
+
+/*
+ * Get active spu stream.
+ */
+int8_t dvdnav_get_active_spu_stream(dvdnav_t *self);
+
+
+/*********************************************************************
+ * multiple angles *
+ *********************************************************************/
+
+/*
+ * The libdvdnav 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. You will always see only the
+ * selected angle coming from the get_next_block functions.
+ *
+ * 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.
+ */
+dvdnav_status_t dvdnav_angle_change(dvdnav_t *self, int32_t angle);
+
+/*
+ * Returns the current angle and number of angles present.
+ */
+dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *self, int32_t *current_angle,
+ int32_t *number_of_angles);
+
+/*********************************************************************
+ * domain queries *
+ *********************************************************************/
+
+/*
+ * Are we in the First Play domain?
+ */
+int8_t dvdnav_is_domain_fp(dvdnav_t *self);
+
+/*
+ * Are we in the Video management Menu domain?
+ */
+int8_t dvdnav_is_domain_vmgm(dvdnav_t *self);
+
+/*
+ * Are we in the Video Title Menu domain?
+ */
+int8_t dvdnav_is_domain_vtsm(dvdnav_t *self);
+
+/*
+ * Are we in the Video Title Set domain?
+ */
+int8_t dvdnav_is_domain_vts(dvdnav_t *self);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DVDNAV_H_INCLUDED */
diff --git a/libdvdnav/dvdnav_events.h b/libdvdnav/dvdnav_events.h
new file mode 100644
index 0000000..1623277
--- /dev/null
+++ b/libdvdnav/dvdnav_events.h
@@ -0,0 +1,241 @@
+/*
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * $Id: dvdnav_events.h,v 1.11 2003/04/07 18:10:48 mroi Exp $
+ *
+ */
+
+/*
+ * This header defines events and event types
+ */
+
+#ifndef DVDNAV_EVENTS_H_INCLUDED
+#define DVDNAV_EVENTS_H_INCLUDED
+
+#include "common.h"
+
+/*
+ * DVDNAV_BLOCK_OK
+ *
+ * A regular data block from the DVD has been returned.
+ * This one should be demuxed and decoded for playback.
+ */
+#define DVDNAV_BLOCK_OK 0
+
+
+/*
+ * DVDNAV_NOP
+ *
+ * Just ignore this.
+ */
+#define DVDNAV_NOP 1
+
+
+/*
+ * DVDNAV_STILL_FRAME
+ *
+ * We have reached a still frame. The player application should wait
+ * the amount of time specified by the still's length while still handling
+ * user input to make menus and other interactive stills work.
+ * The last delivered frame should be kept showing.
+ * Once the still has timed out, call dvdnav_skip_still().
+ * A length of 0xff means an infinite still which has to be skipped
+ * indirectly by some user interaction.
+ */
+#define DVDNAV_STILL_FRAME 2
+
+typedef struct {
+ /* The length (in seconds) the still frame should be displayed for,
+ * or 0xff if infinite. */
+ int length;
+} dvdnav_still_event_t;
+
+
+/*
+ * DVDNAV_SPU_STREAM_CHANGE
+ *
+ * Inform the SPU decoding/overlaying engine to switch SPU channels.
+ */
+#define DVDNAV_SPU_STREAM_CHANGE 3
+
+typedef struct {
+ /* The physical (MPEG) stream number for widescreen SPU display.
+ * Use this, if you blend the SPU on an anamorphic image before
+ * unsqueezing it. */
+ int physical_wide;
+
+ /* The physical (MPEG) stream number for letterboxed display.
+ * Use this, if you blend the SPU on an anamorphic image after
+ * unsqueezing it. */
+ int physical_letterbox;
+
+ /* The physical (MPEG) stream number for pan&scan display.
+ * Use this, if you blend the SPU on an anamorphic image after
+ * unsqueezing it the pan&scan way. */
+ int physical_pan_scan;
+
+ /* The logical (DVD) stream number. */
+ int logical;
+} dvdnav_spu_stream_change_event_t;
+
+
+/*
+ * DVDNAV_AUDIO_STREAM_CHANGE
+ *
+ * Inform the audio decoder to switch channels.
+ */
+#define DVDNAV_AUDIO_STREAM_CHANGE 4
+
+typedef struct {
+ /* The physical (MPEG) stream number. */
+ int physical;
+
+ /* The logical (DVD) stream number. */
+ int logical;
+} dvdnav_audio_stream_change_event_t;
+
+
+/*
+ * DVDNAV_VTS_CHANGE
+ *
+ * Some status information like video aspect and video scale permissions do
+ * not change inside a VTS. Therefore this event can be used to query such
+ * information only when necessary and update the decoding/displaying
+ * accordingly.
+ */
+#define DVDNAV_VTS_CHANGE 5
+
+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;
+
+
+/*
+ * DVDNAV_CELL_CHANGE
+ *
+ * Some status information like the current Title and Part numbers do not
+ * change inside a cell. Therefore this event can be used to query such
+ * information only when necessary and update the decoding/displaying
+ * accordingly.
+ * Some useful information for accurate time display is also reported
+ * together with this event.
+ */
+#define DVDNAV_CELL_CHANGE 6
+
+typedef struct {
+ int cellN; /* the new cell number */
+ int pgN; /* the current program number */
+ int64_t cell_length; /* the length of the current cell in PTS ticks */
+ int64_t pg_length; /* the length of the current program in PTS ticks */
+ int64_t pgc_length; /* the length of the current program chain in PTS ticks */
+ int64_t cell_start; /* the start time of the current cell relatively to the PGC in PTS ticks */
+ int64_t pg_start; /* the start time of the current PG relatively to the PGC in PTS ticks */
+} dvdnav_cell_change_event_t;
+
+
+/*
+ * DVDNAV_NAV_PACKET
+ *
+ * NAV packets are useful for various purposes. They define the button
+ * highlight areas and VM commands of DVD menus, so they should in any
+ * case be sent to the SPU decoder/overlaying engine for the menus to work.
+ * NAV packets also provide a way to detect PTS discontinuities, because
+ * they carry the start and end PTS values for the current VOBU.
+ * (pci.vobu_s_ptm and pci.vobu_e_ptm) Whenever the start PTS of the
+ * current NAV does not match the end PTS of the previous NAV, a PTS
+ * discontinuity has occured.
+ * NAV packets can also be used for time display, because they are
+ * timestamped relatively to the current Cell.
+ */
+#define DVDNAV_NAV_PACKET 7
+
+
+/*
+ * DVDNAV_STOP
+ *
+ * Applications should end playback here. A subsequent dvdnav_get_next_block()
+ * call will restart the VM from the beginning of the DVD.
+ */
+#define DVDNAV_STOP 8
+
+
+/*
+ * DVDNAV_HIGHLIGHT
+ *
+ * The current button highlight changed. Inform the overlaying engine to
+ * highlight a different button. Please note, that at the moment only mode 1
+ * highlights are reported this way. That means, when the button highlight
+ * has been moved around by some function call, you will receive an event
+ * telling you the new button. But when a button gets activated, you have
+ * to handle the mode 2 highlighting (that is some different colour the
+ * button turns to on activation) in your application.
+ */
+#define DVDNAV_HIGHLIGHT 9
+
+typedef struct {
+ /* highlight mode: 0 - hide, 1 - show, 2 - activate, currently always 1 */
+ int display;
+
+ /* FIXME: these fields are currently not set */
+ 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 */
+
+ /* button number for the SPU decoder/overlaying engine */
+ uint32_t buttonN;
+} dvdnav_highlight_event_t;
+
+
+/*
+ * DVDNAV_SPU_CLUT_CHANGE
+ *
+ * Inform the SPU decoder/overlaying engine to update its colour lookup table.
+ * The CLUT is given as 16 uint32_t's in the buffer.
+ */
+#define DVDNAV_SPU_CLUT_CHANGE 10
+
+
+/*
+ * DVDNAV_HOP_CHANNEL
+ *
+ * A non-seamless operation has been performed. Applications can drop all
+ * their internal fifo's content, which will speed up the response.
+ */
+#define DVDNAV_HOP_CHANNEL 12
+
+
+/*
+ * DVDNAV_WAIT
+ *
+ * We have reached a point in DVD playback, where timing is critical.
+ * Player application with internal fifos can introduce state
+ * inconsistencies, because libdvdnav is always the fifo's length
+ * ahead in the stream compared to what the application sees.
+ * Such applications should wait until their fifos are empty
+ * when they receive this type of event.
+ * Once this is achieved, call dvdnav_skip_wait().
+ */
+#define DVDNAV_WAIT 13
+
+
+#endif /* DVDNAV_EVENTS_H_INCLUDED */
diff --git a/libdvdnav/dvdnav_internal.h b/libdvdnav/dvdnav_internal.h
new file mode 100644
index 0000000..efa8708
--- /dev/null
+++ b/libdvdnav/dvdnav_internal.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2001-2004 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * $Id: dvdnav_internal.h,v 1.15 2004/09/20 19:30:04 valtri Exp $
+ *
+ */
+
+#ifndef DVDNAV_INTERNAL_H_INCLUDED
+#define DVDNAV_INTERNAL_H_INCLUDED
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "common.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include <string.h>
+
+#ifdef WIN32
+
+/* pthread_mutex_* wrapper for win32 */
+#include <windows.h>
+#include <process.h>
+typedef CRITICAL_SECTION pthread_mutex_t;
+#define pthread_mutex_init(a, b) InitializeCriticalSection(a)
+#define pthread_mutex_lock(a) EnterCriticalSection(a)
+#define pthread_mutex_unlock(a) LeaveCriticalSection(a)
+#define pthread_mutex_destroy(a)
+
+/* replacement gettimeofday implementation */
+#include <sys/timeb.h>
+static inline int _private_gettimeofday( struct timeval *tv, void *tz )
+{
+ struct timeb t;
+ ftime( &t );
+ tv->tv_sec = t.time;
+ tv->tv_usec = t.millitm * 1000;
+ return 0;
+}
+#define gettimeofday(TV, TZ) _private_gettimeofday((TV), (TZ))
+#include <io.h> /* read() */
+#define lseek64 _lseeki64
+
+#else
+
+#include <pthread.h>
+
+#endif /* WIN32 */
+
+/* Uncomment for VM command tracing */
+/* #define TRACE */
+
+#include "decoder.h"
+#include "dvdnav.h"
+#include "vm.h"
+#include "vmcmd.h"
+
+/* where should libdvdnav write its messages (stdout/stderr) */
+#define MSG_OUT stderr
+
+/* 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 /* Arbitrary */
+#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;
+ 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 */
+
+ /* Position data */
+ vm_position_t position_next;
+ vm_position_t position_current;
+ dvdnav_vobu_t vobu;
+
+ /* NAV data */
+ pci_t pci;
+ dsi_t dsi;
+ uint32_t last_cmd_nav_lbn; /* detects when a command is issued on an already left NAV */
+
+ /* Flags */
+ int skip_still; /* Set when skipping a still */
+ int sync_wait; /* applications should wait till they are in sync with us */
+ int sync_wait_skip; /* Set when skipping wait state */
+ 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 */
+ int pgc_based; /* positioning works PGC based instead of PG based */
+
+ /* VM */
+ vm_t *vm;
+ pthread_mutex_t vm_lock;
+
+ /* Read-ahead cache */
+ read_cache_t *cache;
+
+ /* Errors */
+ char err_str[MAX_ERR_LEN];
+};
+
+/** USEFUL MACROS **/
+
+#ifdef __GNUC__
+#define printerrf(format, args...) snprintf(this->err_str, MAX_ERR_LEN, format, ## args);
+#else
+#ifdef _MSC_VER
+#define printerrf(str) snprintf(this->err_str, MAX_ERR_LEN, str);
+#else
+#define printerrf(...) snprintf(this->err_str, MAX_ERR_LEN, __VA_ARGS__);
+#endif /* WIN32 */
+#endif
+#define printerr(str) strncpy(this->err_str, str, MAX_ERR_LEN);
+
+#endif /* DVDNAV_INTERNAL_H_INCLUDED */
diff --git a/libdvdnav/dvdread.c b/libdvdnav/dvdread.c
new file mode 100644
index 0000000..9291ff6
--- /dev/null
+++ b/libdvdnav/dvdread.c
@@ -0,0 +1,74 @@
+#include "dvdread.h"
+
+#include <dlfcn.h>
+
+void *libHandle=0;
+
+DvdreadFunctions_t f_dvdread;
+
+void loadDvdread() {
+ libHandle=dlopen("libdvdread.so",RTLD_LAZY | RTLD_GLOBAL);
+ if (!libHandle) {
+ libHandle=dlopen("libdvdread.so.3",RTLD_LAZY | RTLD_GLOBAL);
+ }
+ if (!libHandle) {
+ libHandle=dlopen("libdvdread.so.2",RTLD_LAZY | RTLD_GLOBAL);
+ }
+ f_dvdread.DVDOpen=dlsym(libHandle,"DVDOpen");
+ f_dvdread.DVDOpenFile=dlsym(libHandle,"DVDOpenFile");
+ f_dvdread.DVDClose=dlsym(libHandle,"DVDClose");
+ f_dvdread.DVDCloseFile=dlsym(libHandle,"DVDCloseFile");
+ f_dvdread.DVDReadBlocks=dlsym(libHandle,"DVDReadBlocks");
+ f_dvdread.DVDFileSeek=dlsym(libHandle,"DVDFileSeek");
+ f_dvdread.DVDReadBytes=dlsym(libHandle,"DVDReadBytes");
+ f_dvdread.DVDFileSize=dlsym(libHandle,"DVDFileSize");
+ f_dvdread.DVDDiscID=dlsym(libHandle,"DVDDiscID");
+ f_dvdread.DVDUDFVolumeInfo=dlsym(libHandle,"DVDUDFVolumeInfo");
+ f_dvdread.DVDISOVolumeInfo=dlsym(libHandle,"DVDISOVolumeInfo");
+ f_dvdread.DVDUDFCacheLevel=dlsym(libHandle,"DVDUDFCacheLevel");
+ f_dvdread.navRead_PCI=dlsym(libHandle,"navRead_PCI");
+ f_dvdread.navRead_DSI=dlsym(libHandle,"navRead_DSI");
+
+
+ f_dvdread.ifoOpen=dlsym(libHandle,"ifoOpen");
+ f_dvdread.ifoOpenVMGI=dlsym(libHandle,"ifoOpenVMGI");
+ f_dvdread.ifoOpenVTSI=dlsym(libHandle,"ifoOpenVTSI");
+ f_dvdread.ifoClose=dlsym(libHandle,"ifoClose");
+ f_dvdread.ifoRead_PTL_MAIT=dlsym(libHandle,"ifoRead_PTL_MAIT");
+ f_dvdread.ifoRead_VTS_ATRT=dlsym(libHandle,"ifoRead_VTS_ATRT");
+ f_dvdread.ifoRead_TT_SRPT=dlsym(libHandle,"ifoRead_TT_SRPT");
+ f_dvdread.ifoRead_VTS_PTT_SRPT=dlsym(libHandle,"ifoRead_VTS_PTT_SRPT");
+
+ f_dvdread.ifoRead_FP_PGC=dlsym(libHandle,"ifoRead_FP_PGC");
+ f_dvdread.ifoRead_PGCIT=dlsym(libHandle,"ifoRead_PGCIT");
+ f_dvdread.ifoRead_PGCI_UT=dlsym(libHandle,"ifoRead_PGCI_UT");
+ f_dvdread.ifoRead_VTS_TMAPT=dlsym(libHandle,"ifoRead_VTS_TMAPT");
+ f_dvdread.ifoRead_C_ADT=dlsym(libHandle,"ifoRead_C_ADT");
+ f_dvdread.ifoRead_TITLE_C_ADT=dlsym(libHandle,"ifoRead_TITLE_C_ADT");
+ f_dvdread.ifoRead_VOBU_ADMAP=dlsym(libHandle,"ifoRead_VOBU_ADMAP");
+ f_dvdread.ifoRead_TITLE_VOBU_ADMAP=dlsym(libHandle,"ifoRead_TITLE_VOBU_ADMAP");
+ f_dvdread.ifoRead_TXTDT_MGI=dlsym(libHandle,"ifoRead_TXTDT_MGI");
+
+ f_dvdread.ifoFree_PTL_MAIT=dlsym(libHandle,"ifoFree_PTL_MAIT");
+ f_dvdread.ifoFree_VTS_ATRT=dlsym(libHandle,"ifoFree_VTS_ATRT");
+ f_dvdread.ifoFree_TT_SRPT=dlsym(libHandle,"ifoFree_TT_SRPT");
+ f_dvdread.ifoFree_VTS_PTT_SRPT=dlsym(libHandle,"ifoFree_VTS_PTT_SRPT");
+ f_dvdread.ifoFree_FP_PGC=dlsym(libHandle,"ifoFree_FP_PGC");
+ f_dvdread.ifoFree_PGCIT=dlsym(libHandle,"ifoFree_PGCIT");
+ f_dvdread.ifoFree_PGCI_UT=dlsym(libHandle,"ifoFree_PGCI_UT");
+ f_dvdread.ifoFree_VTS_TMAPT=dlsym(libHandle,"ifoFree_VTS_TMAPT");
+ f_dvdread.ifoFree_C_ADT=dlsym(libHandle,"ifoFree_C_ADT");
+ f_dvdread.ifoFree_TITLE_C_ADT=dlsym(libHandle,"ifoFree_TITLE_C_ADT");
+ f_dvdread.ifoFree_VOBU_ADMAP=dlsym(libHandle,"ifoFree_VOBU_ADMAP");
+ f_dvdread.ifoFree_TITLE_VOBU_ADMAP=dlsym(libHandle,"ifoFree_TITLE_VOBU_ADMAP");
+ f_dvdread.ifoFree_TXTDT_MGI=dlsym(libHandle,"ifoFree_TXTDT_MGI");
+
+}
+
+DvdreadFunctions_t *DvdreadF(void) {
+ return &f_dvdread;
+}
+
+void unloadDvdread() {
+ dlclose(libHandle);
+}
diff --git a/libdvdnav/dvdread.h b/libdvdnav/dvdread.h
new file mode 100644
index 0000000..8150385
--- /dev/null
+++ b/libdvdnav/dvdread.h
@@ -0,0 +1,28 @@
+/*
+// C++ Interface: dvdread
+//
+// Description:
+//
+//
+// Author: Jean-Michel PETIT <copy@free.fr>, (C) 2006
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+*/
+#ifndef DVDREAD_H_INCLUDED
+#define DVDREAD_H_INCLUDED
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "common.h"
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/libdvdnav/highlight.c b/libdvdnav/highlight.c
new file mode 100644
index 0000000..0335c36
--- /dev/null
+++ b/libdvdnav/highlight.c
@@ -0,0 +1,512 @@
+/*
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * $Id: highlight.c,v 1.17 2004/03/16 11:43:38 mroi Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "common.h"
+#include <assert.h>
+#include "dvdnav_internal.h"
+
+/*
+#define BUTTON_TESTING
+*/
+
+#ifdef BUTTON_TESTING
+
+#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);
+
+ fprintf(MSG_OUT,"%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;
+ }
+ fprintf(MSG_OUT," @ %s fps", rate);
+}
+
+static void nav_print_PCI_GI(pci_gi_t *pci_gi) {
+ int32_t i;
+
+ fprintf(MSG_OUT,"libdvdnav: pci_gi:\n");
+ fprintf(MSG_OUT,"libdvdnav: nv_pck_lbn 0x%08x\n", pci_gi->nv_pck_lbn);
+ fprintf(MSG_OUT,"libdvdnav: vobu_cat 0x%04x\n", pci_gi->vobu_cat);
+ fprintf(MSG_OUT,"libdvdnav: vobu_uop_ctl 0x%08x\n", *(uint32_t*)&pci_gi->vobu_uop_ctl);
+ fprintf(MSG_OUT,"libdvdnav: vobu_s_ptm 0x%08x\n", pci_gi->vobu_s_ptm);
+ fprintf(MSG_OUT,"libdvdnav: vobu_e_ptm 0x%08x\n", pci_gi->vobu_e_ptm);
+ fprintf(MSG_OUT,"libdvdnav: vobu_se_e_ptm 0x%08x\n", pci_gi->vobu_se_e_ptm);
+ fprintf(MSG_OUT,"libdvdnav: e_eltm ");
+ print_time(&pci_gi->e_eltm);
+ fprintf(MSG_OUT,"\n");
+
+ fprintf(MSG_OUT,"libdvdnav: vobu_isrc \"");
+ for(i = 0; i < 32; i++) {
+ char c = pci_gi->vobu_isrc[i];
+ if((c >= ' ') && (c <= '~'))
+ fprintf(MSG_OUT,"%c", c);
+ else
+ fprintf(MSG_OUT,".");
+ }
+ fprintf(MSG_OUT,"\"\n");
+}
+
+static void nav_print_NSML_AGLI(nsml_agli_t *nsml_agli) {
+ int32_t i, j = 0;
+
+ for(i = 0; i < 9; i++)
+ j |= nsml_agli->nsml_agl_dsta[i];
+ if(j == 0)
+ return;
+
+ fprintf(MSG_OUT,"libdvdnav: nsml_agli:\n");
+ for(i = 0; i < 9; i++)
+ if(nsml_agli->nsml_agl_dsta[i])
+ fprintf(MSG_OUT,"libdvdnav: nsml_agl_c%d_dsta 0x%08x\n", i + 1,
+ nsml_agli->nsml_agl_dsta[i]);
+}
+
+static void nav_print_HL_GI(hl_gi_t *hl_gi, int32_t *btngr_ns, int32_t *btn_ns) {
+
+ if((hl_gi->hli_ss & 0x03) == 0)
+ return;
+
+ fprintf(MSG_OUT,"libdvdnav: hl_gi:\n");
+ fprintf(MSG_OUT,"libdvdnav: hli_ss 0x%01x\n", hl_gi->hli_ss & 0x03);
+ fprintf(MSG_OUT,"libdvdnav: hli_s_ptm 0x%08x\n", hl_gi->hli_s_ptm);
+ fprintf(MSG_OUT,"libdvdnav: hli_e_ptm 0x%08x\n", hl_gi->hli_e_ptm);
+ fprintf(MSG_OUT,"libdvdnav: btn_se_e_ptm 0x%08x\n", hl_gi->btn_se_e_ptm);
+
+ *btngr_ns = hl_gi->btngr_ns;
+ fprintf(MSG_OUT,"libdvdnav: btngr_ns %d\n", hl_gi->btngr_ns);
+ fprintf(MSG_OUT,"libdvdnav: btngr%d_dsp_ty 0x%02x\n", 1, hl_gi->btngr1_dsp_ty);
+ fprintf(MSG_OUT,"libdvdnav: btngr%d_dsp_ty 0x%02x\n", 2, hl_gi->btngr2_dsp_ty);
+ fprintf(MSG_OUT,"libdvdnav: btngr%d_dsp_ty 0x%02x\n", 3, hl_gi->btngr3_dsp_ty);
+
+ fprintf(MSG_OUT,"libdvdnav: btn_ofn %d\n", hl_gi->btn_ofn);
+ *btn_ns = hl_gi->btn_ns;
+ fprintf(MSG_OUT,"libdvdnav: btn_ns %d\n", hl_gi->btn_ns);
+ fprintf(MSG_OUT,"libdvdnav: nsl_btn_ns %d\n", hl_gi->nsl_btn_ns);
+ fprintf(MSG_OUT,"libdvdnav: fosl_btnn %d\n", hl_gi->fosl_btnn);
+ fprintf(MSG_OUT,"libdvdnav: foac_btnn %d\n", hl_gi->foac_btnn);
+}
+
+static void nav_print_BTN_COLIT(btn_colit_t *btn_colit) {
+ int32_t i, j;
+
+ j = 0;
+ for(i = 0; i < 6; i++)
+ j |= btn_colit->btn_coli[i/2][i&1];
+ if(j == 0)
+ return;
+
+ fprintf(MSG_OUT,"libdvdnav: btn_colit:\n");
+ for(i = 0; i < 3; i++)
+ for(j = 0; j < 2; j++)
+ fprintf(MSG_OUT,"libdvdnav: btn_cqoli %d %s_coli: %08x\n",
+ i, (j == 0) ? "sl" : "ac",
+ btn_colit->btn_coli[i][j]);
+}
+
+static void nav_print_BTNIT(btni_t *btni_table, int32_t btngr_ns, int32_t btn_ns) {
+ int32_t i, j, k;
+
+ fprintf(MSG_OUT,"libdvdnav: btnit:\n");
+ fprintf(MSG_OUT,"libdvdnav: btngr_ns: %i\n", btngr_ns);
+ fprintf(MSG_OUT,"libdvdnav: 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];
+
+ fprintf(MSG_OUT,"libdvdnav: group %d btni %d: ", i+1, j+1);
+ fprintf(MSG_OUT,"btn_coln %d, auto_action_mode %d\n",
+ btni->btn_coln, btni->auto_action_mode);
+ fprintf(MSG_OUT,"libdvdnav: coords (%d, %d) .. (%d, %d)\n",
+ btni->x_start, btni->y_start, btni->x_end, btni->y_end);
+
+ fprintf(MSG_OUT,"libdvdnav: up %d, ", btni->up);
+ fprintf(MSG_OUT,"down %d, ", btni->down);
+ fprintf(MSG_OUT,"left %d, ", btni->left);
+ fprintf(MSG_OUT,"right %d\n", btni->right);
+ for(k = 0; k < 8; k++) {
+ fprintf(MSG_OUT, "libdvdnav: %02x ", btni->cmd.bytes[k]);
+ }
+ fprintf(MSG_OUT, "| ");
+#ifdef TRACE
+ vm_print_mnemonic(&btni->cmd);
+#endif
+ fprintf(MSG_OUT, "\n");
+ }
+ }
+ }
+}
+
+static void nav_print_HLI(hli_t *hli) {
+ int32_t btngr_ns = 0, btn_ns = 0;
+
+ fprintf(MSG_OUT,"libdvdnav: hli:\n");
+ nav_print_HL_GI(&hli->hl_gi, & btngr_ns, & btn_ns);
+ nav_print_BTN_COLIT(&hli->btn_colit);
+ nav_print_BTNIT(hli->btnit, btngr_ns, btn_ns);
+}
+
+void nav_print_PCI(pci_t *pci) {
+ fprintf(MSG_OUT,"libdvdnav: pci packet:\n");
+ nav_print_PCI_GI(&pci->pci_gi);
+ nav_print_NSML_AGLI(&pci->nsml_agli);
+ nav_print_HLI(&pci->hli);
+}
+
+#endif
+
+
+/* Highlighting API calls */
+
+dvdnav_status_t dvdnav_get_current_highlight(dvdnav_t *this, int32_t *button) {
+
+ if(!this || !button) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ /* Simply return the appropriate value based on the SPRM */
+ if(((*button) = this->position_current.button) == -1)
+ (*button) = this->vm->state.HL_BTNN_REG >> 10;
+
+ return DVDNAV_STATUS_OK;
+}
+
+static btni_t *get_current_button(dvdnav_t *this, pci_t *pci) {
+ int32_t button = 0;
+
+ if(!this || !pci) {
+ printerr("Passed a NULL pointer.");
+ return NULL;
+ }
+ if(!pci->hli.hl_gi.hli_ss) {
+ printerr("Not in a menu.");
+ return NULL;
+ }
+ if(this->last_cmd_nav_lbn == pci->pci_gi.nv_pck_lbn) {
+ printerr("This NAV has already been left.");
+ return NULL;
+ }
+
+ button = this->vm->state.HL_BTNN_REG >> 10;
+#ifdef BUTTON_TESTING
+ nav_print_PCI(pci);
+#endif
+
+ return &(pci->hli.btnit[button-1]);
+}
+
+static dvdnav_status_t button_auto_action(dvdnav_t *this, pci_t *pci) {
+ if (get_current_button(this, pci)->auto_action_mode)
+ return dvdnav_button_activate(this, pci);
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_upper_button_select(dvdnav_t *this, pci_t *pci) {
+ btni_t *button_ptr;
+
+ if(!(button_ptr = get_current_button(this, pci)))
+ return DVDNAV_STATUS_ERR;
+
+ dvdnav_button_select(this, pci, button_ptr->up);
+ return button_auto_action(this, pci);
+}
+
+dvdnav_status_t dvdnav_lower_button_select(dvdnav_t *this, pci_t *pci) {
+ btni_t *button_ptr;
+
+ if(!(button_ptr = get_current_button(this, pci)))
+ return DVDNAV_STATUS_ERR;
+
+ dvdnav_button_select(this, pci, button_ptr->down);
+ return button_auto_action(this, pci);
+}
+
+dvdnav_status_t dvdnav_right_button_select(dvdnav_t *this, pci_t *pci) {
+ btni_t *button_ptr;
+
+ if(!(button_ptr = get_current_button(this, pci)))
+ return DVDNAV_STATUS_ERR;
+
+ dvdnav_button_select(this, pci, button_ptr->right);
+ return button_auto_action(this, pci);
+}
+
+dvdnav_status_t dvdnav_left_button_select(dvdnav_t *this, pci_t *pci) {
+ btni_t *button_ptr;
+
+ if(!(button_ptr = get_current_button(this, pci)))
+ return DVDNAV_STATUS_ERR;
+
+ dvdnav_button_select(this, pci, button_ptr->left);
+ return button_auto_action(this, pci);
+}
+
+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(MSG_OUT, "libdvdnav: Button get_highlight_area %i\n", button);
+#endif
+
+ if(!nav_pci->hli.hl_gi.hli_ss)
+ return DVDNAV_STATUS_ERR;
+ if((button <= 0) || (button > nav_pci->hli.hl_gi.btn_ns))
+ return DVDNAV_STATUS_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(MSG_OUT, "libdvdnav: highlight: 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 DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_button_activate(dvdnav_t *this, pci_t *pci) {
+ int32_t button;
+ btni_t *button_ptr = NULL;
+
+ if(!this || !pci) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+ if(!pci->hli.hl_gi.hli_ss) {
+ printerr("Not in a menu.");
+ return DVDNAV_STATUS_ERR;
+ }
+ if(this->last_cmd_nav_lbn == pci->pci_gi.nv_pck_lbn) {
+ printerr("This NAV has already been left.");
+ return DVDNAV_STATUS_ERR;
+ }
+ pthread_mutex_lock(&this->vm_lock);
+
+ button = this->vm->state.HL_BTNN_REG >> 10;
+
+ if((button <= 0) || (button > pci->hli.hl_gi.btn_ns)) {
+ /* Special code to handle still menus with no buttons.
+ * The navigation is expected to report to the application that a STILL is
+ * underway. In turn, the application is supposed to report to the user
+ * that the playback is paused. 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 Lacage <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;
+ this->sync_wait = 0;
+ this->last_cmd_nav_lbn = pci->pci_gi.nv_pck_lbn;
+ pthread_mutex_unlock(&this->vm_lock);
+ /* clear error message */
+ printerr("");
+ return DVDNAV_STATUS_OK;
+ }
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+ button_ptr = get_current_button(this, pci);
+ /* Finally, make the VM execute the appropriate code and probably
+ * scedule a jump */
+#ifdef BUTTON_TESTING
+ fprintf(MSG_OUT, "libdvdnav: Evaluating Button Activation commands.\n");
+#endif
+ if(vm_exec_cmd(this->vm, &(button_ptr->cmd)) == 1) {
+ /* Command caused a jump */
+ this->vm->hop_channel++;
+ this->position_current.still = 0;
+ this->last_cmd_nav_lbn = pci->pci_gi.nv_pck_lbn;
+ }
+
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_button_activate_cmd(dvdnav_t *this, int32_t button, vm_cmd_t *cmd)
+{
+ if(!this || !cmd) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ /* make the VM execute the appropriate code and probably
+ * schedule a jump */
+#ifdef BUTTON_TESTING
+ fprintf(MSG_OUT, "libdvdnav: dvdnav_button_activate_cmd: Evaluating Button Activation commands.\n");
+#endif
+ if(button > 0) {
+ this->vm->state.HL_BTNN_REG = (button << 10);
+ if(vm_exec_cmd(this->vm, cmd) == 1) {
+ /* Command caused a jump */
+ this->vm->hop_channel++;
+ }
+ }
+ /* Always remove still, because some still menus have no buttons. */
+ this->position_current.still = 0;
+ this->sync_wait = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_button_select(dvdnav_t *this, pci_t *pci, int32_t button) {
+
+ if(!this || !pci) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+ if(!pci->hli.hl_gi.hli_ss) {
+ printerr("Not in a menu.");
+ return DVDNAV_STATUS_ERR;
+ }
+ if(this->last_cmd_nav_lbn == pci->pci_gi.nv_pck_lbn) {
+ printerr("This NAV has already been left.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+#ifdef BUTTON_TESTING
+ fprintf(MSG_OUT, "libdvdnav: Button select %i\n", button);
+#endif
+
+ if((button <= 0) || (button > pci->hli.hl_gi.btn_ns)) {
+ printerr("Button does not exist.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ this->vm->state.HL_BTNN_REG = (button << 10);
+ this->position_current.button = -1; /* Force Highligh change */
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_button_select_and_activate(dvdnav_t *this, pci_t *pci,
+ int32_t button) {
+ /* A trivial function */
+ if(dvdnav_button_select(this, pci, button) != DVDNAV_STATUS_ERR)
+ return dvdnav_button_activate(this, pci);
+ return DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_mouse_select(dvdnav_t *this, pci_t *pci, int32_t x, int32_t y) {
+ int32_t button, cur_button;
+ int32_t best,dist,d;
+ int32_t mx,my,dx,dy;
+
+ if(!this || !pci) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+ if(!pci->hli.hl_gi.hli_ss) {
+ printerr("Not in a menu.");
+ return DVDNAV_STATUS_ERR;
+ }
+ if(this->last_cmd_nav_lbn == pci->pci_gi.nv_pck_lbn) {
+ printerr("This NAV has already been left.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ cur_button = this->vm->state.HL_BTNN_REG >> 10;
+
+ best = 0;
+ dist = 0x08000000; /* >> than (720*720)+(567*567); */
+
+ /* Loop through all buttons */
+ for(button = 1; button <= pci->hli.hl_gi.btn_ns; button++) {
+ btni_t *button_ptr = &(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;
+ }
+ }
+ }
+ /* As an efficiency measure, only re-select the button
+ * if it is different to the previously selected one. */
+ if (best != 0 && best != cur_button)
+ dvdnav_button_select(this, pci, best);
+
+ /* return DVDNAV_STATUS_OK only if we actually found a matching button */
+ return best ? DVDNAV_STATUS_OK : DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_mouse_activate(dvdnav_t *this, pci_t *pci, int32_t x, int32_t y) {
+ /* A trivial function */
+ if(dvdnav_mouse_select(this, pci, x,y) != DVDNAV_STATUS_ERR)
+ return dvdnav_button_activate(this, pci);
+ return DVDNAV_STATUS_ERR;
+}
diff --git a/libdvdnav/navigation.c b/libdvdnav/navigation.c
new file mode 100644
index 0000000..07d18d7
--- /dev/null
+++ b/libdvdnav/navigation.c
@@ -0,0 +1,231 @@
+/*
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * $Id: navigation.c,v 1.11 2004/06/20 16:59:13 mroi Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "dvdnav_internal.h"
+
+/* Navigation API calls */
+
+dvdnav_status_t dvdnav_still_skip(dvdnav_t *this) {
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ this->position_current.still = 0;
+ this->skip_still = 1;
+ this->sync_wait = 0;
+ this->sync_wait_skip = 1;
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_wait_skip(dvdnav_t *this) {
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ this->sync_wait = 0;
+ this->sync_wait_skip = 1;
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_get_number_of_titles(dvdnav_t *this, int32_t *titles) {
+ if(!this || !titles) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+ if (!this->vm->vmgi) {
+ printerr("Bad VM state.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ (*titles) = vm_get_vmgi(this->vm)->tt_srpt->nr_of_srpts;
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_get_number_of_parts(dvdnav_t *this, int32_t title, int32_t *parts) {
+ if(!this || !parts) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+ if (!this->vm->vmgi) {
+ printerr("Bad VM state.");
+ return DVDNAV_STATUS_ERR;
+ }
+ if ((title < 1) || (title > vm_get_vmgi(this->vm)->tt_srpt->nr_of_srpts) ) {
+ printerr("Passed a title number out of range.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ (*parts) = vm_get_vmgi(this->vm)->tt_srpt->title[title-1].nr_of_ptts;
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_current_title_info(dvdnav_t *this, int32_t *title, int32_t *part) {
+ int32_t retval;
+
+ if(!this || !title || !part) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ if (!this->vm->vtsi || !this->vm->vmgi) {
+ printerr("Bad VM state.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ if (!this->started) {
+ printerr("Virtual DVD machine not started.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ if (!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ if ( (this->vm->state.domain == VTSM_DOMAIN)
+ || (this->vm->state.domain == VMGM_DOMAIN) ) {
+ /* Get current Menu ID: into *part. */
+ vm_get_current_menu(this->vm, part);
+ if (*part > -1) {
+ *title = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+ }
+ if (this->vm->state.domain == VTS_DOMAIN) {
+ retval = vm_get_current_title_part(this->vm, title, part);
+ pthread_mutex_unlock(&this->vm_lock);
+ return retval ? DVDNAV_STATUS_OK : DVDNAV_STATUS_ERR;
+ }
+ printerr("Not in a title or menu.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_title_play(dvdnav_t *this, int32_t title) {
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+ return dvdnav_part_play(this, title, 1);
+}
+
+dvdnav_status_t dvdnav_part_play(dvdnav_t *this, int32_t title, int32_t part) {
+ int32_t retval;
+
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ if (!this->vm->vmgi) {
+ printerr("Bad VM state.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ if (!this->started) {
+ /* don't report an error but be nice */
+ vm_start(this->vm);
+ this->started = 1;
+ }
+ if (!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ if((title < 1) || (title > this->vm->vmgi->tt_srpt->nr_of_srpts)) {
+ printerr("Title out of range.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ if((part < 1) || (part > this->vm->vmgi->tt_srpt->title[title-1].nr_of_ptts)) {
+ printerr("Part out of range.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+ retval = vm_jump_title_part(this->vm, title, part);
+ if (retval)
+ this->vm->hop_channel++;
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return retval ? DVDNAV_STATUS_OK : DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_part_play_auto_stop(dvdnav_t *this, int32_t title,
+ int32_t part, int32_t parts_to_play) {
+ /* FIXME: Implement auto-stop */
+ if (dvdnav_part_play(this, title, part) == DVDNAV_STATUS_OK)
+ printerr("Not implemented yet.");
+ return DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_time_play(dvdnav_t *this, int32_t title,
+ uint64_t time) {
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ /* FIXME: Implement */
+ printerr("Not implemented yet.");
+ return DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_stop(dvdnav_t *this) {
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ this->vm->stopped = 1;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_go_up(dvdnav_t *this) {
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ /* A nice easy function... delegate to the VM */
+ pthread_mutex_lock(&this->vm_lock);
+ vm_jump_up(this->vm);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return DVDNAV_STATUS_OK;
+}
diff --git a/libdvdnav/read_cache.c b/libdvdnav/read_cache.c
new file mode 100644
index 0000000..1941c3c
--- /dev/null
+++ b/libdvdnav/read_cache.c
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ * 2001-2004 the dvdnav project
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * $Id: read_cache.c,v 1.12 2004/03/16 11:43:38 mroi Exp $
+ *
+ */
+/*
+ * There was a multithreaded read ahead cache in here for some time, but
+ * it had only been used for a short time. If you want to have a look at it,
+ * search the CVS attic.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "dvdnav.h"
+#include "dvdnav_internal.h"
+#include "read_cache.h"
+#include <sys/time.h>
+#include <time.h>
+#include "dvdread.h"
+#define READ_CACHE_CHUNKS 10
+
+/* all cache chunks must be memory aligned to allow use of raw devices */
+#define ALIGNMENT 2048
+
+#define READ_AHEAD_SIZE_MIN 4
+#define READ_AHEAD_SIZE_MAX 512
+
+typedef struct read_cache_chunk_s {
+ uint8_t *cache_buffer;
+ uint8_t *cache_buffer_base; /* used in malloc and free for alignment */
+ int32_t cache_start_sector; /* -1 means cache invalid */
+ int32_t cache_read_count; /* this many sectors are already read */
+ size_t cache_block_count; /* this many sectors will go in this chunk */
+ 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 */
+ uint32_t read_ahead_size;
+ int read_ahead_incr;
+ int last_sector;
+ pthread_mutex_t lock;
+
+ /* Bit of strange cross-linking going on here :) -- Gotta love C :) */
+ dvdnav_t *dvd_self;
+};
+
+/*
+#define READ_CACHE_TRACE 0
+*/
+
+#ifdef __GNUC__
+# if READ_CACHE_TRACE
+# define dprintf(fmt, args...) fprintf(MSG_OUT, "libdvdnav: %s: "fmt, __func__ , ## args)
+# else
+# define dprintf(fmt, args...) /* Nowt */
+# endif
+#else
+# if READ_CACHE_TRACE
+# define dprintf(fmt, ...) fprintf(MSG_OUT, "libdvdnav: %s: "fmt, __func__ , __VA_ARGS__)
+# else
+#ifdef _MSC_VER
+# define dprintf(fmt, str) /* Nowt */
+#else
+# define dprintf(fmt, ...) /* Nowt */
+#endif /* _MSC_VER */
+# endif
+#endif
+
+
+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;
+ self->last_sector = 0;
+ self->read_ahead_size = READ_AHEAD_SIZE_MIN;
+ self->read_ahead_incr = 0;
+ 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_base);
+ 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);
+}
+
+/* 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;
+
+ 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_base = realloc(self->chunk[use].cache_buffer_base,
+ block_count * DVD_VIDEO_LB_LEN + ALIGNMENT);
+ self->chunk[use].cache_buffer =
+ (uint8_t *)(((uintptr_t)self->chunk[use].cache_buffer_base & ~((uintptr_t)(ALIGNMENT - 1))) + ALIGNMENT);
+ 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_base =
+ malloc((block_count > 500 ? block_count : 500) * DVD_VIDEO_LB_LEN + ALIGNMENT);
+ self->chunk[i].cache_buffer =
+ (uint8_t *)(((uintptr_t)self->chunk[i].cache_buffer_base & ~((uintptr_t)(ALIGNMENT - 1))) + ALIGNMENT);
+ 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->chunk[use].cache_read_count = 0;
+ self->chunk[use].cache_valid = 1;
+ self->current = use;
+ } 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;
+ int start;
+ int size;
+ int incr;
+ uint8_t *read_ahead_buf;
+ int32_t res;
+
+ if(!self)
+ return 0;
+
+ 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 <= (cur.cache_start_sector + cur.cache_read_count) &&
+ 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 <= (self->chunk[i].cache_start_sector + self->chunk[i].cache_read_count) &&
+ sector + block_count <= self->chunk[i].cache_start_sector + self->chunk[i].cache_block_count)
+ use = i;
+ }
+
+ if (use >= 0) {
+ read_cache_chunk_t *chunk;
+
+ /* Increment read-ahead size if sector follows the last sector */
+ if (sector == (self->last_sector + 1)) {
+ if (self->read_ahead_incr < READ_AHEAD_SIZE_MAX)
+ self->read_ahead_incr++;
+ } else {
+ self->read_ahead_size = READ_AHEAD_SIZE_MIN;
+ self->read_ahead_incr = 0;
+ }
+ self->last_sector = sector;
+
+ /* The following resources need to be protected by a mutex :
+ * self->chunk[*].cache_buffer
+ * self->chunk[*].cache_malloc_size
+ * self->chunk[*].usage_count
+ */
+ pthread_mutex_lock(&self->lock);
+ chunk = &self->chunk[use];
+ read_ahead_buf = chunk->cache_buffer + chunk->cache_read_count * DVD_VIDEO_LB_LEN;
+ *buf = chunk->cache_buffer + (sector - chunk->cache_start_sector) * DVD_VIDEO_LB_LEN;
+ chunk->usage_count++;
+ pthread_mutex_unlock(&self->lock);
+
+ dprintf("libdvdnav: sector=%d, start_sector=%d, last_sector=%d\n", sector, chunk->cache_start_sector, chunk->cache_start_sector + chunk->cache_block_count);
+
+ /* read_ahead_size */
+ incr = self->read_ahead_incr >> 1;
+ if ((self->read_ahead_size + incr) > READ_AHEAD_SIZE_MAX) {
+ self->read_ahead_size = READ_AHEAD_SIZE_MAX;
+ } else {
+ self->read_ahead_size += incr;
+ }
+
+ /* real read size */
+ start = chunk->cache_start_sector + chunk->cache_read_count;
+ if (chunk->cache_read_count + self->read_ahead_size > chunk->cache_block_count) {
+ size = chunk->cache_block_count - chunk->cache_read_count;
+ } else {
+ size = self->read_ahead_size;
+ /* ensure that the sector we want will be read */
+ if (sector >= chunk->cache_start_sector + chunk->cache_read_count + size)
+ size = sector - chunk->cache_start_sector - chunk->cache_read_count;
+ }
+ dprintf("libdvdnav: read_ahead_size=%d, size=%d\n", self->read_ahead_size, size);
+
+ if (size)
+ chunk->cache_read_count += DVDReadBlocks(self->dvd_self->file,
+ start,
+ size,
+ read_ahead_buf);
+
+ res = DVD_VIDEO_LB_LEN * block_count;
+
+ } else {
+
+ if (self->dvd_self->use_read_ahead)
+ dprintf("cache miss on sector %d\n", sector);
+
+ res = DVDReadBlocks(self->dvd_self->file,
+ sector,
+ block_count,
+ *buf) * DVD_VIDEO_LB_LEN;
+ }
+
+ return res;
+
+}
+
+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;
+}
diff --git a/libdvdnav/read_cache.h b/libdvdnav/read_cache.h
new file mode 100644
index 0000000..857d676
--- /dev/null
+++ b/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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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/libdvdnav/remap.c b/libdvdnav/remap.c
new file mode 100644
index 0000000..1788b15
--- /dev/null
+++ b/libdvdnav/remap.c
@@ -0,0 +1,257 @@
+/*
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * $Id: remap.c,v 1.4 2003/08/25 21:51:40 f1rmb Exp $
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifndef _MSC_VER
+#include <sys/param.h>
+#include <sys/fcntl.h>
+#else
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 255
+#endif
+#endif /* _MSC_VER */
+
+#include <assert.h>
+#include "remap.h"
+#include "dvdnav_internal.h"
+
+struct block_s {
+ int domain;
+ int title;
+ int program;
+ unsigned long start_block;
+ unsigned long end_block;
+};
+
+struct remap_s {
+ char *title;
+ int maxblocks;
+ int nblocks;
+ int debug;
+ struct block_s *blocks;
+};
+
+static remap_t* remap_new( char *title) {
+ remap_t *map = malloc( sizeof(remap_t));
+ map->title = strdup(title);
+ map->maxblocks = 0;
+ map->nblocks = 0;
+ map->blocks = NULL;
+ map->debug = 0;
+ return map;
+}
+
+static int compare_block( block_t *a, block_t *b) {
+ /* returns -1 if a precedes b, 1 if a follows b, and 0 if a and b overlap */
+ if (a->domain < b->domain) {
+ return -1;
+ } else if (a->domain > b->domain) {
+ return 1;
+ }
+
+ if (a->title < b->title) {
+ return -1;
+ } else if (a->title > b->title) {
+ return 1;
+ }
+
+ if (a->program < b->program) {
+ return -1;
+ } else if (a->program > b->program) {
+ return 1;
+ }
+
+ if (a->end_block < b->start_block) {
+ return -1;
+ } else if (a->start_block > b->end_block) {
+ /*
+ * if a->start_block == b->end_block then the two regions
+ * aren't strictly overlapping, but they should be merged
+ * anyway since there are zero blocks between them
+ */
+ return 1;
+ }
+
+ return 0;
+}
+
+static block_t *findblock( remap_t *map, block_t *key) {
+ int lb = 0;
+ int ub = map->nblocks - 1;
+ int mid;
+ int res;
+
+ while (lb <= ub) {
+ mid = lb + (ub - lb)/2;
+ res = compare_block( key, &map->blocks[mid]);
+ if (res < 0) {
+ ub = mid-1;
+ } else if (res > 0) {
+ lb = mid+1;
+ } else {
+ return &map->blocks[mid];
+ }
+ }
+ return NULL;
+}
+
+static void mergeblock( block_t *b, block_t tmp) {
+ if (tmp.start_block < b->start_block) b->start_block = tmp.start_block;
+ if (tmp.end_block > b->end_block) b->end_block = tmp.end_block;
+}
+
+static void remap_add_node( remap_t *map, block_t block) {
+ block_t *b;
+ int n;
+ b = findblock( map, &block);
+ if (b) {
+ /* overlaps an existing block */
+ mergeblock( b, block);
+ } else {
+ /* new block */
+ if (map->nblocks >= map->maxblocks) {
+ map->maxblocks += 20;
+ map->blocks = realloc( map->blocks, sizeof( block_t)*map->maxblocks);
+ }
+ n = map->nblocks++;
+ while (n > 0 && compare_block( &block, &map->blocks[ n-1]) < 0) {
+ map->blocks[ n] = map->blocks[ n-1];
+ n--;
+ }
+ map->blocks[ n] = block;
+ }
+}
+
+static int parseblock(char *buf, int *dom, int *tt, int *pg,
+ unsigned long *start, unsigned long *end) {
+ long tmp;
+ char *tok;
+ char *epos;
+ char *marker[]={"domain", "title", "program", "start", "end"};
+ int st = 0;
+ tok = strtok( buf, " ");
+ while (st < 5) {
+ if (strcmp(tok, marker[st])) return -st-1000;
+ tok = strtok( NULL, " ");
+ if (!tok) return -st-2000;
+ tmp = strtol( tok, &epos, 0);
+ if (*epos != 0 && *epos != ',') return -st-3000;
+ switch (st) {
+ case 0:
+ *dom = (int)tmp;
+ break;
+ case 1:
+ *tt = (int)tmp;
+ break;
+ case 2:
+ *pg = (int)tmp;
+ break;
+ case 3:
+ *start = tmp;
+ break;
+ case 4:
+ *end = tmp;
+ break;
+ }
+ st++;
+ tok = strtok( NULL, " ");
+ }
+ return st;
+}
+
+remap_t* remap_loadmap( char *title) {
+ char buf[160];
+ char *fname;
+ char *home;
+ int res;
+ int fname_len=0;
+ FILE *fp;
+ block_t tmp;
+ remap_t *map;
+
+ /* Build the map filename */
+ home = getenv("HOME"); assert(home);
+ /*strncpy(fname, home, sizeof(fname));
+ strncat(fname, "/.dvdnav/", sizeof(fname));
+ strncat(fname, title, sizeof(fname));
+ strncat(fname, ".map", sizeof(fname));
+ */
+ fname_len = strlen(home)+strlen("/.dvdnav/")+strlen(title)+strlen(".map")+1;
+ fname = calloc(fname_len, sizeof(char));
+ snprintf(fname, fname_len, "%s%s%s%s", home, "/.dvdnav/", title, ".map");
+
+ /* Open the map file */
+ fp = fopen( fname, "r");
+ if (!fp) {
+ fprintf(MSG_OUT, "libdvdnav: Unable to find map file '%s'\n", fname);
+ return NULL;
+ }
+
+ /* Load the map file */
+ map = remap_new( title);
+ while (fgets( buf, sizeof(buf), fp) != NULL) {
+ if (buf[0] == '\n' || buf[0] == '#' || buf[0] == 0) continue;
+ if (strncasecmp( buf, "debug", 5) == 0) {
+ map->debug = 1;
+ } else {
+ res = parseblock( buf,
+ &tmp.domain, &tmp.title, &tmp.program, &tmp.start_block, &tmp.end_block);
+ if (res != 5) {
+ fprintf(MSG_OUT, "libdvdnav: Ignoring map line (%d): %s\n", res, buf);
+ continue;
+ }
+ remap_add_node( map, tmp);
+ }
+ }
+
+ free(fname);
+ if (map->nblocks == 0 && map->debug == 0) return NULL;
+ return map;
+}
+
+unsigned long remap_block(
+ remap_t *map, int domain, int title, int program,
+ unsigned long cblock, unsigned long offset)
+{
+ block_t key;
+ block_t *b;
+
+ if (map->debug) {
+ fprintf(MSG_OUT, "libdvdnav: %s: domain %d, title %d, program %d, start %lx, next %lx\n",
+ map->title, domain, title, program, cblock, cblock+offset);
+ }
+
+ key.domain = domain;
+ key.title = title;
+ key.program = program;
+ key.start_block = key.end_block = cblock + offset;
+ b = findblock( map, &key);
+
+ if (b) {
+ if (map->debug) {
+ fprintf(MSG_OUT, "libdvdnav: Redirected to %lx\n", b->end_block);
+ }
+ return b->end_block - cblock;
+ }
+ return offset;
+}
diff --git a/libdvdnav/remap.h b/libdvdnav/remap.h
new file mode 100644
index 0000000..88e06d2
--- /dev/null
+++ b/libdvdnav/remap.h
@@ -0,0 +1,33 @@
+/*
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * $Id: remap.h,v 1.2 2003/02/20 16:02:00 mroi Exp $
+ */
+
+#ifndef __REMAP__H
+#define __REMAP__H
+typedef struct block_s block_t;
+
+typedef struct remap_s remap_t;
+
+remap_t* remap_loadmap( char *title);
+
+unsigned long remap_block(
+ remap_t *map, int domain, int title, int program,
+ unsigned long cblock, unsigned long offset);
+
+#endif
diff --git a/libdvdnav/searching.c b/libdvdnav/searching.c
new file mode 100644
index 0000000..a069f51
--- /dev/null
+++ b/libdvdnav/searching.c
@@ -0,0 +1,567 @@
+/*
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * $Id: searching.c,v 1.19 2005/10/14 21:02:16 miguelfreitas Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include "dvdnav_internal.h"
+#include "dvdnav.h"
+
+/*
+#define LOG_DEBUG
+*/
+
+/* Searching API calls */
+
+/* Scan the ADMAP for a particular block number. */
+/* Return placed in vobu. */
+/* Returns error status */
+/* FIXME: Maybe need to handle seeking outside current cell. */
+static dvdnav_status_t dvdnav_scan_admap(dvdnav_t *this, int32_t domain, uint32_t seekto_block, uint32_t *vobu) {
+ vobu_admap_t *admap = NULL;
+
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: Seeking to target %u ...\n", seekto_block);
+#endif
+ *vobu = -1;
+
+ /* 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(MSG_OUT, "libdvdnav: Error: Unknown domain for seeking.\n");
+ }
+ if(admap) {
+ uint32_t address = 0;
+ uint32_t vobu_start, next_vobu;
+ int32_t found = 0;
+
+ /* Search through ADMAP for best sector */
+ vobu_start = SRI_END_OF_CELL;
+ /* FIXME: Implement a faster search algorithm */
+ while((!found) && ((address<<2) < admap->last_byte)) {
+ next_vobu = admap->vobu_start_sectors[address];
+
+ /* fprintf(MSG_OUT, "libdvdnav: 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 DVDNAV_STATUS_OK;
+ } else {
+ fprintf(MSG_OUT, "libdvdnav: Could not locate block\n");
+ return DVDNAV_STATUS_ERR;
+ }
+ }
+ fprintf(MSG_OUT, "libdvdnav: admap not located\n");
+ return DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_time_search(dvdnav_t *this,
+ uint64_t time) {
+
+ uint64_t target = time;
+ uint64_t length = 0;
+ uint32_t first_cell_nr, last_cell_nr, cell_nr;
+ int32_t found;
+ cell_playback_t *cell;
+ dvd_state_t *state;
+ dvdnav_status_t result;
+
+ if(this->position_current.still != 0) {
+ printerr("Cannot seek in a still frame.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ state = &(this->vm->state);
+ if(!state->pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+
+ if (this->pgc_based) {
+ first_cell_nr = 1;
+ last_cell_nr = state->pgc->nr_of_cells;
+ } else {
+ /* Find start cell of program. */
+ first_cell_nr = state->pgc->program_map[state->pgN-1];
+ /* Find end cell of program */
+ 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;
+ for(cell_nr = first_cell_nr; (cell_nr <= last_cell_nr) && !found; cell_nr ++) {
+ cell = &(state->pgc->cell_playback[cell_nr-1]);
+ length = dvdnav_convert_time(&cell->playback_time);
+ if (target >= length) {
+ target -= length;
+ } else {
+ /* FIXME: there must be a better way than interpolation */
+ target = target * (cell->last_sector - cell->first_sector + 1) / length;
+ target += cell->first_sector;
+
+ found = 1;
+ break;
+ }
+ }
+
+ if(found) {
+ int32_t vobu;
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: Seeking to cell %i from choice of %i to %i\n",
+ cell_nr, first_cell_nr, last_cell_nr);
+#endif
+ if (dvdnav_scan_admap(this, state->domain, target, &vobu) == DVDNAV_STATUS_OK) {
+ int32_t start = state->pgc->cell_playback[cell_nr-1].first_sector;
+
+ if (vm_jump_cell_block(this->vm, cell_nr, vobu - start)) {
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: After cellN=%u blockN=%u target=%x vobu=%x start=%x\n" ,
+ state->cellN, state->blockN, target, vobu, start);
+#endif
+ this->vm->hop_channel += HOP_SEEK;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+ }
+ }
+
+ fprintf(MSG_OUT, "libdvdnav: Error when seeking\n");
+ printerr("Error when seeking.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_sector_search(dvdnav_t *this,
+ uint64_t offset, int32_t origin) {
+ uint32_t target = 0;
+ uint32_t length = 0;
+ uint32_t first_cell_nr, last_cell_nr, cell_nr;
+ int32_t found;
+ cell_playback_t *cell;
+ dvd_state_t *state;
+ dvdnav_status_t result;
+
+ if(this->position_current.still != 0) {
+ printerr("Cannot seek in a still frame.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ result = dvdnav_get_position(this, &target, &length);
+ if(!result) {
+ return DVDNAV_STATUS_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ state = &(this->vm->state);
+ if(!state->pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: seeking to offset=%lu pos=%u length=%u\n", offset, target, length);
+ fprintf(MSG_OUT, "libdvdnav: Before cellN=%u blockN=%u\n", state->cellN, state->blockN);
+#endif
+
+ switch(origin) {
+ case SEEK_SET:
+ if(offset > length) {
+ printerr("Request to seek behind end.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ target = offset;
+ break;
+ case SEEK_CUR:
+ if(target + offset > length) {
+ printerr("Request to seek behind end.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ target += offset;
+ break;
+ case SEEK_END:
+ if(length - offset < 0) {
+ printerr("Request to seek before start.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ target = length - offset;
+ break;
+ default:
+ /* Error occured */
+ printerr("Illegal seek mode.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+ if (this->pgc_based) {
+ first_cell_nr = 1;
+ last_cell_nr = state->pgc->nr_of_cells;
+ } else {
+ /* Find start cell of program. */
+ first_cell_nr = state->pgc->program_map[state->pgN-1];
+ /* Find end cell of program */
+ 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;
+ for(cell_nr = first_cell_nr; (cell_nr <= last_cell_nr) && !found; cell_nr ++) {
+ cell = &(state->pgc->cell_playback[cell_nr-1]);
+ length = cell->last_sector - cell->first_sector + 1;
+ if (target >= length) {
+ target -= length;
+ } else {
+ /* convert the target sector from Cell-relative to absolute physical sector */
+ target += cell->first_sector;
+ found = 1;
+ break;
+ }
+ }
+
+ if(found) {
+ int32_t vobu;
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: Seeking to cell %i from choice of %i to %i\n",
+ cell_nr, first_cell_nr, last_cell_nr);
+#endif
+ if (dvdnav_scan_admap(this, state->domain, target, &vobu) == DVDNAV_STATUS_OK) {
+ int32_t start = state->pgc->cell_playback[cell_nr-1].first_sector;
+
+ if (vm_jump_cell_block(this->vm, cell_nr, vobu - start)) {
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: After cellN=%u blockN=%u target=%x vobu=%x start=%x\n" ,
+ state->cellN, state->blockN, target, vobu, start);
+#endif
+ this->vm->hop_channel += HOP_SEEK;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+ }
+ }
+
+ fprintf(MSG_OUT, "libdvdnav: Error when seeking\n");
+ fprintf(MSG_OUT, "libdvdnav: FIXME: Implement seeking to location %u\n", target);
+ printerr("Error when seeking.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_part_search(dvdnav_t *this, int32_t part) {
+ int32_t title, old_part;
+
+ if (dvdnav_current_title_info(this, &title, &old_part) == DVDNAV_STATUS_OK)
+ return dvdnav_part_play(this, title, part);
+ return DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_prev_pg_search(dvdnav_t *this) {
+
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ if(!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: previous chapter\n");
+#endif
+ if (!vm_jump_prev_pg(this->vm)) {
+ fprintf(MSG_OUT, "libdvdnav: previous chapter failed.\n");
+ printerr("Skip to previous chapter failed.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ this->position_current.still = 0;
+ this->vm->hop_channel++;
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: previous chapter done\n");
+#endif
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_top_pg_search(dvdnav_t *this) {
+
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ if(!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: top chapter\n");
+#endif
+ if (!vm_jump_top_pg(this->vm)) {
+ fprintf(MSG_OUT, "libdvdnav: top chapter failed.\n");
+ printerr("Skip to top chapter failed.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ this->position_current.still = 0;
+ this->vm->hop_channel++;
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: top chapter done\n");
+#endif
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *this) {
+ vm_t *try_vm;
+
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ if(!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: next chapter\n");
+#endif
+ /* make a copy of current VM and try to navigate the copy to the next PG */
+ try_vm = vm_new_copy(this->vm);
+ if (!vm_jump_next_pg(try_vm) || try_vm->stopped) {
+ vm_free_copy(try_vm);
+ /* next_pg failed, try to jump at least to the next cell */
+ try_vm = vm_new_copy(this->vm);
+ vm_get_next_cell(try_vm);
+ if (try_vm->stopped) {
+ vm_free_copy(try_vm);
+ fprintf(MSG_OUT, "libdvdnav: next chapter failed.\n");
+ printerr("Skip to next chapter failed.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ }
+ /* merge changes on success */
+ vm_merge(this->vm, try_vm);
+ vm_free_copy(try_vm);
+ this->position_current.still = 0;
+ this->vm->hop_channel++;
+#ifdef LOG_DEBUG
+ fprintf(MSG_OUT, "libdvdnav: next chapter done\n");
+#endif
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_menu_call(dvdnav_t *this, DVDMenuID_t menu) {
+ vm_t *try_vm;
+
+ if(!this) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ if(!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+ /* make a copy of current VM and try to navigate the copy to the menu */
+ try_vm = vm_new_copy(this->vm);
+ if ( (menu == DVD_MENU_Escape) && (this->vm->state.domain != VTS_DOMAIN)) {
+ /* Try resume */
+ if (vm_jump_resume(try_vm) && !try_vm->stopped) {
+ /* merge changes on success */
+ vm_merge(this->vm, try_vm);
+ vm_free_copy(try_vm);
+ this->position_current.still = 0;
+ this->vm->hop_channel++;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+ }
+ if (menu == DVD_MENU_Escape) menu = DVD_MENU_Root;
+
+ if (vm_jump_menu(try_vm, menu) && !try_vm->stopped) {
+ /* merge changes on success */
+ vm_merge(this->vm, try_vm);
+ vm_free_copy(try_vm);
+ this->position_current.still = 0;
+ this->vm->hop_channel++;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ } else {
+ vm_free_copy(try_vm);
+ printerr("No such menu or menu not reachable.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+}
+
+dvdnav_status_t dvdnav_get_position(dvdnav_t *this, uint32_t *pos,
+ uint32_t *len) {
+ uint32_t cur_sector;
+ int32_t cell_nr, first_cell_nr, last_cell_nr;
+ cell_playback_t *cell;
+ dvd_state_t *state;
+
+ if(!this || !pos || !len) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ state = &(this->vm->state);
+ if(!state->pgc || this->vm->stopped) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ if (this->position_current.hop_channel != this->vm->hop_channel ||
+ this->position_current.domain != state->domain ||
+ this->position_current.vts != state->vtsN ||
+ this->position_current.cell_restart != state->cell_restart) {
+ printerr("New position not yet determined.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+ /* Get current sector */
+ cur_sector = this->vobu.vobu_start + this->vobu.blockN;
+
+ if (this->pgc_based) {
+ first_cell_nr = 1;
+ last_cell_nr = state->pgc->nr_of_cells;
+ } else {
+ /* Find start cell of program. */
+ first_cell_nr = state->pgc->program_map[state->pgN-1];
+ /* Find end cell of program */
+ 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;
+ }
+
+ *pos = -1;
+ *len = 0;
+ for (cell_nr = first_cell_nr; cell_nr <= last_cell_nr; cell_nr++) {
+ cell = &(state->pgc->cell_playback[cell_nr-1]);
+ if (cell_nr == state->cellN) {
+ /* the current sector is in this cell,
+ * pos is length of PG up to here + sector's offset in this cell */
+ *pos = *len + cur_sector - cell->first_sector;
+ }
+ *len += cell->last_sector - cell->first_sector + 1;
+ }
+
+ assert((signed)*pos != -1);
+
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_get_position_in_title(dvdnav_t *this,
+ uint32_t *pos,
+ uint32_t *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 || !pos || !len) {
+ printerr("Passed a NULL pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ state = &(this->vm->state);
+ if(!state->pgc) {
+ printerr("No current PGC.");
+ return DVDNAV_STATUS_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 DVDNAV_STATUS_OK;
+}
diff --git a/libdvdnav/settings.c b/libdvdnav/settings.c
new file mode 100644
index 0000000..4af8aea
--- /dev/null
+++ b/libdvdnav/settings.c
@@ -0,0 +1,121 @@
+/*
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * $Id: settings.c,v 1.7 2004/03/16 11:43:38 mroi Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "dvdnav_internal.h"
+
+/* Characteristics/setting API calls */
+
+dvdnav_status_t dvdnav_get_region_mask(dvdnav_t *this, int32_t *region) {
+ if(!this || !region) {
+ printerr("Passed a NULL this pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ (*region) = this->vm->state.registers.SPRM[20];
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_set_region_mask(dvdnav_t *this, int32_t mask) {
+ if(!this) {
+ printerr("Passed a NULL this pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ this->vm->state.registers.SPRM[20] = (mask & 0xff);
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_set_readahead_flag(dvdnav_t *this, int32_t use_readahead) {
+ if(!this) {
+ printerr("Passed a NULL this pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ this->use_read_ahead = use_readahead;
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_get_readahead_flag(dvdnav_t *this, int32_t *flag) {
+ if(!this || !flag) {
+ printerr("Passed a NULL this pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ (*flag) = this->use_read_ahead;
+ return DVDNAV_STATUS_OK;
+}
+
+static dvdnav_status_t set_language_register(dvdnav_t *this, char *code, int reg) {
+ if(!this || !code) {
+ printerr("Passed a NULL this pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ if(!code[0] || !code[1]) {
+ printerr("Passed illegal language code.");
+ return DVDNAV_STATUS_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 DVDNAV_STATUS_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);
+}
+
+dvdnav_status_t dvdnav_set_PGC_positioning_flag(dvdnav_t *this, int32_t pgc) {
+ if(!this) {
+ printerr("Passed a NULL this pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ this->pgc_based = pgc;
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_get_PGC_positioning_flag(dvdnav_t *this, int32_t *flag) {
+ if(!this || !flag) {
+ printerr("Passed a NULL this pointer.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ (*flag) = this->pgc_based;
+ return DVDNAV_STATUS_OK;
+}
diff --git a/libdvdnav/vm.c b/libdvdnav/vm.c
new file mode 100644
index 0000000..dbebafa
--- /dev/null
+++ b/libdvdnav/vm.c
@@ -0,0 +1,1861 @@
+/*
+ * Copyright (C) 2000, 2001 H�an Hjort
+ * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+ * 2002-2004 the dvdnav project
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * $Id: vm.c,v 1.32 2004/12/20 19:27:20 mroi 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "dvdread.h"
+
+#include "dvdnav_internal.h"
+
+#ifdef _MSC_VER
+#include <io.h> /* read() */
+#endif /* _MSC_VER */
+
+/*
+#define STRICT
+*/
+
+/* Local prototypes */
+
+/* get_XYZ returns a value.
+ * set_XYZ sets state using passed parameters.
+ * returns success/failure.
+ */
+
+/* Play */
+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);
+
+/* Process link - returns 1 if a hop has been performed */
+static int process_command(vm_t *vm,link_t link_values);
+
+/* Set */
+static int set_TT(vm_t *vm, int tt);
+static int set_PTT(vm_t *vm, int tt, int ptt);
+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_FP_PGC(vm_t *vm);
+static int set_MENU(vm_t *vm, int menu);
+static int set_PGCN(vm_t *vm, int pgcN);
+static int set_PGN(vm_t *vm); /* Set PGN based on (vm->state).CellN */
+static void set_RSMinfo(vm_t *vm, int cellN, int blockN);
+
+/* Get */
+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 pgcit_t* get_MENU_PGCIT(vm_t *vm, ifo_handle_t *h, uint16_t lang);
+static pgcit_t* get_PGCIT(vm_t *vm);
+
+
+/* Helper functions */
+
+#ifdef TRACE
+static void vm_print_current_domain_state(vm_t *vm) {
+ switch((vm->state).domain) {
+ case VTS_DOMAIN:
+ fprintf(MSG_OUT, "libdvdnav: Video Title Domain: -\n");
+ break;
+
+ case VTSM_DOMAIN:
+ fprintf(MSG_OUT, "libdvdnav: Video Title Menu Domain: -\n");
+ break;
+
+ case VMGM_DOMAIN:
+ fprintf(MSG_OUT, "libdvdnav: Video Manager Menu Domain: -\n");
+ break;
+
+ case FP_DOMAIN:
+ fprintf(MSG_OUT, "libdvdnav: First Play Domain: -\n");
+ break;
+
+ default:
+ fprintf(MSG_OUT, "libdvdnav: Unknown Domain: -\n");
+ break;
+ }
+ fprintf(MSG_OUT, "libdvdnav: VTS:%d PGC:%d PG:%u CELL:%u BLOCK:%u VTS_TTN:%u TTN:%u TT_PGCN:%u\n",
+ (vm->state).vtsN,
+ get_PGCN(vm),
+ (vm->state).pgN,
+ (vm->state).cellN,
+ (vm->state).blockN,
+ (vm->state).VTS_TTN_REG,
+ (vm->state).TTN_REG,
+ (vm->state).TT_PGCN_REG);
+}
+#endif
+
+static void dvd_read_name(char *name, const char *device) {
+ /* Because we are compiling with _FILE_OFFSET_BITS=64
+ * all off_t are 64bit.
+ */
+ off_t off;
+ int fd, i;
+ uint8_t data[DVD_VIDEO_LB_LEN];
+
+ /* Read DVD name */
+ fd = open(device, O_RDONLY);
+ if (fd > 0) {
+ off = lseek( fd, 32 * (off_t) DVD_VIDEO_LB_LEN, SEEK_SET );
+ if( off == ( 32 * (off_t) DVD_VIDEO_LB_LEN ) ) {
+ off = read( fd, data, DVD_VIDEO_LB_LEN );
+ close(fd);
+ if (off == ( (off_t) DVD_VIDEO_LB_LEN )) {
+ fprintf(MSG_OUT, "libdvdnav: DVD Title: ");
+ for(i=25; i < 73; i++ ) {
+ if((data[i] == 0)) break;
+ if((data[i] > 32) && (data[i] < 127)) {
+ fprintf(MSG_OUT, "%c", data[i]);
+ } else {
+ fprintf(MSG_OUT, " ");
+ }
+ }
+ strncpy(name, &data[25], 48);
+ name[48] = 0;
+ fprintf(MSG_OUT, "\nlibdvdnav: DVD Serial Number: ");
+ for(i=73; i < 89; i++ ) {
+ if((data[i] == 0)) break;
+ if((data[i] > 32) && (data[i] < 127)) {
+ fprintf(MSG_OUT, "%c", data[i]);
+ } else {
+ fprintf(MSG_OUT, " ");
+ }
+ }
+ fprintf(MSG_OUT, "\nlibdvdnav: DVD Title (Alternative): ");
+ for(i=89; i < 128; i++ ) {
+ if((data[i] == 0)) break;
+ if((data[i] > 32) && (data[i] < 127)) {
+ fprintf(MSG_OUT, "%c", data[i]);
+ } else {
+ fprintf(MSG_OUT, " ");
+ }
+ }
+ fprintf(MSG_OUT, "\n");
+ } else {
+ fprintf(MSG_OUT, "libdvdnav: Can't read name block. Probably not a DVD-ROM device.\n");
+ }
+ } else {
+ fprintf(MSG_OUT, "libdvdnav: Can't seek to block %u\n", 32 );
+ }
+ close(fd);
+ } else {
+ fprintf(MSG_OUT, "NAME OPEN FAILED\n");
+ }
+}
+
+static int ifoOpenNewVTSI(vm_t *vm, dvd_reader_t *dvd, int vtsN) {
+ if((vm->state).vtsN == vtsN) {
+ return 1; /* We alread have it */
+ }
+
+ if(vm->vtsi != NULL)
+ ifoClose(vm->vtsi);
+
+ vm->vtsi = ifoOpenVTSI(dvd, vtsN);
+ if(vm->vtsi == NULL) {
+ fprintf(MSG_OUT, "libdvdnav: ifoOpenVTSI failed\n");
+ return 0;
+ }
+ if(!ifoRead_VTS_PTT_SRPT(vm->vtsi)) {
+ fprintf(MSG_OUT, "libdvdnav: ifoRead_VTS_PTT_SRPT failed\n");
+ return 0;
+ }
+ if(!ifoRead_PGCIT(vm->vtsi)) {
+ fprintf(MSG_OUT, "libdvdnav: ifoRead_PGCIT failed\n");
+ return 0;
+ }
+ if(!ifoRead_PGCI_UT(vm->vtsi)) {
+ fprintf(MSG_OUT, "libdvdnav: ifoRead_PGCI_UT failed\n");
+ return 0;
+ }
+ if(!ifoRead_VOBU_ADMAP(vm->vtsi)) {
+ fprintf(MSG_OUT, "libdvdnav: ifoRead_VOBU_ADMAP vtsi failed\n");
+ return 0;
+ }
+ if(!ifoRead_TITLE_VOBU_ADMAP(vm->vtsi)) {
+ fprintf(MSG_OUT, "libdvdnav: ifoRead_TITLE_VOBU_ADMAP vtsi failed\n");
+ return 0;
+ }
+ (vm->state).vtsN = vtsN;
+
+ return 1;
+}
+
+
+/* Initialisation & Destruction */
+
+vm_t* vm_new_vm() {
+ return (vm_t*)calloc(sizeof(vm_t), sizeof(char));
+}
+
+void vm_free_vm(vm_t *vm) {
+ vm_stop(vm);
+ free(vm);
+}
+
+
+/* IFO Access */
+
+ifo_handle_t *vm_get_vmgi(vm_t *vm) {
+ return vm->vmgi;
+}
+
+ifo_handle_t *vm_get_vtsi(vm_t *vm) {
+ return vm->vtsi;
+}
+
+
+/* Reader Access */
+
+dvd_reader_t *vm_get_dvd_reader(vm_t *vm) {
+ return vm->dvd;
+}
+
+
+/* Basic Handling */
+
+int vm_start(vm_t *vm) {
+ /* Set pgc to FP (First Play) pgc */
+ set_FP_PGC(vm);
+ process_command(vm, play_PGC(vm));
+ return !vm->stopped;
+}
+
+void vm_stop(vm_t *vm) {
+ if(vm->vmgi) {
+ ifoClose(vm->vmgi);
+ vm->vmgi=NULL;
+ }
+ if(vm->vtsi) {
+ ifoClose(vm->vtsi);
+ vm->vtsi=NULL;
+ }
+ if(vm->dvd) {
+ if (!vm->openedDvd)
+ DVDClose(vm->dvd);
+ vm->dvd=NULL;
+ }
+ vm->stopped = 1;
+}
+
+int vm_reset(vm_t *vm, const char *dvdroot) {
+ /* Setup State */
+ memset((vm->state).registers.SPRM, 0, sizeof((vm->state).registers.SPRM));
+ 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 */
+ (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) {
+ if (vm->openedDvd) /*JMP we use the dvd opened by copy*/
+ vm->dvd=vm->openedDvd;
+ else
+ vm->dvd = DVDOpen(dvdroot);
+ if(!vm->dvd) {
+ fprintf(MSG_OUT, "libdvdnav: vm: faild to open/read the DVD\n");
+ return 0;
+ }
+ dvd_read_name(vm->dvd_name, dvdroot);
+ vm->map = remap_loadmap(vm->dvd_name);
+ vm->vmgi = ifoOpenVMGI(vm->dvd);
+ if(!vm->vmgi) {
+ fprintf(MSG_OUT, "libdvdnav: vm: faild to read VIDEO_TS.IFO\n");
+ return 0;
+ }
+ if(!ifoRead_FP_PGC(vm->vmgi)) {
+ fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_FP_PGC failed\n");
+ return 0;
+ }
+ if(!ifoRead_TT_SRPT(vm->vmgi)) {
+ fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_TT_SRPT failed\n");
+ return 0;
+ }
+ if(!ifoRead_PGCI_UT(vm->vmgi)) {
+ fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_PGCI_UT failed\n");
+ return 0;
+ }
+ if(!ifoRead_PTL_MAIT(vm->vmgi)) {
+ fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_PTL_MAIT failed\n");
+ /* return 0; Not really used for now.. */
+ }
+ if(!ifoRead_VTS_ATRT(vm->vmgi)) {
+ fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_VTS_ATRT failed\n");
+ /* return 0; Not really used for now.. */
+ }
+ if(!ifoRead_VOBU_ADMAP(vm->vmgi)) {
+ fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_VOBU_ADMAP vgmi failed\n");
+ /* return 0; Not really used for now.. */
+ }
+ /* ifoRead_TXTDT_MGI(vmgi); Not implemented yet */
+ }
+ if (vm->vmgi) {
+ int i, mask;
+ fprintf(MSG_OUT, "libdvdnav: DVD disk reports itself with Region mask 0x%08x. Regions:",
+ vm->vmgi->vmgi_mat->vmg_category);
+ for (i = 1, mask = 1; i <= 8; i++, mask <<= 1)
+ if (((vm->vmgi->vmgi_mat->vmg_category >> 16) & mask) == 0)
+ fprintf(MSG_OUT, " %d", i);
+ fprintf(MSG_OUT, "\n");
+ }
+ return 1;
+}
+
+
+/* copying and merging */
+
+vm_t *vm_new_copy(vm_t *source) {
+ vm_t *target = vm_new_vm();
+ int vtsN;
+ int pgcN = get_PGCN(source);
+ int pgN = (source->state).pgN;
+
+ assert(pgcN);
+
+ memcpy(target, source, sizeof(vm_t));
+
+ /* open a new vtsi handle, because the copy might switch to another VTS */
+ target->vtsi = NULL;
+ vtsN = (target->state).vtsN;
+ if (vtsN > 0) {
+ (target->state).vtsN = 0;
+ if (!ifoOpenNewVTSI(target, target->dvd, vtsN))
+ assert(0);
+
+ /* restore pgc pointer into the new vtsi */
+ if (!set_PGCN(target, pgcN))
+ assert(0);
+ (target->state).pgN = pgN;
+ }
+
+ return target;
+}
+
+void vm_merge(vm_t *target, vm_t *source) {
+ if(target->vtsi)
+ ifoClose(target->vtsi);
+ memcpy(target, source, sizeof(vm_t));
+ memset(source, 0, sizeof(vm_t));
+}
+
+void vm_free_copy(vm_t *vm) {
+ if(vm->vtsi)
+ ifoClose(vm->vtsi);
+ free(vm);
+}
+
+
+/* regular playback */
+
+void vm_position_get(vm_t *vm, vm_position_t *position) {
+ position->button = (vm->state).HL_BTNN_REG >> 10;
+ position->vts = (vm->state).vtsN;
+ position->domain = (vm->state).domain;
+ 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->cell = (vm->state).cellN;
+ position->cell_restart = (vm->state).cell_restart;
+ position->cell_start = (vm->state).pgc->cell_playback[(vm->state).cellN - 1].first_sector;
+ position->still = (vm->state).pgc->cell_playback[(vm->state).cellN - 1].still_time;
+ position->block = (vm->state).blockN;
+
+ /* handle PGC stills at PGC end */
+ if ((vm->state).cellN == (vm->state).pgc->nr_of_cells)
+ position->still += (vm->state).pgc->still_time;
+ /* still already determined */
+ if (position->still)
+ return;
+ /* This is a rough fix for some strange still situations on some strange DVDs.
+ * There are discs (like the German "Back to the Future" RC2) where the only
+ * indication of a still is a cell playback time higher than the time the frames
+ * in this cell actually take to play (like 1 frame with 1 minute playback time).
+ * On the said BTTF disc, for these cells last_sector and last_vobu_start_sector
+ * are equal and the cells are very short, so we abuse these conditions to
+ * detect such discs. I consider these discs broken, so the fix is somewhat
+ * broken, too. */
+ if (((vm->state).pgc->cell_playback[(vm->state).cellN - 1].last_sector ==
+ (vm->state).pgc->cell_playback[(vm->state).cellN - 1].last_vobu_start_sector) &&
+ ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].last_sector -
+ (vm->state).pgc->cell_playback[(vm->state).cellN - 1].first_sector < 1024)) {
+ int time;
+ int size = (vm->state).pgc->cell_playback[(vm->state).cellN - 1].last_sector -
+ (vm->state).pgc->cell_playback[(vm->state).cellN - 1].first_sector;
+ time = ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.hour >> 4 ) * 36000;
+ time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.hour & 0x0f) * 3600;
+ time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.minute >> 4 ) * 600;
+ time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.minute & 0x0f) * 60;
+ time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.second >> 4 ) * 10;
+ time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.second & 0x0f) * 1;
+ if (!time || size / time > 30)
+ /* datarate is too high, it might be a very short, but regular cell */
+ return;
+ if (time > 0xff) time = 0xff;
+ position->still = time;
+ }
+}
+
+void vm_get_next_cell(vm_t *vm) {
+ process_command(vm, play_Cell_post(vm));
+}
+
+
+/* Jumping */
+
+int vm_jump_pg(vm_t *vm, int pg) {
+ (vm->state).pgN = pg;
+ process_command(vm, play_PG(vm));
+ return 1;
+}
+
+int vm_jump_cell_block(vm_t *vm, int cell, int block) {
+ (vm->state).cellN = cell;
+ process_command(vm, play_Cell(vm));
+ /* play_Cell can jump to a different cell in case of angles */
+ if ((vm->state).cellN == cell)
+ (vm->state).blockN = block;
+ return 1;
+}
+
+int vm_jump_title_part(vm_t *vm, int title, int part) {
+ link_t link;
+
+ if(!set_PTT(vm, title, part))
+ return 0;
+ /* Some DVDs do not want us to jump directly into a title and have
+ * PGC pre commands taking us back to some menu. Since we do not like that,
+ * we do not execute PGC pre commands that would do a jump. */
+ /* process_command(vm, play_PGC_PG(vm, (vm->state).pgN)); */
+ link = play_PGC_PG(vm, (vm->state).pgN);
+ if (link.command != PlayThis)
+ /* jump occured -> ignore it and play the PG anyway */
+ process_command(vm, play_PG(vm));
+ else
+ process_command(vm, link);
+ return 1;
+}
+
+int vm_jump_top_pg(vm_t *vm) {
+ process_command(vm, play_PG(vm));
+ return 1;
+}
+
+int vm_jump_next_pg(vm_t *vm) {
+ if((vm->state).pgN >= (vm->state).pgc->nr_of_programs) {
+ /* last program -> move to TailPGC */
+ process_command(vm, play_PGC_post(vm));
+ return 1;
+ } else {
+ vm_jump_pg(vm, (vm->state).pgN + 1);
+ return 1;
+ }
+}
+
+int vm_jump_prev_pg(vm_t *vm) {
+ if ((vm->state).pgN <= 1) {
+ /* first program -> move to last program of previous PGC */
+ if ((vm->state).pgc->prev_pgc_nr && set_PGCN(vm, (vm->state).pgc->prev_pgc_nr)) {
+ process_command(vm, play_PGC(vm));
+ vm_jump_pg(vm, (vm->state).pgc->nr_of_programs);
+ return 1;
+ }
+ return 0;
+ } else {
+ vm_jump_pg(vm, (vm->state).pgN - 1);
+ return 1;
+ }
+}
+
+int vm_jump_up(vm_t *vm) {
+ if((vm->state).pgc->goup_pgc_nr && set_PGCN(vm, (vm->state).pgc->goup_pgc_nr)) {
+ process_command(vm, play_PGC(vm));
+ return 1;
+ }
+ return 0;
+}
+
+int vm_jump_menu(vm_t *vm, DVDMenuID_t menuid) {
+ domain_t old_domain = (vm->state).domain;
+
+ switch ((vm->state).domain) {
+ case VTS_DOMAIN:
+ set_RSMinfo(vm, 0, (vm->state).blockN);
+ /* FALL THROUGH */
+ case VTSM_DOMAIN:
+ case VMGM_DOMAIN:
+ switch(menuid) {
+ case DVD_MENU_Title:
+ case DVD_MENU_Escape:
+ (vm->state).domain = VMGM_DOMAIN;
+ break;
+ case DVD_MENU_Root:
+ case DVD_MENU_Subpicture:
+ case DVD_MENU_Audio:
+ case DVD_MENU_Angle:
+ case DVD_MENU_Part:
+ (vm->state).domain = VTSM_DOMAIN;
+ break;
+ }
+ if(get_PGCIT(vm) && set_MENU(vm, menuid)) {
+ process_command(vm, play_PGC(vm));
+ 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_jump_resume(vm_t *vm) {
+ link_t link_values = { LinkRSM, 0, 0, 0 };
+
+ if (!(vm->state).rsm_vtsN) /* Do we have resume info? */
+ return 0;
+ if (!process_command(vm, link_values))
+ return 0;
+ return 1;
+}
+
+int vm_exec_cmd(vm_t *vm, vm_cmd_t *cmd) {
+ link_t link_values;
+
+ if(vmEval_CMD(cmd, 1, &(vm->state).registers, &link_values))
+ return process_command(vm, link_values);
+ else
+ return 0; /* It updated some state thats all... */
+}
+
+
+/* getting information */
+
+int vm_get_current_menu(vm_t *vm, int *menuid) {
+ pgcit_t* pgcit;
+ int pgcn;
+ pgcn = (vm->state).pgcN;
+ pgcit = get_PGCIT(vm);
+ *menuid = pgcit->pgci_srp[pgcn - 1].entry_id & 0xf ;
+ return 1;
+}
+
+int vm_get_current_title_part(vm_t *vm, int *title_result, int *part_result) {
+ vts_ptt_srpt_t *vts_ptt_srpt;
+ int title, part = 0, vts_ttn;
+ int found;
+ int16_t pgcN, pgN;
+
+ vts_ptt_srpt = vm->vtsi->vts_ptt_srpt;
+ pgcN = get_PGCN(vm);
+ pgN = vm->state.pgN;
+
+ found = 0;
+ for (vts_ttn = 0; (vts_ttn < vts_ptt_srpt->nr_of_srpts) && !found; vts_ttn++) {
+ for (part = 0; (part < vts_ptt_srpt->title[vts_ttn].nr_of_ptts) && !found; part++) {
+ if (vts_ptt_srpt->title[vts_ttn].ptt[part].pgcn == pgcN) {
+ if (vts_ptt_srpt->title[vts_ttn].ptt[part].pgn == pgN) {
+ found = 1;
+ break;
+ }
+ if (part > 0 && vts_ptt_srpt->title[vts_ttn].ptt[part].pgn > pgN &&
+ vts_ptt_srpt->title[vts_ttn].ptt[part - 1].pgn < pgN) {
+ part--;
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (found) break;
+ }
+ vts_ttn++;
+ part++;
+
+ if (!found) {
+ fprintf(MSG_OUT, "libdvdnav: chapter NOT FOUND!\n");
+ return 0;
+ }
+
+ title = get_TT(vm, vm->state.vtsN, vts_ttn);
+
+#ifdef TRACE
+ if (title) {
+ fprintf(MSG_OUT, "libdvdnav: ************ this chapter FOUND!\n");
+ fprintf(MSG_OUT, "libdvdnav: VTS_PTT_SRPT - Title %3i part %3i: PGC: %3i PG: %3i\n",
+ title, part,
+ vts_ptt_srpt->title[vts_ttn-1].ptt[part-1].pgcn ,
+ vts_ptt_srpt->title[vts_ttn-1].ptt[part-1].pgn );
+ }
+#endif
+ *title_result = title;
+ *part_result = part;
+ return 1;
+}
+
+/* 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;
+
+ if((vm->state).domain != VTS_DOMAIN)
+ audioN = 0;
+
+ if(audioN < 8) {
+ /* Is there any control 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 != VTS_DOMAIN && streamN == -1)
+ streamN = 0;
+
+ /* FIXME: Should also check in vtsi/vmgi status 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 != VTS_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;
+ }
+ }
+ }
+
+ if((vm->state).domain != VTS_DOMAIN && streamN == -1)
+ streamN = 0;
+
+ /* FIXME: Should also check in vtsi/vmgi status what kind of stream it is. */
+ 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)) {
+ if ((streamN = vm_get_audio_stream(vm, audioN)) >= 0)
+ break;
+ }
+ }
+ }
+
+ 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)) {
+ if ((streamN = vm_get_subp_stream(vm, subpN, mode)) >= 0)
+ break;
+ }
+ }
+ }
+
+ 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;
+}
+
+void vm_get_angle_info(vm_t *vm, int *current, int *num_avail) {
+ *num_avail = 1;
+ *current = 1;
+
+ if((vm->state).domain == VTS_DOMAIN) {
+ title_info_t *title;
+ /* TTN_REG does not allways point to the correct 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 0
+/* currently unused */
+void vm_get_audio_info(vm_t *vm, int *current, int *num_avail) {
+ switch ((vm->state).domain) {
+ case VTS_DOMAIN:
+ *num_avail = vm->vtsi->vtsi_mat->nr_of_vts_audio_streams;
+ *current = (vm->state).AST_REG;
+ break;
+ case VTSM_DOMAIN:
+ *num_avail = vm->vtsi->vtsi_mat->nr_of_vtsm_audio_streams; /* 1 */
+ *current = 1;
+ break;
+ case VMGM_DOMAIN:
+ case FP_DOMAIN:
+ *num_avail = vm->vmgi->vmgi_mat->nr_of_vmgm_audio_streams; /* 1 */
+ *current = 1;
+ break;
+ }
+}
+
+/* currently unused */
+void vm_get_subp_info(vm_t *vm, int *current, int *num_avail) {
+ switch ((vm->state).domain) {
+ case VTS_DOMAIN:
+ *num_avail = vm->vtsi->vtsi_mat->nr_of_vts_subp_streams;
+ *current = (vm->state).SPST_REG;
+ break;
+ case VTSM_DOMAIN:
+ *num_avail = vm->vtsi->vtsi_mat->nr_of_vtsm_subp_streams; /* 1 */
+ *current = 0x41;
+ break;
+ case VMGM_DOMAIN:
+ case FP_DOMAIN:
+ *num_avail = vm->vmgi->vmgi_mat->nr_of_vmgm_subp_streams; /* 1 */
+ *current = 0x41;
+ break;
+ }
+}
+
+/* currently unused */
+void vm_get_video_res(vm_t *vm, int *width, int *height) {
+ video_attr_t 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;
+ }
+}
+#endif
+
+int vm_get_video_aspect(vm_t *vm) {
+ int aspect = vm_get_video_attr(vm).display_aspect_ratio;
+
+ 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) {
+ return vm_get_video_attr(vm).permitted_df;
+}
+
+video_attr_t vm_get_video_attr(vm_t *vm) {
+ switch ((vm->state).domain) {
+ case VTS_DOMAIN:
+ return vm->vtsi->vtsi_mat->vts_video_attr;
+ case VTSM_DOMAIN:
+ return vm->vtsi->vtsi_mat->vtsm_video_attr;
+ case VMGM_DOMAIN:
+ case FP_DOMAIN:
+ return vm->vmgi->vmgi_mat->vmgm_video_attr;
+ default:
+ abort();
+ }
+}
+
+audio_attr_t vm_get_audio_attr(vm_t *vm, int streamN) {
+ switch ((vm->state).domain) {
+ case VTS_DOMAIN:
+ return vm->vtsi->vtsi_mat->vts_audio_attr[streamN];
+ case VTSM_DOMAIN:
+ return vm->vtsi->vtsi_mat->vtsm_audio_attr;
+ case VMGM_DOMAIN:
+ case FP_DOMAIN:
+ return vm->vmgi->vmgi_mat->vmgm_audio_attr;
+ default:
+ abort();
+ }
+}
+
+subp_attr_t vm_get_subp_attr(vm_t *vm, int streamN) {
+ switch ((vm->state).domain) {
+ case VTS_DOMAIN:
+ return vm->vtsi->vtsi_mat->vts_subp_attr[streamN];
+ case VTSM_DOMAIN:
+ return vm->vtsi->vtsi_mat->vtsm_subp_attr;
+ case VMGM_DOMAIN:
+ case FP_DOMAIN:
+ return vm->vmgi->vmgi_mat->vmgm_subp_attr;
+ default:
+ abort();
+ }
+}
+
+
+/* Playback control */
+
+static link_t play_PGC(vm_t *vm) {
+ link_t link_values;
+
+#ifdef TRACE
+ fprintf(MSG_OUT, "libdvdnav: play_PGC:");
+ if((vm->state).domain != FP_DOMAIN) {
+ fprintf(MSG_OUT, " (vm->state).pgcN (%i)\n", get_PGCN(vm));
+ } else {
+ fprintf(MSG_OUT, " 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;
+ (vm->state).blockN = 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 an 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(MSG_OUT, "libdvdnav: 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(MSG_OUT, "libdvdnav: play_PGC_PG:");
+ if((vm->state).domain != FP_DOMAIN) {
+ fprintf(MSG_OUT, " (vm->state).pgcN (%i)\n", get_PGCN(vm));
+ } else {
+ fprintf(MSG_OUT, " 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;
+ (vm->state).blockN = 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 an 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(MSG_OUT, "libdvdnav: PGC pre commands didn't do a Jump, Link or Call\n");
+#endif
+ }
+ }
+ return play_PG(vm);
+}
+
+static link_t play_PGC_post(vm_t *vm) {
+ link_t link_values;
+
+#ifdef TRACE
+ fprintf(MSG_OUT, "libdvdnav: play_PGC_post:\n");
+#endif
+
+ /* eval -> updates the state and returns either
+ - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN)
+ - just go to next PGC
+ (This is what happens if you fall of the end of the post_cmds)
+ - or an error (are there more cases?) */
+ if((vm->state).pgc->command_tbl && (vm->state).pgc->command_tbl->nr_of_post &&
+ 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;
+ }
+
+#ifdef TRACE
+ fprintf(MSG_OUT, "libdvdnav: ** Fell of the end of the pgc, continuing in NextPGC\n");
+#endif
+ /* Should end up in the STOP_DOMAIN if next_pgc is 0. */
+ if(!set_PGCN(vm, (vm->state).pgc->next_pgc_nr)) {
+ link_values.command = Exit;
+ return link_values;
+ }
+ return play_PGC(vm);
+}
+
+static link_t play_PG(vm_t *vm) {
+#ifdef TRACE
+ fprintf(MSG_OUT, "libdvdnav: 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(MSG_OUT, "libdvdnav: play_PG: (vm->state).pgN (%i) > pgc->nr_of_programs (%i)\n",
+ (vm->state).pgN, (vm->state).pgc->nr_of_programs );
+#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) {
+ static const link_t play_this = {PlayThis, /* Block in Cell */ 0, 0, 0};
+
+#ifdef TRACE
+ fprintf(MSG_OUT, "libdvdnav: 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(MSG_OUT, "libdvdnav: (vm->state).cellN (%i) > pgc->nr_of_cells (%i)\n",
+ (vm->state).cellN, (vm->state).pgc->nr_of_cells );
+#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);
+ break;
+ case 1: /* Angle block */
+ /* Loop and check each cell instead? So we don't get outside the block? */
+ (vm->state).cellN += (vm->state).AGL_REG - 1;
+#ifdef STRICT
+ 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);
+#else
+ if (!((vm->state).cellN <= (vm->state).pgc->nr_of_cells) ||
+ !((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode != 0) ||
+ !((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 1)) {
+ fprintf(MSG_OUT, "libdvdnav: Invalid angle block\n");
+ (vm->state).cellN -= (vm->state).AGL_REG - 1;
+ }
+#endif
+ break;
+ case 2: /* ?? */
+ case 3: /* ?? */
+ default:
+ fprintf(MSG_OUT, "libdvdnav: 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);
+ assert(0);
+ }
+ 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(MSG_OUT, "libdvdnav: 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 */
+ assert(0);
+ return play_PGC_post(vm);
+ }
+ (vm->state).cell_restart++;
+ (vm->state).blockN = 0;
+#ifdef TRACE
+ fprintf(MSG_OUT, "libdvdnav: Cell should restart here\n");
+#endif
+ return play_this;
+}
+
+static link_t play_Cell_post(vm_t *vm) {
+ cell_playback_t *cell;
+
+#ifdef TRACE
+ fprintf(MSG_OUT, "libdvdnav: 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;
+
+/* These asserts are now not needed.
+ * Some DVDs have no cell commands listed in the PGC,
+ * but the Cell itself points to a cell command that does not exist.
+ * For this situation, just ignore the cell command and continue.
+ *
+ * assert((vm->state).pgc->command_tbl != NULL);
+ * assert((vm->state).pgc->command_tbl->nr_of_cell >= cell->cell_cmd_nr);
+ */
+
+ if ((vm->state).pgc->command_tbl != NULL &&
+ (vm->state).pgc->command_tbl->nr_of_cell >= cell->cell_cmd_nr) {
+#ifdef TRACE
+ fprintf(MSG_OUT, "libdvdnav: Cell command present, 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 {
+#ifdef TRACE
+ fprintf(MSG_OUT, "libdvdnav: Cell command didn't do a Jump, Link or Call\n");
+#endif
+ }
+ } else {
+#ifdef TRACE
+ fprintf(MSG_OUT, "libdvdnav: Invalid Cell command\n");
+#endif
+ }
+ }
+
+ /* 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);
+ break;
+ 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(MSG_OUT, "libdvdnav: 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);
+ assert(0);
+ }
+ break;
+ }
+
+ /* Figure out the correct pgN for the new cell */
+ if(!set_PGN(vm)) {
+#ifdef TRACE
+ fprintf(MSG_OUT, "libdvdnav: last cell in this PGC\n");
+#endif
+ return play_PGC_post(vm);
+ }
+ return play_Cell(vm);
+}
+
+
+/* link processing */
+
+static int process_command(vm_t *vm, link_t link_values) {
+
+ while(link_values.command != PlayThis) {
+
+#ifdef TRACE
+ fprintf(MSG_OUT, "libdvdnav: Before printout starts:\n");
+ vm_print_link(link_values);
+ fprintf(MSG_OUT, "libdvdnav: Link values %i %i %i %i\n", link_values.command,
+ link_values.data1, link_values.data2, link_values.data3);
+ vm_print_current_domain_state(vm);
+ fprintf(MSG_OUT, "libdvdnav: Before printout ends.\n");
+#endif
+
+ switch(link_values.command) {
+ case LinkNoLink:
+ /* BUTTON number:data1 */
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ return 0; /* no actual jump */
+
+ case LinkTopC:
+ /* Restart playing from the beginning of the current Cell. */
+ /* BUTTON number:data1 */
+ 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;
+ 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;
+ assert((vm->state).cellN > 1);
+ (vm->state).cellN -= 1;
+ link_values = play_Cell(vm);
+ break;
+
+ case LinkTopPG:
+ /* Link to Top of current Program */
+ /* BUTTON number:data1 */
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ 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;
+ (vm->state).pgN += 1;
+ 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;
+ assert((vm->state).pgN > 1);
+ (vm->state).pgN -= 1;
+ link_values = play_PG(vm);
+ break;
+
+ case LinkTopPGC:
+ /* Restart playing from beginning of current Program Chain */
+ /* BUTTON number:data1 */
+ 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_PGCN(vm, (vm->state).pgc->next_pgc_nr))
+ link_values = play_PGC(vm);
+ else
+ link_values.command = Exit;
+ 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_PGCN(vm, (vm->state).pgc->prev_pgc_nr))
+ link_values = play_PGC(vm);
+ else
+ link_values.command = Exit;
+ 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_PGCN(vm, (vm->state).pgc->goup_pgc_nr))
+ link_values = play_PGC(vm);
+ else
+ link_values.command = Exit;
+ break;
+ case LinkTailPGC:
+ /* Link to Tail of Program Chain */
+ /* BUTTON number:data1 */
+ 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 point */
+ int i;
+
+ /* Check and see if there is any rsm info!! */
+ if (!(vm->state).rsm_vtsN) {
+ fprintf(MSG_OUT, "libdvdnav: trying to resume without any resume info set\n");
+ link_values.command = Exit;
+ break;
+ }
+
+ (vm->state).domain = VTS_DOMAIN;
+ if (!ifoOpenNewVTSI(vm, vm->dvd, (vm->state).rsm_vtsN))
+ assert(0);
+ set_PGCN(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 */
+ (vm->state).pgN = 1;
+ link_values = play_PG(vm);
+ } else {
+ /* (vm->state).pgN = ?? this gets the right value in set_PGN() below */
+ (vm->state).cellN = (vm->state).rsm_cellN;
+ link_values.command = PlayThis;
+ link_values.data1 = (vm->state).rsm_blockN & 0xffff;
+ link_values.data2 = (vm->state).rsm_blockN >> 16;
+ 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_PGCN(vm, link_values.data1))
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case LinkPTTN:
+ /* Link to Part of current Title Number:data1 */
+ /* BUTTON number:data2 */
+ /* PGC Pre-Commands are not executed */
+ 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))
+ 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:
+ vm->stopped = 1;
+ return 0;
+
+ case JumpTT:
+ /* Jump to VTS Title Domain */
+ /* Only allowed from the First Play domain(PGC) */
+ /* or the Video Manager domain (VMG) */
+ /* Stop SPRM9 Timer */
+ /* Set SPRM1 and SPRM2 */
+ assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN); /* ?? */
+ if(set_TT(vm, link_values.data1))
+ link_values = play_PGC(vm);
+ else
+ link_values.command = Exit;
+ 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) */
+ /* Stop SPRM9 Timer */
+ /* Set SPRM1 and SPRM2 */
+ assert((vm->state).domain == VTSM_DOMAIN || (vm->state).domain == VTS_DOMAIN); /* ?? */
+ if(!set_VTS_TT(vm, (vm->state).vtsN, link_values.data1))
+ 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) */
+ /* Stop SPRM9 Timer */
+ /* Set SPRM1 and SPRM2 */
+ 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))
+ assert(0);
+ link_values = play_PGC_PG(vm, (vm->state).pgN);
+ break;
+
+ case JumpSS_FP:
+ /* Jump to First Play Domain */
+ /* Only allowed from the VTS Menu Domain(VTSM) */
+ /* or the Video Manager domain (VMG) */
+ /* Stop SPRM9 Timer and any GPRM counters */
+ assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == VTSM_DOMAIN); /* ?? */
+ if (!set_FP_PGC(vm))
+ assert(0);
+ 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 */
+ /* Stop SPRM9 Timer and any GPRM counters */
+ assert((vm->state).domain != VTS_DOMAIN); /* ?? */
+ (vm->state).domain = VMGM_DOMAIN;
+ if(!set_MENU(vm, link_values.data1))
+ 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 */
+ /* Stop SPRM9 Timer and any GPRM counters */
+ /* ifoOpenNewVTSI:data1 */
+ /* VTS_TTN_REG:data2 */
+ /* get_MENU:data3 */
+ if(link_values.data1 != 0) {
+ if (link_values.data1 != (vm->state).vtsN) {
+ /* the normal case */
+ assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN); /* ?? */
+ (vm->state).domain = VTSM_DOMAIN;
+ if (!ifoOpenNewVTSI(vm, vm->dvd, link_values.data1)) /* Also sets (vm->state).vtsN */
+ assert(0);
+ } else {
+ /* This happens on some discs like "Captain Scarlet & the Mysterons" or
+ * the German RC2 of "Anatomie" in VTSM. */
+ assert((vm->state).domain == VTSM_DOMAIN ||
+ (vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN); /* ?? */
+ (vm->state).domain = VTSM_DOMAIN;
+ }
+ } 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;
+ /* TTN_REG (SPRM4), VTS_TTN_REG (SPRM5), TT_PGCN_REG (SPRM6) are linked, */
+ /* so if one changes, the others must change to match it. */
+ (vm->state).TTN_REG = get_TT(vm, (vm->state).vtsN, (vm->state).VTS_TTN_REG);
+ if(!set_MENU(vm, link_values.data3))
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case JumpSS_VMGM_PGC:
+ /* set_PGCN:data1 */
+ /* Stop SPRM9 Timer and any GPRM counters */
+ assert((vm->state).domain != VTS_DOMAIN); /* ?? */
+ (vm->state).domain = VMGM_DOMAIN;
+ if(!set_PGCN(vm, link_values.data1))
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+
+ case CallSS_FP:
+ /* set_RSMinfo:data1 */
+ assert((vm->state).domain == VTS_DOMAIN); /* ?? */
+ /* Must be called before domain is changed */
+ set_RSMinfo(vm, link_values.data1, /* We dont have block info */ 0);
+ set_FP_PGC(vm);
+ link_values = play_PGC(vm);
+ break;
+ case CallSS_VMGM_MENU:
+ /* set_MENU:data1 */
+ /* set_RSMinfo:data2 */
+ assert((vm->state).domain == VTS_DOMAIN); /* ?? */
+ /* Must be called before domain is changed */
+ set_RSMinfo(vm, link_values.data2, /* We dont have block info */ 0);
+ (vm->state).domain = VMGM_DOMAIN;
+ if(!set_MENU(vm, link_values.data1))
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case CallSS_VTSM:
+ /* set_MENU:data1 */
+ /* set_RSMinfo:data2 */
+ assert((vm->state).domain == VTS_DOMAIN); /* ?? */
+ /* Must be called before domain is changed */
+ set_RSMinfo(vm, link_values.data2, /* We dont have block info */ 0);
+ (vm->state).domain = VTSM_DOMAIN;
+ if(!set_MENU(vm, link_values.data1))
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case CallSS_VMGM_PGC:
+ /* set_PGC:data1 */
+ /* set_RSMinfo:data2 */
+ assert((vm->state).domain == VTS_DOMAIN); /* ?? */
+ /* Must be called before domain is changed */
+ set_RSMinfo(vm, link_values.data2, /* We dont have block info */ 0);
+ (vm->state).domain = VMGM_DOMAIN;
+ if(!set_PGCN(vm, link_values.data1))
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case PlayThis:
+ /* Should never happen. */
+ assert(0);
+ break;
+ }
+
+#ifdef TRACE
+ fprintf(MSG_OUT, "libdvdnav: After printout starts:\n");
+ vm_print_current_domain_state(vm);
+ fprintf(MSG_OUT, "libdvdnav: After printout ends.\n");
+#endif
+
+ }
+ (vm->state).blockN = link_values.data1 | (link_values.data2 << 16);
+ return 1;
+}
+
+
+/* Set functions */
+
+static int set_TT(vm_t *vm, int tt) {
+ return set_PTT(vm, tt, 1);
+}
+
+static int set_PTT(vm_t *vm, int tt, int ptt) {
+ assert(tt <= vm->vmgi->tt_srpt->nr_of_srpts);
+ return set_VTS_PTT(vm, vm->vmgi->tt_srpt->title[tt - 1].title_set_nr,
+ vm->vmgi->tt_srpt->title[tt - 1].vts_ttn, ptt);
+}
+
+static int set_VTS_TT(vm_t *vm, int vtsN, int vts_ttn) {
+ return set_VTS_PTT(vm, vtsN, vts_ttn, 1);
+}
+
+static int set_VTS_PTT(vm_t *vm, int vtsN, int vts_ttn, int part) {
+ int pgcN, pgN, res;
+
+ (vm->state).domain = VTS_DOMAIN;
+
+ if (vtsN != (vm->state).vtsN)
+ if (!ifoOpenNewVTSI(vm, vm->dvd, vtsN)) /* Also sets (vm->state).vtsN */
+ return 0;
+
+ if ((vts_ttn < 1) || (vts_ttn > vm->vtsi->vts_ptt_srpt->nr_of_srpts) ||
+ (part < 1) || (part > vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].nr_of_ptts) ) {
+ return 0;
+ }
+
+ 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 = part;
+ (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_PGCN(vm, pgcN); /* This clobber's state.pgN (sets it to 1), but we don't want clobbering here. */
+ (vm->state).pgN = pgN;
+ return res;
+}
+
+static int set_FP_PGC(vm_t *vm) {
+ (vm->state).domain = FP_DOMAIN;
+ if (!vm->vmgi->first_play_pgc) {
+ return set_PGCN(vm, 1);
+ }
+ (vm->state).pgc = vm->vmgi->first_play_pgc;
+ (vm->state).pgcN = vm->vmgi->vmgi_mat->first_play_pgc;
+ return 1;
+}
+
+
+static int set_MENU(vm_t *vm, int menu) {
+ assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == VTSM_DOMAIN);
+ return set_PGCN(vm, get_ID(vm, menu));
+}
+
+static int set_PGCN(vm_t *vm, int pgcN) {
+ pgcit_t *pgcit;
+
+ pgcit = get_PGCIT(vm);
+ assert(pgcit != NULL); /* ?? Make this return -1 instead */
+
+ if(pgcN < 1 || pgcN > pgcit->nr_of_pgci_srp) {
+#ifdef TRACE
+ fprintf(MSG_OUT, "libdvdnav: ** No such pgcN = %d\n", pgcN);
+#endif
+ return 0;
+ }
+
+ (vm->state).pgc = pgcit->pgci_srp[pgcN - 1].pgc;
+ (vm->state).pgcN = pgcN;
+ (vm->state).pgN = 1;
+
+ if((vm->state).domain == VTS_DOMAIN)
+ (vm->state).TT_PGCN_REG = pgcN;
+
+ return 1;
+}
+
+/* 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 0; /* 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) {
+ int dummy, part;
+ vm_get_current_title_part(vm, &dummy, &part);
+ (vm->state).PTTN_REG = part;
+ } else {
+ /* FIXME: Handle RANDOM or SHUFFLE titles. */
+ fprintf(MSG_OUT, "libdvdnav: RANDOM or SHUFFLE titles are NOT handled yet.\n");
+ }
+ }
+ return 1;
+}
+
+/* Must be called before domain is changed (set_PGCN()) */
+static void set_RSMinfo(vm_t *vm, int cellN, int blockN) {
+ int i;
+
+ if(cellN) {
+ (vm->state).rsm_cellN = cellN;
+ (vm->state).rsm_blockN = blockN;
+ } 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];
+ }
+}
+
+
+/* Get functions */
+
+/* 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;
+}
+
+/* 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);
+#ifdef TRACE
+ fprintf(MSG_OUT, "libdvdnav: ** Searching for menu (0x%x) entry PGC\n", id);
+#endif
+
+ /* Force high bit set. */
+ id |=0x80;
+
+ /* Get menu/title */
+ for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+ if( (pgcit->pgci_srp[i].entry_id) == id) {
+ pgcN = i + 1;
+#ifdef TRACE
+ fprintf(MSG_OUT, "libdvdnav: Found menu.\n");
+#endif
+ return pgcN;
+ }
+ }
+#ifdef TRACE
+ fprintf(MSG_OUT, "libdvdnav: ** No such id/menu (0x%02x) entry PGC\n", id & 0x7f);
+ for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+ if ( (pgcit->pgci_srp[i].entry_id & 0x80) == 0x80) {
+ fprintf(MSG_OUT, "libdvdnav: Available menus: 0x%x\n",
+ pgcit->pgci_srp[i].entry_id & 0x7f);
+ }
+ }
+#endif
+ return 0; /* error */
+}
+
+/* FIXME: we have a pgcN member in the vm's state now, so this should be obsolete */
+static int get_PGCN(vm_t *vm) {
+ pgcit_t *pgcit;
+ int pgcN = 1;
+
+ pgcit = get_PGCIT(vm);
+
+ if (pgcit) {
+ while(pgcN <= pgcit->nr_of_pgci_srp) {
+ if(pgcit->pgci_srp[pgcN - 1].pgc == (vm->state).pgc) {
+ assert((vm->state).pgcN == pgcN);
+ return pgcN;
+ }
+ pgcN++;
+ }
+ }
+ fprintf(MSG_OUT, "libdvdnav: get_PGCN failed. Was trying to find pgcN in domain %d\n",
+ (vm->state).domain);
+ return 0; /* error */
+}
+
+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(MSG_OUT, "libdvdnav: *** 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(MSG_OUT, "libdvdnav: 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(MSG_OUT, "libdvdnav: Menu Languages available: ");
+ for(i = 0; i < h->pgci_ut->nr_of_lus; i++) {
+ fprintf(MSG_OUT, "%c%c ",
+ (char)(h->pgci_ut->lu[i].lang_code >> 8),
+ (char)(h->pgci_ut->lu[i].lang_code & 0xff));
+ }
+ fprintf(MSG_OUT, "\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:
+ case FP_DOMAIN:
+ pgcit = get_MENU_PGCIT(vm, vm->vmgi, (vm->state).registers.SPRM[0]);
+ break;
+ default:
+ abort();
+ }
+
+ return pgcit;
+}
+
+
+/* Debug functions */
+
+#ifdef TRACE
+void vm_position_print(vm_t *vm, vm_position_t *position) {
+ fprintf(MSG_OUT, "libdvdnav: But=%x Spu=%x Aud=%x Ang=%x Hop=%x vts=%x dom=%x cell=%x cell_restart=%x cell_start=%x still=%x block=%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->cell_start,
+ position->still,
+ position->block);
+}
+#endif
+
diff --git a/libdvdnav/vm.h b/libdvdnav/vm.h
new file mode 100644
index 0000000..e853182
--- /dev/null
+++ b/libdvdnav/vm.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2000, 2001 H�an 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * $Id: vm.h,v 1.11 2004/10/05 19:07:10 hadess Exp $
+ *
+ */
+
+#ifndef VM_H_INCLUDED
+#define VM_H_INCLUDED
+
+#include "remap.h"
+#include "dvdnav_internal.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;
+
+ domain_t domain;
+ int vtsN; /* 0 is vmgm? */
+ pgc_t *pgc; /* either this or 'int pgcN' is enough? */
+ int pgcN; /* but provide pgcN for quick lookup */
+ 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 */
+ int32_t vts; /* vts number to use */
+ domain_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 */
+#if 0
+ /* currently unused */
+ int32_t title; /* title number */
+ int32_t chapter; /* chapter number */
+#endif
+ int32_t cell; /* cell number */
+ int32_t cell_restart; /* get cell to restart */
+ int32_t cell_start; /* sector number of start of current cell in use */
+ int32_t still; /* is cell still */
+ int32_t block; /* block number within cell in use */
+} vm_position_t;
+
+typedef struct {
+ dvd_reader_t *dvd,*openedDvd;
+ ifo_handle_t *vmgi;
+ ifo_handle_t *vtsi;
+ dvd_state_t state;
+ int32_t hop_channel;
+ char dvd_name[50];
+ remap_t *map;
+ int stopped;
+} vm_t;
+
+/* magic number for seeking hops */
+#define HOP_SEEK 0x1000
+
+
+/* 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);
+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);
+
+/* Basic Handling */
+int vm_start(vm_t *vm);
+void vm_stop(vm_t *vm);
+int vm_reset(vm_t *vm, const char *dvdroot);
+
+/* copying and merging - useful for try-running an operation */
+vm_t *vm_new_copy(vm_t *vm);
+void vm_merge(vm_t *target, vm_t *source);
+void vm_free_copy(vm_t *vm);
+
+/* regular playback */
+void vm_position_get(vm_t *vm, vm_position_t *position);
+void vm_get_next_cell(vm_t *vm);
+
+/* Jumping - all these return 1, if a hop has been performed */
+int vm_jump_pg(vm_t *vm, int pg);
+int vm_jump_cell_block(vm_t *vm, int cell, int block);
+int vm_jump_title_part(vm_t *vm, int title, int part);
+int vm_jump_top_pg(vm_t *vm);
+int vm_jump_next_pg(vm_t *vm);
+int vm_jump_prev_pg(vm_t *vm);
+int vm_jump_up(vm_t *vm);
+int vm_jump_menu(vm_t *vm, DVDMenuID_t menuid);
+int vm_jump_resume(vm_t *vm);
+int vm_exec_cmd(vm_t *vm, vm_cmd_t *cmd);
+
+/* getting information */
+int vm_get_current_menu(vm_t *vm, int *menuid);
+int vm_get_current_title_part(vm_t *vm, int *title_result, int *part_result);
+int vm_get_audio_stream(vm_t *vm, int audioN);
+int vm_get_subp_stream(vm_t *vm, int subpN, int mode);
+int vm_get_audio_active_stream(vm_t *vm);
+int vm_get_subp_active_stream(vm_t *vm, int mode);
+void vm_get_angle_info(vm_t *vm, int *current, int *num_avail);
+#if 0
+/* currently unused */
+void vm_get_audio_info(vm_t *vm, int *current, int *num_avail);
+void vm_get_subp_info(vm_t *vm, int *current, int *num_avail);
+void vm_get_video_res(vm_t *vm, int *width, int *height);
+#endif
+int vm_get_video_aspect(vm_t *vm);
+int vm_get_video_scale_permission(vm_t *vm);
+video_attr_t vm_get_video_attr(vm_t *vm);
+audio_attr_t vm_get_audio_attr(vm_t *vm, int streamN);
+subp_attr_t vm_get_subp_attr(vm_t *vm, int streamN);
+
+#ifdef TRACE
+/* Debug */
+void vm_position_print(vm_t *vm, vm_position_t *position);
+#endif
+
+
+#endif /* VM_HV_INCLUDED */
diff --git a/libdvdnav/vmcmd.c b/libdvdnav/vmcmd.c
new file mode 100644
index 0000000..76f0a4b
--- /dev/null
+++ b/libdvdnav/vmcmd.c
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) 2000, 2001 Martin Norb�k, H�an Hjort
+ * 2002-2004 the dvdnav project
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * $Id: vmcmd.c,v 1.6 2004/03/16 11:43:38 mroi Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <inttypes.h>
+
+#include "dvdnav_internal.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(MSG_OUT, "%s (SRPM:%d)", system_reg_table[reg], reg);
+ else
+ fprintf(MSG_OUT, " WARNING: Unknown system register ( reg=%d ) ", reg);
+}
+
+static void print_g_reg(uint8_t reg) {
+ if(reg < 16)
+ fprintf(MSG_OUT, "g[%" PRIu8 "]", reg);
+ else
+ fprintf(MSG_OUT, " WARNING: Unknown general register ");
+}
+
+static void print_reg(uint8_t reg) {
+ if(reg & 0x80)
+ print_system_reg(reg & 0x7f);
+ else
+ print_g_reg(reg & 0x7f);
+}
+
+static void print_cmp_op(uint8_t op) {
+ if(op < sizeof(cmp_op_table) / sizeof(char *) && cmp_op_table[op] != NULL)
+ fprintf(MSG_OUT, " %s ", cmp_op_table[op]);
+ else
+ fprintf(MSG_OUT, " 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(MSG_OUT, " %s ", set_op_table[op]);
+ else
+ fprintf(MSG_OUT, " WARNING: Unknown set op ");
+}
+
+static void print_reg_or_data(command_t* command, int immediate, int start) {
+ if(immediate) {
+ uint32_t i = vm_getbits(command, start, 16);
+
+ fprintf(MSG_OUT, "0x%x", i);
+ if(isprint(i & 0xff) && isprint((i>>8) & 0xff))
+ fprintf(MSG_OUT, " (\"%c%c\")", (char)((i>>8) & 0xff), (char)(i & 0xff));
+ } else {
+ print_reg(vm_getbits(command, start - 8, 8));
+ }
+}
+
+static void print_reg_or_data_2(command_t* command, int immediate, int start) {
+ if(immediate)
+ fprintf(MSG_OUT, "0x%x", vm_getbits(command, start - 1, 7));
+ else
+ fprintf(MSG_OUT, "g[%" PRIu8 "]", vm_getbits(command, start - 4, 4));
+}
+
+static void print_reg_or_data_3(command_t* command, int immediate, int start) {
+ if(immediate) {
+ uint32_t i = vm_getbits(command, start, 16);
+
+ fprintf(MSG_OUT, "0x%x", i);
+ if(isprint(i & 0xff) && isprint((i>>8) & 0xff))
+ fprintf(MSG_OUT, " (\"%c%c\")", (char)((i>>8) & 0xff), (char)(i & 0xff));
+ } else {
+ print_reg(vm_getbits(command, start, 8));
+ }
+}
+
+
+static void print_if_version_1(command_t* command) {
+ uint8_t op = vm_getbits(command, 54, 3);
+
+ if(op) {
+ fprintf(MSG_OUT, "if (");
+ print_g_reg(vm_getbits(command,39,8));
+ print_cmp_op(op);
+ print_reg_or_data(command, vm_getbits(command, 55,1), 31);
+ fprintf(MSG_OUT, ") ");
+ }
+}
+
+static void print_if_version_2(command_t* command) {
+ uint8_t op = vm_getbits(command, 54, 3);
+
+ if(op) {
+ fprintf(MSG_OUT, "if (");
+ print_reg(vm_getbits(command, 15, 8));
+ print_cmp_op(op);
+ print_reg(vm_getbits(command, 7, 8));
+ fprintf(MSG_OUT, ") ");
+ }
+}
+
+static void print_if_version_3(command_t* command) {
+ uint8_t op = vm_getbits(command, 54, 3);
+
+ if(op) {
+ fprintf(MSG_OUT, "if (");
+ print_g_reg(vm_getbits(command, 43, 4));
+ print_cmp_op(op);
+ print_reg_or_data(command, vm_getbits(command, 55, 1), 15);
+ fprintf(MSG_OUT, ") ");
+ }
+}
+
+static void print_if_version_4(command_t* command) {
+ uint8_t op = vm_getbits(command, 54, 3);
+
+ if(op) {
+ fprintf(MSG_OUT, "if (");
+ print_g_reg(vm_getbits(command, 51, 4));
+ print_cmp_op(op);
+ print_reg_or_data(command, vm_getbits(command, 55, 1), 31);
+ fprintf(MSG_OUT, ") ");
+ }
+}
+
+static void print_if_version_5(command_t* command) {
+ uint8_t op = vm_getbits(command, 54, 3);
+ int set_immediate = vm_getbits(command, 60, 1);
+
+ if(op) {
+ if (set_immediate) {
+ fprintf(MSG_OUT, "if (");
+ print_g_reg(vm_getbits(command, 31, 8));
+ print_cmp_op(op);
+ print_reg(vm_getbits(command, 23, 8));
+ fprintf(MSG_OUT, ") ");
+ } else {
+ fprintf(MSG_OUT, "if (");
+ print_g_reg(vm_getbits(command, 39, 8));
+ print_cmp_op(op);
+ print_reg_or_data(command, vm_getbits(command, 55, 1), 31);
+ fprintf(MSG_OUT, ") ");
+ }
+ }
+}
+
+static void print_special_instruction(command_t* command) {
+ uint8_t op = vm_getbits(command, 51, 4);
+
+ switch(op) {
+ case 0: /* NOP */
+ fprintf(MSG_OUT, "Nop");
+ break;
+ case 1: /* Goto line */
+ fprintf(MSG_OUT, "Goto %" PRIu8, vm_getbits(command, 7, 8));
+ break;
+ case 2: /* Break */
+ fprintf(MSG_OUT, "Break");
+ break;
+ case 3: /* Parental level */
+ fprintf(MSG_OUT, "SetTmpPML %" PRIu8 ", Goto %" PRIu8,
+ vm_getbits(command, 11, 4), vm_getbits(command, 7, 8));
+ break;
+ default:
+ fprintf(MSG_OUT, "WARNING: Unknown special instruction (%i)",
+ vm_getbits(command, 51, 4));
+ }
+}
+
+static void print_linksub_instruction(command_t* command) {
+ uint32_t linkop = vm_getbits(command, 7, 8);
+ uint32_t button = vm_getbits(command, 15, 6);
+
+ if(linkop < sizeof(link_table)/sizeof(char *) && link_table[linkop] != NULL)
+ fprintf(MSG_OUT, "%s (button %" PRIu8 ")", link_table[linkop], button);
+ else
+ fprintf(MSG_OUT, "WARNING: Unknown linksub instruction (%i)", linkop);
+}
+
+static void print_link_instruction(command_t* command, int optional) {
+ uint8_t op = vm_getbits(command, 51, 4);
+
+ if(optional && op)
+ fprintf(MSG_OUT, ", ");
+
+ switch(op) {
+ case 0:
+ if(!optional)
+ fprintf(MSG_OUT, "WARNING: NOP (link)!");
+ break;
+ case 1:
+ print_linksub_instruction(command);
+ break;
+ case 4:
+ fprintf(MSG_OUT, "LinkPGCN %" PRIu16, vm_getbits(command, 14, 15));
+ break;
+ case 5:
+ fprintf(MSG_OUT, "LinkPTT %" PRIu16 " (button %" PRIu8 ")",
+ vm_getbits(command, 9, 10), vm_getbits(command, 15, 6));
+ break;
+ case 6:
+ fprintf(MSG_OUT, "LinkPGN %" PRIu8 " (button %" PRIu8 ")",
+ vm_getbits(command, 6, 7), vm_getbits(command, 15, 6));
+ break;
+ case 7:
+ fprintf(MSG_OUT, "LinkCN %" PRIu8 " (button %" PRIu8 ")",
+ vm_getbits(command, 7, 8), vm_getbits(command, 15, 6));
+ break;
+ default:
+ fprintf(MSG_OUT, "WARNING: Unknown link instruction");
+ }
+}
+
+static void print_jump_instruction(command_t* command) {
+ switch(vm_getbits(command, 51, 4)) {
+ case 1:
+ fprintf(MSG_OUT, "Exit");
+ break;
+ case 2:
+ fprintf(MSG_OUT, "JumpTT %" PRIu8, vm_getbits(command, 22, 7));
+ break;
+ case 3:
+ fprintf(MSG_OUT, "JumpVTS_TT %" PRIu8, vm_getbits(command, 22, 7));
+ break;
+ case 5:
+ fprintf(MSG_OUT, "JumpVTS_PTT %" PRIu8 ":%" PRIu16,
+ vm_getbits(command, 22, 7), vm_getbits(command, 41, 10));
+ break;
+ case 6:
+ switch(vm_getbits(command, 23, 2)) {
+ case 0:
+ fprintf(MSG_OUT, "JumpSS FP");
+ break;
+ case 1:
+ fprintf(MSG_OUT, "JumpSS VMGM (menu %" PRIu8 ")", vm_getbits(command, 19, 4));
+ break;
+ case 2:
+ fprintf(MSG_OUT, "JumpSS VTSM (vts %" PRIu8 ", title %" PRIu8
+ ", menu %" PRIu8 ")", vm_getbits(command, 30, 7), vm_getbits(command, 38, 7), vm_getbits(command, 19, 4));
+ break;
+ case 3:
+ fprintf(MSG_OUT, "JumpSS VMGM (pgc %" PRIu8 ")", vm_getbits(command, 46, 15));
+ break;
+ }
+ break;
+ case 8:
+ switch(vm_getbits(command, 23, 2)) {
+ case 0:
+ fprintf(MSG_OUT, "CallSS FP (rsm_cell %" PRIu8 ")",
+ vm_getbits(command, 31, 8));
+ break;
+ case 1:
+ fprintf(MSG_OUT, "CallSS VMGM (menu %" PRIu8
+ ", rsm_cell %" PRIu8 ")", vm_getbits(command, 19, 4), vm_getbits(command, 31, 8));
+ break;
+ case 2:
+ fprintf(MSG_OUT, "CallSS VTSM (menu %" PRIu8
+ ", rsm_cell %" PRIu8 ")", vm_getbits(command, 19, 4), vm_getbits(command, 31, 8));
+ break;
+ case 3:
+ fprintf(MSG_OUT, "CallSS VMGM (pgc %" PRIu8 ", rsm_cell %" PRIu8 ")",
+ vm_getbits(command, 46, 15), vm_getbits(command, 31, 8));
+ break;
+ }
+ break;
+ default:
+ fprintf(MSG_OUT, "WARNING: Unknown Jump/Call instruction");
+ }
+}
+
+static void print_system_set(command_t* command) {
+ int i;
+/* FIXME: What about SPRM11 ? Karaoke */
+/* Surely there must be some system set command for that ? */
+
+ switch(vm_getbits(command, 59, 4)) {
+ case 1: /* Set system reg 1 &| 2 &| 3 (Audio, Subp. Angle) */
+ for(i = 1; i <= 3; i++) {
+ if(vm_getbits(command, 47 - (i*8), 1)) {
+ print_system_reg(i);
+ fprintf(MSG_OUT, " = ");
+ print_reg_or_data_2(command, vm_getbits(command, 60, 1), 47 - (i*8) );
+ fprintf(MSG_OUT, " ");
+ }
+ }
+ break;
+ case 2: /* Set system reg 9 & 10 (Navigation timer, Title PGC number) */
+ print_system_reg(9);
+ fprintf(MSG_OUT, " = ");
+ print_reg_or_data(command, vm_getbits(command, 60, 1), 47);
+ fprintf(MSG_OUT, " ");
+ print_system_reg(10);
+ fprintf(MSG_OUT, " = %" PRIu16, vm_getbits(command, 30, 15)); /* ?? */
+ break;
+ case 3: /* Mode: Counter / Register + Set */
+ fprintf(MSG_OUT, "SetMode ");
+ if(vm_getbits(command, 23, 1))
+ fprintf(MSG_OUT, "Counter ");
+ else
+ fprintf(MSG_OUT, "Register ");
+ print_g_reg(vm_getbits(command, 19, 4));
+ print_set_op(0x1); /* '=' */
+ print_reg_or_data(command, vm_getbits(command, 60, 1), 47);
+ break;
+ case 6: /* Set system reg 8 (Highlighted button) */
+ print_system_reg(8);
+ if(vm_getbits(command, 60, 1)) /* immediate */
+ fprintf(MSG_OUT, " = 0x%x (button no %d)", vm_getbits(command, 31, 16), vm_getbits(command, 31, 6));
+ else
+ fprintf(MSG_OUT, " = g[%" PRIu8 "]", vm_getbits(command, 19, 4));
+ break;
+ default:
+ fprintf(MSG_OUT, "WARNING: Unknown system set instruction (%i)",
+ vm_getbits(command, 59, 4));
+ }
+}
+
+static void print_set_version_1(command_t* command) {
+ uint8_t set_op = vm_getbits(command, 59, 4);
+
+ if(set_op) {
+ print_g_reg(vm_getbits(command, 35, 4));
+ print_set_op(set_op);
+ print_reg_or_data(command, vm_getbits(command, 60, 1), 31);
+ } else {
+ fprintf(MSG_OUT, "NOP");
+ }
+}
+
+static void print_set_version_2(command_t* command) {
+ uint8_t set_op = vm_getbits(command, 59, 4);
+
+ if(set_op) {
+ print_g_reg(vm_getbits(command, 51, 4));
+ print_set_op(set_op);
+ print_reg_or_data(command, vm_getbits(command, 60, 1), 47);
+ } else {
+ fprintf(MSG_OUT, "NOP");
+ }
+}
+
+static void print_set_version_3(command_t* command) {
+ uint8_t set_op = vm_getbits(command, 59, 4);
+
+ if(set_op) {
+ print_g_reg(vm_getbits(command, 51, 4));
+ print_set_op(set_op);
+ print_reg_or_data_3(command, vm_getbits(command, 60, 1), 47);
+ } else {
+ fprintf(MSG_OUT, "NOP");
+ }
+}
+
+
+void vm_print_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,63,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,60,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(MSG_OUT, ", ");
+ print_if_version_4(&command);
+ print_linksub_instruction(&command);
+ break;
+ case 5: /* Compare -> (Set and LinkSub) instructions */
+ print_if_version_5(&command);
+ fprintf(MSG_OUT, "{ ");
+ print_set_version_3(&command);
+ fprintf(MSG_OUT, ", ");
+ print_linksub_instruction(&command);
+ fprintf(MSG_OUT, " }");
+ break;
+ case 6: /* Compare -> Set, always LinkSub instructions */
+ print_if_version_5(&command);
+ fprintf(MSG_OUT, "{ ");
+ print_set_version_3(&command);
+ fprintf(MSG_OUT, " } ");
+ print_linksub_instruction(&command);
+ break;
+ default:
+ fprintf(MSG_OUT, "WARNING: Unknown instruction type (%i)", vm_getbits(&command, 63, 3));
+ }
+ /* Check if there still are bits set that were not examined */
+
+ if(command.instruction & ~ command.examined) {
+ fprintf(MSG_OUT, " libdvdnav: vmcmd.c: [WARNING, unknown bits:");
+ fprintf(MSG_OUT, " %08llx", (command.instruction & ~ command.examined) );
+ fprintf(MSG_OUT, "]");
+ }
+}
+
+void vm_print_cmd(int row, vm_cmd_t *vm_command) {
+ int i;
+
+ fprintf(MSG_OUT, "(%03d) ", row + 1);
+ for(i = 0; i < 8; i++)
+ fprintf(MSG_OUT, "%02x ", vm_command->bytes[i]);
+ fprintf(MSG_OUT, "| ");
+
+ vm_print_mnemonic(vm_command);
+ fprintf(MSG_OUT, "\n");
+}
+
diff --git a/libdvdnav/vmcmd.h b/libdvdnav/vmcmd.h
new file mode 100644
index 0000000..2e823f9
--- /dev/null
+++ b/libdvdnav/vmcmd.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2000, 2001 Martin Norb�k, H�an 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * $Id: vmcmd.h,v 1.7 2003/05/11 13:44:05 jcdutton Exp $
+ *
+ */
+
+#ifndef VMCMD_H_INCLUDED
+#define VMCMD_H_INCLUDED
+
+#include "common.h"
+
+void vm_print_mnemonic(vm_cmd_t *command);
+void vm_print_cmd(int row, vm_cmd_t *command);
+
+#endif /* VMCMD_H_INCLUDED */