summaryrefslogtreecommitdiff
path: root/src/input/libdvdnav
diff options
context:
space:
mode:
authorRich J Wareham <richwareham@users.sourceforge.net>2002-08-08 17:49:20 +0000
committerRich J Wareham <richwareham@users.sourceforge.net>2002-08-08 17:49:20 +0000
commit892715d418f93897aa941bf8aaee69e74628099f (patch)
treeb0954b9c584b9a79a83fcb186d40bb947e5af430 /src/input/libdvdnav
parent2f9f3c0ec0e38ca2c6cbfea9d6a4f8fe5fde78b9 (diff)
downloadxine-lib-892715d418f93897aa941bf8aaee69e74628099f.tar.gz
xine-lib-892715d418f93897aa941bf8aaee69e74628099f.tar.bz2
First stage of DVD plugin -> dvdnav conversion
CVS patchset: 2404 CVS date: 2002/08/08 17:49:20
Diffstat (limited to 'src/input/libdvdnav')
-rw-r--r--src/input/libdvdnav/Makefile.am30
-rw-r--r--src/input/libdvdnav/decoder.c768
-rw-r--r--src/input/libdvdnav/decoder.h107
-rw-r--r--src/input/libdvdnav/dvd_types.h300
-rw-r--r--src/input/libdvdnav/dvdnav.c1136
-rw-r--r--src/input/libdvdnav/dvdnav.h879
-rw-r--r--src/input/libdvdnav/dvdnav_events.h124
-rw-r--r--src/input/libdvdnav/dvdnav_internal.h168
-rw-r--r--src/input/libdvdnav/highlight.c344
-rw-r--r--src/input/libdvdnav/navigation.c164
-rw-r--r--src/input/libdvdnav/read_cache.c543
-rw-r--r--src/input/libdvdnav/read_cache.h51
-rw-r--r--src/input/libdvdnav/searching.c443
-rw-r--r--src/input/libdvdnav/settings.c120
-rw-r--r--src/input/libdvdnav/vm.c1905
-rw-r--r--src/input/libdvdnav/vm.h154
-rw-r--r--src/input/libdvdnav/vmcmd.c488
-rw-r--r--src/input/libdvdnav/vmcmd.h35
18 files changed, 7759 insertions, 0 deletions
diff --git a/src/input/libdvdnav/Makefile.am b/src/input/libdvdnav/Makefile.am
new file mode 100644
index 000000000..cf30e12e4
--- /dev/null
+++ b/src/input/libdvdnav/Makefile.am
@@ -0,0 +1,30 @@
+DVD_CFLAGS = -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE \
+ -Ilibdvdnav/ \
+ -Ilibdvdread/
+
+CFLAGS = @CFLAGS@ $(DVD_CFLAGS)
+
+noinst_LTLIBRARIES = libdvdnav.la
+
+libdvdnav_la_SOURCES = decoder.c dvdnav.c vm.c vmcmd.c \
+ read_cache.c navigation.c highlight.c \
+ searching.c settings.c
+libdvdnav_la_LDFLAGS = $(THREAD_LIBS) \
+ -avoid-version -module
+
+noinst_HEADERS = decoder.h dvdnav.h dvdnav_events.h \
+ dvdnav_internal.h vm.h vmcmd.h read_cache.h dvd_types.h
+
+debug:
+ @$(MAKE) CFLAGS="$(DEBUG_CFLAGS) $(OGG_CFLAGS) $(VORBIS_CFLAGS)"
+
+install-debug: debug
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+mostlyclean-generic:
+ -rm -f *~ \#* .*~ .\#*
+
+maintainer-clean-generic:
+ -@echo "This command is intended for maintainers to use;"
+ -@echo "it deletes files that may require special tools to rebuild."
+ -rm -f Makefile.in
diff --git a/src/input/libdvdnav/decoder.c b/src/input/libdvdnav/decoder.c
new file mode 100644
index 000000000..8e177b49d
--- /dev/null
+++ b/src/input/libdvdnav/decoder.c
@@ -0,0 +1,768 @@
+/*
+ * Copyright (C) 2000, 2001 Martin Norbäck, Håkan Hjort
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: decoder.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h> /* For memset */
+#include <dvdread/ifo_types.h> /* vm_cmd_t */
+#include <assert.h>
+#include "vmcmd.h"
+#include "decoder.h"
+
+uint32_t vm_getbits(command_t *command, int start, int count) {
+ uint64_t result = 0;
+ uint64_t bit_mask=0xffffffffffffffff; /* I could put -1 instead */
+ uint64_t examining = 0;
+ int32_t bits;
+ if (count == 0) return 0;
+
+ if ( ((count+start) > 64) ||
+ (count > 32) ||
+ (start > 63) ||
+ (count < 0) ||
+ (start < 0) ){
+ fprintf(stderr, "Bad call to vm_getbits. Parameter out of range\n");
+ assert(0);
+ }
+ bit_mask >>= start;
+ bits = 64-count-start;
+ examining = ((bit_mask >> bits) << bits );
+ command->examined |= examining;
+ result = (command->instruction & bit_mask) >> bits;
+ return (uint32_t) result;
+}
+
+static uint16_t get_GPRM(registers_t* registers, uint8_t reg) {
+ if (registers->GPRM_mode[reg] & 0x01) {
+ struct timeval current_time, time_offset;
+ uint16_t result;
+ /* Counter mode */
+ /* fprintf(stderr, "Getting counter %d\n",reg);*/
+ gettimeofday(&current_time, NULL);
+ time_offset.tv_sec = current_time.tv_sec - registers->GPRM_time[reg].tv_sec;
+ time_offset.tv_usec = current_time.tv_usec - registers->GPRM_time[reg].tv_usec;
+ if (time_offset.tv_usec < 0) {
+ time_offset.tv_sec--;
+ time_offset.tv_usec += 1000000;
+ }
+ result = (uint16_t) (time_offset.tv_sec & 0xffff);
+ registers->GPRM[reg]=result;
+ return result;
+
+ } else {
+ /* Register mode */
+ return registers->GPRM[reg];
+ }
+
+}
+
+static void set_GPRM(registers_t* registers, uint8_t reg, uint16_t value) {
+ if (registers->GPRM_mode[reg] & 0x01) {
+ struct timeval current_time;
+ /* Counter mode */
+ /* fprintf(stderr, "Setting counter %d\n",reg); */
+ gettimeofday(&current_time, NULL);
+ registers->GPRM_time[reg] = current_time;
+ registers->GPRM_time[reg].tv_sec -= value;
+ }
+ registers->GPRM[reg] = value;
+}
+
+/* Eval register code, can either be system or general register.
+ SXXX_XXXX, where S is 1 if it is system register. */
+static uint16_t eval_reg(command_t* command, uint8_t reg) {
+ if(reg & 0x80) {
+ if ((reg & 0x1f) == 20) {
+ fprintf(stderr, "Suspected RCE Region Protection!!!");
+ }
+ return command->registers->SPRM[reg & 0x1f]; /* FIXME max 24 not 32 */
+ } else {
+ return get_GPRM(command->registers, reg & 0x0f) ;
+ }
+}
+
+/* Eval register or immediate data.
+ AAAA_AAAA BBBB_BBBB, if immediate use all 16 bits for data else use
+ lower eight bits for the system or general purpose register. */
+static uint16_t eval_reg_or_data(command_t* command, int32_t imm, int32_t byte) {
+ if(imm) { /* immediate */
+ return vm_getbits(command, (byte*8), 16);
+ } else {
+ return eval_reg(command, vm_getbits(command, ((byte + 1)*8), 8));
+ }
+}
+
+/* Eval register or immediate data.
+ xBBB_BBBB, if immediate use all 7 bits for data else use
+ lower four bits for the general purpose register number. */
+/* Evaluates gprm or data depending on bit, data is in byte n */
+uint16_t eval_reg_or_data_2(command_t* command, int32_t imm, int32_t byte) {
+ if(imm) /* immediate */
+ return vm_getbits(command, ((byte*8)+1), 7);
+ else
+ return get_GPRM(command->registers, (vm_getbits(command, ((byte*8)+4), 4)) );
+}
+
+
+/* Compare data using operation, return result from comparison.
+ Helper function for the different if functions. */
+static int32_t eval_compare(uint8_t operation, uint16_t data1, uint16_t data2) {
+ switch(operation) {
+ case 1:
+ return data1 & data2;
+ case 2:
+ return data1 == data2;
+ case 3:
+ return data1 != data2;
+ case 4:
+ return data1 >= data2;
+ case 5:
+ return data1 > data2;
+ case 6:
+ return data1 <= data2;
+ case 7:
+ return data1 < data2;
+ }
+ fprintf(stderr,"eval_compare: Invalid comparison code\n");
+ return 0;
+}
+
+
+/* Evaluate if version 1.
+ Has comparison data in byte 3 and 4-5 (immediate or register) */
+static int32_t eval_if_version_1(command_t* command) {
+ uint8_t op = vm_getbits(command, 9, 3);
+ if(op) {
+ return eval_compare(op, eval_reg(command, vm_getbits(command, 24, 8)),
+ eval_reg_or_data(command, vm_getbits(command, 8, 1), 4));
+ }
+ return 1;
+}
+
+/* Evaluate if version 2.
+ This version only compares register which are in byte 6 and 7 */
+static int32_t eval_if_version_2(command_t* command) {
+ uint8_t op = vm_getbits(command, 9, 3);
+ if(op) {
+ return eval_compare(op, eval_reg(command, vm_getbits(command, 48, 8)),
+ eval_reg(command, vm_getbits(command, 56, 8)));
+ }
+ return 1;
+}
+
+/* Evaluate if version 3.
+ Has comparison data in byte 2 and 6-7 (immediate or register) */
+static int32_t eval_if_version_3(command_t* command) {
+ uint8_t op = vm_getbits(command, 9, 3);
+ if(op) {
+ return eval_compare(op, eval_reg(command, vm_getbits(command, 16, 8)),
+ eval_reg_or_data(command, vm_getbits(command, 8, 1), 6));
+ }
+ return 1;
+}
+
+/* Evaluate if version 4.
+ Has comparison data in byte 1 and 4-5 (immediate or register)
+ The register in byte 1 is only the lowe nibble (4 bits) */
+static int32_t eval_if_version_4(command_t* command) {
+ uint8_t op = vm_getbits(command, 9, 3);
+ if(op) {
+ return eval_compare(op, eval_reg(command, vm_getbits(command, 12, 4)),
+ eval_reg_or_data(command, vm_getbits(command, 8, 1), 4));
+ }
+ return 1;
+}
+
+/* Evaluate special instruction.... returns the new row/line number,
+ 0 if no new row and 256 if Break. */
+static int32_t eval_special_instruction(command_t* command, int32_t cond) {
+ int32_t line, level;
+
+ switch(vm_getbits(command, 12, 4)) {
+ case 0: /* NOP */
+ line = 0;
+ return cond ? line : 0;
+ case 1: /* Goto line */
+ line = vm_getbits(command, 56, 8);
+ return cond ? line : 0;
+ case 2: /* Break */
+ /* max number of rows < 256, so we will end this set */
+ line = 256;
+ return cond ? 256 : 0;
+ case 3: /* Set temporary parental level and goto */
+ line = vm_getbits(command, 56, 8);
+ level = vm_getbits(command, 52, 4);
+ if(cond) {
+ /* This always succeeds now, if we want real parental protection */
+ /* we need to ask the user and have passwords and stuff. */
+ command->registers->SPRM[13] = level;
+ }
+ return cond ? line : 0;
+ }
+ return 0;
+}
+
+/* Evaluate link by subinstruction.
+ Return 1 if link, or 0 if no link
+ Actual link instruction is in return_values parameter */
+static int32_t eval_link_subins(command_t* command, int32_t cond, link_t *return_values) {
+ uint16_t button = vm_getbits(command, 48, 6);
+ uint8_t linkop = vm_getbits(command, 59, 5);
+
+ if(linkop > 0x10)
+ return 0; /* Unknown Link by Sub-Instruction command */
+
+ /* Assumes that the link_cmd_t enum has the same values as the LinkSIns codes */
+ return_values->command = linkop;
+ return_values->data1 = button;
+ return cond;
+}
+
+
+/* Evaluate link instruction.
+ Return 1 if link, or 0 if no link
+ Actual link instruction is in return_values parameter */
+static int32_t eval_link_instruction(command_t* command, int32_t cond, link_t *return_values) {
+ uint8_t op = vm_getbits(command, 12, 4);
+
+ switch(op) {
+ case 1:
+ return eval_link_subins(command, cond, return_values);
+ case 4:
+ return_values->command = LinkPGCN;
+ return_values->data1 = vm_getbits(command, 49, 15);
+ return cond;
+ case 5:
+ return_values->command = LinkPTTN;
+ return_values->data1 = vm_getbits(command, 54, 10);
+ return_values->data2 = vm_getbits(command, 48, 6);
+ return cond;
+ case 6:
+ return_values->command = LinkPGN;
+ return_values->data1 = vm_getbits(command, 57, 7);
+ return_values->data2 = vm_getbits(command, 48, 6);
+ return cond;
+ case 7:
+ return_values->command = LinkCN;
+ return_values->data1 = vm_getbits(command, 56, 8);
+ return_values->data2 = vm_getbits(command, 48, 6);
+ return cond;
+ }
+ return 0;
+}
+
+
+/* Evaluate a jump instruction.
+ returns 1 if jump or 0 if no jump
+ actual jump instruction is in return_values parameter */
+static int32_t eval_jump_instruction(command_t* command, int32_t cond, link_t *return_values) {
+
+ switch(vm_getbits(command, 12, 4)) {
+ case 1:
+ return_values->command = Exit;
+ return cond;
+ case 2:
+ return_values->command = JumpTT;
+ return_values->data1 = vm_getbits(command, 41, 7);
+ return cond;
+ case 3:
+ return_values->command = JumpVTS_TT;
+ return_values->data1 = vm_getbits(command, 41, 7);
+ return cond;
+ case 5:
+ return_values->command = JumpVTS_PTT;
+ return_values->data1 = vm_getbits(command, 41, 7);
+ return_values->data2 = vm_getbits(command, 22, 10);
+ return cond;
+ case 6:
+ switch(vm_getbits(command, 40, 2)) {
+ case 0:
+ return_values->command = JumpSS_FP;
+ return cond;
+ case 1:
+ return_values->command = JumpSS_VMGM_MENU;
+ return_values->data1 = vm_getbits(command, 44, 4);
+ return cond;
+ case 2:
+ return_values->command = JumpSS_VTSM;
+ return_values->data1 = vm_getbits(command, 32, 8);
+ return_values->data2 = vm_getbits(command, 24, 8);
+ return_values->data3 = vm_getbits(command, 44, 4);
+ return cond;
+ case 3:
+ return_values->command = JumpSS_VMGM_PGC;
+ return_values->data1 = vm_getbits(command, 17, 15);
+ return cond;
+ }
+ break;
+ case 8:
+ switch(vm_getbits(command, 40, 2)) {
+ case 0:
+ return_values->command = CallSS_FP;
+ return_values->data1 = vm_getbits(command, 32, 8);
+ return cond;
+ case 1:
+ return_values->command = CallSS_VMGM_MENU;
+ return_values->data1 = vm_getbits(command, 44, 4);
+ return_values->data2 = vm_getbits(command, 32, 8);
+ return cond;
+ case 2:
+ return_values->command = CallSS_VTSM;
+ return_values->data1 = vm_getbits(command, 44, 4);
+ return_values->data2 = vm_getbits(command, 32, 8);
+ return cond;
+ case 3:
+ return_values->command = CallSS_VMGM_PGC;
+ return_values->data1 = vm_getbits(command, 17, 15);
+ return_values->data2 = vm_getbits(command, 32, 8);
+ return cond;
+ }
+ break;
+ }
+ return 0;
+}
+
+/* Evaluate a set sytem register instruction
+ May contain a link so return the same as eval_link */
+static int32_t eval_system_set(command_t* command, int32_t cond, link_t *return_values) {
+ int32_t i;
+ uint16_t data, data2;
+
+ switch(vm_getbits(command, 4, 4)) {
+ case 1: /* Set system reg 1 &| 2 &| 3 (Audio, Subp. Angle) */
+ for(i = 1; i <= 3; i++) {
+ if(vm_getbits(command, ((2 + i)*8), 1)) {
+ data = eval_reg_or_data_2(command, vm_getbits(command, 3, 1), 2 + i);
+ if(cond) {
+ command->registers->SPRM[i] = data;
+ }
+ }
+ }
+ break;
+ case 2: /* Set system reg 9 & 10 (Navigation timer, Title PGC number) */
+ data = eval_reg_or_data(command, vm_getbits(command, 3, 1), 2);
+ data2 = vm_getbits(command, 40, 8); /* ?? size */
+ if(cond) {
+ command->registers->SPRM[9] = data; /* time */
+ command->registers->SPRM[10] = data2; /* pgcN */
+ }
+ break;
+ case 3: /* Mode: Counter / Register + Set */
+ data = eval_reg_or_data(command, vm_getbits(command, 3, 1), 2);
+ data2 = vm_getbits(command, 44, 4);
+ if(vm_getbits(command, 40, 1)) {
+ command->registers->GPRM_mode[data2] |= 1; /* Set bit 0 */
+ } else {
+ command->registers->GPRM_mode[data2] &= ~ 0x01; /* Reset bit 0 */
+ }
+ if(cond) {
+ set_GPRM(command->registers, data2, data);
+ }
+ break;
+ case 6: /* Set system reg 8 (Highlighted button) */
+ data = eval_reg_or_data(command, vm_getbits(command, 3, 1), 4); /* Not system reg!! */
+ if(cond) {
+ command->registers->SPRM[8] = data;
+ }
+ break;
+ }
+ if(vm_getbits(command, 12, 4)) {
+ return eval_link_instruction(command, cond, return_values);
+ }
+ return 0;
+}
+
+
+/* Evaluate set operation
+ Sets the register given to the value indicated by op and data.
+ For the swap case the contents of reg is stored in reg2.
+*/
+static void eval_set_op(command_t* command, int32_t op, int32_t reg, int32_t reg2, int32_t data) {
+ const int32_t shortmax = 0xffff;
+ int32_t tmp;
+ switch(op) {
+ case 1:
+ set_GPRM(command->registers, reg, data);
+ break;
+ case 2: /* SPECIAL CASE - SWAP! */
+ set_GPRM(command->registers, reg2, get_GPRM(command->registers, reg));
+ set_GPRM(command->registers, reg, data);
+ break;
+ case 3:
+ tmp = get_GPRM(command->registers, reg) + data;
+ if(tmp > shortmax) tmp = shortmax;
+ set_GPRM(command->registers, reg, (uint16_t)tmp);
+ break;
+ case 4:
+ tmp = get_GPRM(command->registers, reg) - data;
+ if(tmp < 0) tmp = 0;
+ set_GPRM(command->registers, reg, (uint16_t)tmp);
+ break;
+ case 5:
+ tmp = get_GPRM(command->registers, reg) * data;
+ if(tmp >= shortmax) tmp = shortmax;
+ set_GPRM(command->registers, reg, (uint16_t)tmp);
+ break;
+ case 6:
+ if (data != 0) {
+ set_GPRM(command->registers, reg, (get_GPRM(command->registers, reg) / data) );
+ } else {
+ set_GPRM(command->registers, reg, 0); /* Avoid that divide by zero! */
+ }
+ break;
+ case 7:
+ set_GPRM(command->registers, reg, (get_GPRM(command->registers, reg) % data) );
+ break;
+ case 8: /* SPECIAL CASE - RND! */
+ set_GPRM(command->registers, reg, ((uint16_t) ((float) data * rand()/(RAND_MAX+1.0))) );
+ break;
+ case 9:
+ set_GPRM(command->registers, reg, (get_GPRM(command->registers, reg) & data) );
+ break;
+ case 10:
+ set_GPRM(command->registers, reg, (get_GPRM(command->registers, reg) | data) );
+ break;
+ case 11:
+ set_GPRM(command->registers, reg, (get_GPRM(command->registers, reg) ^ data) );
+ break;
+ }
+}
+
+/* Evaluate set instruction, combined with either Link or Compare. */
+static void eval_set_version_1(command_t* command, int32_t cond) {
+ uint8_t op = vm_getbits(command, 4, 4);
+ uint8_t reg = vm_getbits(command, 28, 4); /* FIXME: This is different from vmcmd.c!!! */
+ uint8_t reg2 = vm_getbits(command, 44, 4);
+ uint16_t data = eval_reg_or_data(command, vm_getbits(command, 3, 1), 4);
+
+ if(cond) {
+ eval_set_op(command, op, reg, reg2, data);
+ }
+}
+
+
+/* Evaluate set instruction, combined with both Link and Compare. */
+static void eval_set_version_2(command_t* command, int32_t cond) {
+ uint8_t op = vm_getbits(command, 4, 4);
+ uint8_t reg = vm_getbits(command, 12, 4);
+ uint8_t reg2 = vm_getbits(command, 28, 4); /* FIXME: This is different from vmcmd.c!!! */
+ uint16_t data = eval_reg_or_data(command, vm_getbits(command, 3, 1), 2);
+
+ if(cond) {
+ eval_set_op(command, op, reg, reg2, data);
+ }
+}
+
+
+/* Evaluate a command
+ returns row number of goto, 0 if no goto, -1 if link.
+ Link command in return_values */
+static int32_t eval_command(uint8_t *bytes, registers_t* registers, link_t *return_values) {
+ int32_t cond, res = 0;
+ command_t command;
+ command.instruction =( (uint64_t) bytes[0] << 56 ) |
+ ( (uint64_t) bytes[1] << 48 ) |
+ ( (uint64_t) bytes[2] << 40 ) |
+ ( (uint64_t) bytes[3] << 32 ) |
+ ( (uint64_t) bytes[4] << 24 ) |
+ ( (uint64_t) bytes[5] << 16 ) |
+ ( (uint64_t) bytes[6] << 8 ) |
+ (uint64_t) bytes[7] ;
+ command.examined = 0;
+ command.registers = registers;
+ memset(return_values, 0, sizeof(link_t));
+
+ switch(vm_getbits(&command, 0, 3)) { /* three first old_bits */
+ case 0: /* Special instructions */
+ cond = eval_if_version_1(&command);
+ res = eval_special_instruction(&command, cond);
+ if(res == -1) {
+ fprintf(stderr, "Unknown Instruction!\n");
+ assert(0);
+ }
+ break;
+ case 1: /* Link/jump instructions */
+ if(vm_getbits(&command, 3, 1)) {
+ cond = eval_if_version_2(&command);
+ res = eval_jump_instruction(&command, cond, return_values);
+ } else {
+ cond = eval_if_version_1(&command);
+ res = eval_link_instruction(&command, cond, return_values);
+ }
+ if(res)
+ res = -1;
+ break;
+ case 2: /* System set instructions */
+ cond = eval_if_version_2(&command);
+ res = eval_system_set(&command, cond, return_values);
+ if(res)
+ res = -1;
+ break;
+ case 3: /* Set instructions, either Compare or Link may be used */
+ cond = eval_if_version_3(&command);
+ eval_set_version_1(&command, cond);
+ if(vm_getbits(&command, 12, 4)) {
+ res = eval_link_instruction(&command, cond, return_values);
+ }
+ if(res)
+ res = -1;
+ break;
+ case 4: /* Set, Compare -> Link Sub-Instruction */
+ eval_set_version_2(&command, /*True*/ 1);
+ cond = eval_if_version_4(&command);
+ res = eval_link_subins(&command, cond, return_values);
+ if(res)
+ res = -1;
+ break;
+ case 5: /* Compare -> (Set and Link Sub-Instruction) */
+ cond = eval_if_version_4(&command);
+ eval_set_version_2(&command, cond);
+ res = eval_link_subins(&command, cond, return_values);
+ if(res)
+ res = -1;
+ break;
+ case 6: /* Compare -> Set, allways Link Sub-Instruction */
+ cond = eval_if_version_4(&command);
+ eval_set_version_2(&command, cond);
+ res = eval_link_subins(&command, /*True*/ 1, return_values);
+ if(res)
+ res = -1;
+ break;
+ default: /* Unknown command */
+ fprintf(stderr, "WARNING: Unknown Command=%x\n", vm_getbits(&command, 0, 3));
+ assert(0);
+ }
+ /* Check if there are bits not yet examined */
+
+ if(command.instruction & ~ command.examined) {
+ fprintf(stderr, " libdvdnav: decoder.c: [WARNING, unknown bits:");
+ fprintf(stderr, " %08llx", (command.instruction & ~ command.examined) );
+ fprintf(stderr, "]");
+ }
+
+ return res;
+}
+
+/* Evaluate a set of commands in the given register set (which is
+ * modified */
+int32_t vmEval_CMD(vm_cmd_t commands[], int32_t num_commands,
+ registers_t *registers, link_t *return_values) {
+ int32_t i = 0;
+ int32_t total = 0;
+
+#ifdef TRACE
+ /* DEBUG */
+ fprintf(stderr, "libdvdnav: Registers before transaction\n");
+ vmPrint_registers( registers );
+ if(1) {
+ int32_t i;
+ fprintf(stderr, "libdvdnav: Full list of commands to execute\n");
+ for(i = 0; i < num_commands; i++)
+ vmPrint_CMD(i, &commands[i]);
+ fprintf(stderr, "--------------------------------------------\n");
+ } /* end DEBUG */
+ if (1) {
+ fprintf(stderr, "libdvdnav: Single stepping commands\n");
+ }
+#endif
+
+ while(i < num_commands && total < 100000) {
+ int32_t line;
+
+#ifdef TRACE
+ if(1) vmPrint_CMD(i, &commands[i]);
+#endif
+ line = eval_command(&commands[i].bytes[0], registers, return_values);
+
+ if (line < 0) { /* Link command */
+#ifdef TRACE
+ fprintf(stderr, "libdvdnav: Registers after transaction\n");
+ vmPrint_registers( registers );
+ fprintf(stderr, "eval: Doing Link/Jump/Call\n");
+#endif
+ return 1;
+ }
+
+ if (line > 0) /* Goto command */
+ i = line - 1;
+ else /* Just continue on the next line */
+ i++;
+
+ total++;
+ }
+
+ memset(return_values, 0, sizeof(link_t));
+#ifdef TRACE
+ fprintf(stderr, "libdvdnav: Registers after transaction\n");
+ vmPrint_registers( registers );
+#endif
+ return 0;
+}
+
+static char *linkcmd2str(link_cmd_t cmd) {
+ switch(cmd) {
+ case LinkNoLink:
+ return "LinkNoLink";
+ case LinkTopC:
+ return "LinkTopC";
+ case LinkNextC:
+ return "LinkNextC";
+ case LinkPrevC:
+ return "LinkPrevC";
+ case LinkTopPG:
+ return "LinkTopPG";
+ case LinkNextPG:
+ return "LinkNextPG";
+ case LinkPrevPG:
+ return "LinkPrevPG";
+ case LinkTopPGC:
+ return "LinkTopPGC";
+ case LinkNextPGC:
+ return "LinkNextPGC";
+ case LinkPrevPGC:
+ return "LinkPrevPGC";
+ case LinkGoUpPGC:
+ return "LinkGoUpPGC";
+ case LinkTailPGC:
+ return "LinkTailPGC";
+ case LinkRSM:
+ return "LinkRSM";
+ case LinkPGCN:
+ return "LinkPGCN";
+ case LinkPTTN:
+ return "LinkPTTN";
+ case LinkPGN:
+ return "LinkPGN";
+ case LinkCN:
+ return "LinkCN";
+ case Exit:
+ return "Exit";
+ case JumpTT:
+ return "JumpTT";
+ case JumpVTS_TT:
+ return "JumpVTS_TT";
+ case JumpVTS_PTT:
+ return "JumpVTS_PTT";
+ case JumpSS_FP:
+ return "JumpSS_FP";
+ case JumpSS_VMGM_MENU:
+ return "JumpSS_VMGM_MENU";
+ case JumpSS_VTSM:
+ return "JumpSS_VTSM";
+ case JumpSS_VMGM_PGC:
+ return "JumpSS_VMGM_PGC";
+ case CallSS_FP:
+ return "CallSS_FP";
+ case CallSS_VMGM_MENU:
+ return "CallSS_VMGM_MENU";
+ case CallSS_VTSM:
+ return "CallSS_VTSM";
+ case CallSS_VMGM_PGC:
+ return "CallSS_VMGM_PGC";
+ case PlayThis:
+ return "PlayThis";
+ }
+ return "*** (bug)";
+}
+
+void vmPrint_LINK(link_t value) {
+ char *cmd = linkcmd2str(value.command);
+
+ switch(value.command) {
+ case LinkNoLink:
+ case LinkTopC:
+ case LinkNextC:
+ case LinkPrevC:
+ case LinkTopPG:
+ case LinkNextPG:
+ case LinkPrevPG:
+ case LinkTopPGC:
+ case LinkNextPGC:
+ case LinkPrevPGC:
+ case LinkGoUpPGC:
+ case LinkTailPGC:
+ case LinkRSM:
+ fprintf(stderr, "%s (button %d)\n", cmd, value.data1);
+ break;
+ case LinkPGCN:
+ case JumpTT:
+ case JumpVTS_TT:
+ case JumpSS_VMGM_MENU: /* == 2 -> Title Menu */
+ case JumpSS_VMGM_PGC:
+ fprintf(stderr, "%s %d\n", cmd, value.data1);
+ break;
+ case LinkPTTN:
+ case LinkPGN:
+ case LinkCN:
+ fprintf(stderr, "%s %d (button %d)\n", cmd, value.data1, value.data2);
+ break;
+ case Exit:
+ case JumpSS_FP:
+ case PlayThis: /* Humm.. should we have this at all.. */
+ fprintf(stderr, "%s\n", cmd);
+ break;
+ case JumpVTS_PTT:
+ fprintf(stderr, "%s %d:%d\n", cmd, value.data1, value.data2);
+ break;
+ case JumpSS_VTSM:
+ fprintf(stderr, "%s vts %d title %d menu %d\n",
+ cmd, value.data1, value.data2, value.data3);
+ break;
+ case CallSS_FP:
+ fprintf(stderr, "%s resume cell %d\n", cmd, value.data1);
+ break;
+ case CallSS_VMGM_MENU: /* == 2 -> Title Menu */
+ case CallSS_VTSM:
+ fprintf(stderr, "%s %d resume cell %d\n", cmd, value.data1, value.data2);
+ break;
+ case CallSS_VMGM_PGC:
+ fprintf(stderr, "%s %d resume cell %d\n", cmd, value.data1, value.data2);
+ break;
+ }
+ }
+
+void vmPrint_registers( registers_t *registers ) {
+ int32_t i;
+ fprintf(stderr, " # ");
+ for(i = 0; i < 24; i++)
+ fprintf(stderr, " %2d |", i);
+ fprintf(stderr, "\nSRPMS: ");
+ for(i = 0; i < 24; i++)
+ fprintf(stderr, "%04x|", registers->SPRM[i]);
+ fprintf(stderr, "\nGRPMS: ");
+ for(i = 0; i < 16; i++)
+ fprintf(stderr, "%04x|", get_GPRM(registers, i) );
+ fprintf(stderr, "\nGmode: ");
+ for(i = 0; i < 16; i++)
+ fprintf(stderr, "%04x|", registers->GPRM_mode[i]);
+ fprintf(stderr, "\nGtime: ");
+ for(i = 0; i < 16; i++)
+ fprintf(stderr, "%04lx|", registers->GPRM_time[i].tv_sec & 0xffff);
+ fprintf(stderr, "\n");
+}
diff --git a/src/input/libdvdnav/decoder.h b/src/input/libdvdnav/decoder.h
new file mode 100644
index 000000000..e977ac90f
--- /dev/null
+++ b/src/input/libdvdnav/decoder.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2000, 2001 Martin Norbäck, Håkan Hjort
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: decoder.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifndef DECODER_H_INCLUDED
+#define DECODER_H_INCLUDED
+
+#include <inttypes.h>
+#include <sys/time.h>
+
+#include <dvdread/ifo_types.h> /* vm_cmd_t */
+
+/* Uncomment for tracing */
+/* #define TRACE */
+
+typedef enum {
+ LinkNoLink = 0,
+
+ LinkTopC = 1,
+ LinkNextC = 2,
+ LinkPrevC = 3,
+
+ LinkTopPG = 5,
+ LinkNextPG = 6,
+ LinkPrevPG = 7,
+
+ LinkTopPGC = 9,
+ LinkNextPGC = 10,
+ LinkPrevPGC = 11,
+ LinkGoUpPGC = 12,
+ LinkTailPGC = 13,
+
+ LinkRSM = 16,
+
+ LinkPGCN,
+ LinkPTTN,
+ LinkPGN,
+ LinkCN,
+
+ Exit,
+
+ JumpTT, /* 22 */
+ JumpVTS_TT,
+ JumpVTS_PTT,
+
+ JumpSS_FP,
+ JumpSS_VMGM_MENU,
+ JumpSS_VTSM,
+ JumpSS_VMGM_PGC,
+
+ CallSS_FP, /* 29 */
+ CallSS_VMGM_MENU,
+ CallSS_VTSM,
+ CallSS_VMGM_PGC,
+
+ PlayThis
+} link_cmd_t;
+
+typedef struct {
+ link_cmd_t command;
+ uint16_t data1;
+ uint16_t data2;
+ uint16_t data3;
+} link_t;
+
+typedef struct {
+ uint16_t SPRM[24];
+ uint16_t GPRM[16];
+ uint8_t GPRM_mode[16]; /* Need to have some thing to indicate normal/counter mode for every GPRM */
+ struct timeval GPRM_time[16]; /* For counter mode */
+} registers_t;
+
+typedef struct
+{
+ uint64_t instruction;
+ uint64_t examined;
+ registers_t *registers;
+} command_t;
+
+int vmEval_CMD(vm_cmd_t commands[], int num_commands,
+ registers_t *registers, link_t *return_values);
+
+void vmPrint_LINK(link_t value);
+void vmPrint_registers( registers_t *registers );
+uint32_t vm_getbits(command_t* command, int start, int count);
+
+#endif /* DECODER_H_INCLUDED */
diff --git a/src/input/libdvdnav/dvd_types.h b/src/input/libdvdnav/dvd_types.h
new file mode 100644
index 000000000..d19bce84a
--- /dev/null
+++ b/src/input/libdvdnav/dvd_types.h
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2000, 2001 Björn Englund, Håkan Hjort
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is a modified
+ * file originally part of the Ogle DVD player project.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: dvd_types.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifndef DVD_H_INCLUDED
+#define DVD_H_INCLUDED
+
+#include <inttypes.h>
+
+/**
+ * \file dvd_types.h
+ *
+ * Various useful structs and enums for DVDs.
+ */
+
+/**
+ * DVD Domain
+ */
+typedef enum {
+ DVD_DOMAIN_FirstPlay, /**< First Play Domain */
+ DVD_DOMAIN_VMG, /**< Video Manager Domain */
+ DVD_DOMAIN_VTSMenu, /**< Video Title Set Menu Domain */
+ DVD_DOMAIN_VTSTitle, /**< Video Title Set Domain */
+ DVD_DOMAIN_Stop /**< Stop Domain */
+} DVDDomain_t;
+
+/**
+ * DVD Menu
+ */
+typedef enum {
+ DVD_MENU_Title = 2, /**< TBD */
+ DVD_MENU_Root = 3, /**< TBD */
+ DVD_MENU_Subpicture = 4, /**< TBD */
+ DVD_MENU_Audio = 5, /**< TBD */
+ DVD_MENU_Angle = 6, /**< TBD */
+ DVD_MENU_Part = 7 /**< TBD */
+} DVDMenuID_t;
+
+/**
+ * User operations
+ */
+typedef enum {
+ UOP_FLAG_TitleOrTimePlay = 0x00000001,
+ UOP_FLAG_ChapterSearchOrPlay = 0x00000002,
+ UOP_FLAG_TitlePlay = 0x00000004,
+ UOP_FLAG_Stop = 0x00000008,
+ UOP_FLAG_GoUp = 0x00000010,
+ UOP_FLAG_TimeOrChapterSearch = 0x00000020,
+ UOP_FLAG_PrevOrTopPGSearch = 0x00000040,
+ UOP_FLAG_NextPGSearch = 0x00000080,
+ UOP_FLAG_ForwardScan = 0x00000100,
+ UOP_FLAG_BackwardScan = 0x00000200,
+ UOP_FLAG_TitleMenuCall = 0x00000400,
+ UOP_FLAG_RootMenuCall = 0x00000800,
+ UOP_FLAG_SubPicMenuCall = 0x00001000,
+ UOP_FLAG_AudioMenuCall = 0x00002000,
+ UOP_FLAG_AngleMenuCall = 0x00004000,
+ UOP_FLAG_ChapterMenuCall = 0x00008000,
+ UOP_FLAG_Resume = 0x00010000,
+ UOP_FLAG_ButtonSelectOrActivate = 0x00020000,
+ UOP_FLAG_StillOff = 0x00040000,
+ UOP_FLAG_PauseOn = 0x00080000,
+ UOP_FLAG_AudioStreamChange = 0x00100000,
+ UOP_FLAG_SubPicStreamChange = 0x00200000,
+ UOP_FLAG_AngleChange = 0x00400000,
+ UOP_FLAG_KaraokeAudioPresModeChange = 0x00800000,
+ UOP_FLAG_VideoPresModeChange = 0x01000000
+} DVDUOP_t;
+
+
+/**
+ * Parental Level
+ */
+typedef enum {
+ DVD_PARENTAL_LEVEL_1 = 1,
+ DVD_PARENTAL_LEVEL_2 = 2,
+ DVD_PARENTAL_LEVEL_3 = 3,
+ DVD_PARENTAL_LEVEL_4 = 4,
+ DVD_PARENTAL_LEVEL_5 = 5,
+ DVD_PARENTAL_LEVEL_6 = 6,
+ DVD_PARENTAL_LEVEL_7 = 7,
+ DVD_PARENTAL_LEVEL_8 = 8,
+ DVD_PARENTAL_LEVEL_None = 15
+} DVDParentalLevel_t;
+
+/**
+ * Language ID (ISO-639 language code)
+ */
+typedef uint16_t DVDLangID_t;
+
+/**
+ * Country ID (ISO-3166 country code)
+ */
+typedef uint16_t DVDCountryID_t;
+
+/**
+ * Register
+ */
+typedef uint16_t DVDRegister_t;
+
+typedef enum {
+ DVDFalse = 0,
+ DVDTrue = 1
+} DVDBool_t;
+
+typedef DVDRegister_t DVDGPRMArray_t[16];
+typedef DVDRegister_t DVDSPRMArray_t[24];
+
+typedef int DVDStream_t;
+
+/**
+ * Angle number (1-9 or default?)
+ */
+typedef int DVDAngle_t;
+
+typedef int DVDPTT_t;
+typedef int DVDTitle_t;
+typedef struct {
+ uint8_t Hours;
+ uint8_t Minutes;
+ uint8_t Seconds;
+ uint8_t Frames;
+} DVDTimecode_t;
+
+/**
+ * Subpicture stream number (0-31,62,63)
+ */
+typedef int DVDSubpictureStream_t;
+
+/**
+ * Audio stream number (0-7, 15(none))
+ */
+typedef int DVDAudioStream_t;
+
+
+/**
+ * The audio application mode
+ */
+typedef enum {
+ DVD_AUDIO_APP_MODE_None = 0, /**< app mode none */
+ DVD_AUDIO_APP_MODE_Karaoke = 1, /**< app mode karaoke */
+ DVD_AUDIO_APP_MODE_Surround = 2, /**< app mode surround */
+ DVD_AUDIO_APP_MODE_Other = 3 /**< app mode other */
+} DVDAudioAppMode_t;
+
+/**
+ * The audio format
+ */
+typedef enum {
+ DVD_AUDIO_FORMAT_AC3 = 0, /**< Dolby AC-3 */
+ DVD_AUDIO_FORMAT_MPEG1 = 1, /**< MPEG-1 */
+ DVD_AUDIO_FORMAT_MPEG1_DRC = 2, /**< MPEG-1 with dynamic range control */
+ DVD_AUDIO_FORMAT_MPEG2 = 3, /**< MPEG-2 */
+ DVD_AUDIO_FORMAT_MPEG2_DRC = 4, /**< MPEG-2 with dynamic range control */
+ DVD_AUDIO_FORMAT_LPCM = 5, /**< Linear Pulse Code Modulation */
+ DVD_AUDIO_FORMAT_DTS = 6, /**< Digital Theater Systems */
+ DVD_AUDIO_FORMAT_SDDS = 7, /**< Sony Dynamic Digital Sound */
+ DVD_AUDIO_FORMAT_Other = 8 /**< Other format*/
+} DVDAudioFormat_t;
+
+/**
+ * Audio language extension
+ */
+typedef enum {
+ DVD_AUDIO_LANG_EXT_NotSpecified = 0, /**< TBD */
+ DVD_AUDIO_LANG_EXT_NormalCaptions = 1, /**< TBD */
+ DVD_AUDIO_LANG_EXT_VisuallyImpaired = 2, /**< TBD */
+ DVD_AUDIO_LANG_EXT_DirectorsComments1 = 3, /**< TBD */
+ DVD_AUDIO_LANG_EXT_DirectorsComments2 = 4 /**< TBD */
+} DVDAudioLangExt_t;
+
+/**
+ * Subpicture language extension
+ */
+typedef enum {
+ DVD_SUBPICTURE_LANG_EXT_NotSpecified = 0,
+ DVD_SUBPICTURE_LANG_EXT_NormalCaptions = 1,
+ DVD_SUBPICTURE_LANG_EXT_BigCaptions = 2,
+ DVD_SUBPICTURE_LANG_EXT_ChildrensCaptions = 3,
+ DVD_SUBPICTURE_LANG_EXT_NormalCC = 5,
+ DVD_SUBPICTURE_LANG_EXT_BigCC = 6,
+ DVD_SUBPICTURE_LANG_EXT_ChildrensCC = 7,
+ DVD_SUBPICTURE_LANG_EXT_Forced = 9,
+ DVD_SUBPICTURE_LANG_EXT_NormalDirectorsComments = 13,
+ DVD_SUBPICTURE_LANG_EXT_BigDirectorsComments = 14,
+ DVD_SUBPICTURE_LANG_EXT_ChildrensDirectorsComments = 15,
+} DVDSubpictureLangExt_t;
+
+/**
+ * Karaoke Downmix mode
+ */
+typedef enum {
+ DVD_KARAOKE_DOWNMIX_0to0 = 0x0001,
+ DVD_KARAOKE_DOWNMIX_1to0 = 0x0002,
+ DVD_KARAOKE_DOWNMIX_2to0 = 0x0004,
+ DVD_KARAOKE_DOWNMIX_3to0 = 0x0008,
+ DVD_KARAOKE_DOWNMIX_4to0 = 0x0010,
+ DVD_KARAOKE_DOWNMIX_Lto0 = 0x0020,
+ DVD_KARAOKE_DOWNMIX_Rto0 = 0x0040,
+ DVD_KARAOKE_DOWNMIX_0to1 = 0x0100,
+ DVD_KARAOKE_DOWNMIX_1to1 = 0x0200,
+ DVD_KARAOKE_DOWNMIX_2to1 = 0x0400,
+ DVD_KARAOKE_DOWNMIX_3to1 = 0x0800,
+ DVD_KARAOKE_DOWNMIX_4to1 = 0x1000,
+ DVD_KARAOKE_DOWNMIX_Lto1 = 0x2000,
+ DVD_KARAOKE_DOWNMIX_Rto1 = 0x4000
+} DVDKaraokeDownmix_t;
+
+typedef int DVDKaraokeDownmixMask_t;
+
+typedef enum {
+ DVD_DISPLAY_MODE_ContentDefault = 0,
+ DVD_DISPLAY_MODE_16x9 = 1,
+ DVD_DISPLAY_MODE_4x3PanScan = 2,
+ DVD_DISPLAY_MODE_4x3Letterboxed = 3
+} DVDDisplayMode_t;
+
+typedef int DVDAudioSampleFreq_t; /**< TBD */
+typedef int DVDAudioSampleQuant_t; /**< TBD */
+typedef int DVDChannelNumber_t; /**< TBD */
+
+
+typedef struct {
+ DVDAudioAppMode_t AppMode;
+ DVDAudioFormat_t AudioFormat;
+ DVDLangID_t Language;
+ DVDAudioLangExt_t LanguageExtension;
+ DVDBool_t HasMultichannelInfo;
+ DVDAudioSampleFreq_t SampleFrequency;
+ DVDAudioSampleQuant_t SampleQuantization;
+ DVDChannelNumber_t NumberOfChannels;
+} DVDAudioAttributes_t;
+
+typedef enum {
+ DVD_SUBPICTURE_TYPE_NotSpecified = 0,
+ DVD_SUBPICTURE_TYPE_Language = 1,
+ DVD_SUBPICTURE_TYPE_Other = 2
+} DVDSubpictureType_t;
+
+typedef enum {
+ DVD_SUBPICTURE_CODING_RunLength = 0,
+ DVD_SUBPICTURE_CODING_Extended = 1,
+ DVD_SUBPICTURE_CODING_Other = 2
+} DVDSubpictureCoding_t;
+
+typedef struct {
+ DVDSubpictureType_t Type;
+ DVDSubpictureCoding_t CodingMode;
+ DVDLangID_t Language;
+ DVDSubpictureLangExt_t LanguageExtension;
+} DVDSubpictureAttributes_t;
+
+typedef int DVDVideoCompression_t; /**< TBD */
+
+typedef struct {
+ DVDBool_t PanscanPermitted;
+ DVDBool_t LetterboxPermitted;
+ int AspectX;
+ int AspectY;
+ int FrameRate;
+ int FrameHeight;
+ DVDVideoCompression_t Compression;
+ DVDBool_t Line21Field1InGop;
+ DVDBool_t Line21Field2InGop;
+ int more_to_come;
+} DVDVideoAttributes_t;
+
+/**
+ * Atructure containing info on highlight areas.
+ */
+typedef struct {
+ uint32_t palette; /*!< The CLUT entries for the highlight palette
+ (4-bits per entry -> 4 entries) */
+ uint16_t sx,sy,ex,ey; /*!< The start/end x,y positions */
+ uint32_t pts; /*!< Highlight PTS to match with SPU */
+ uint32_t buttonN; /*!< Button number for the SPU decoder. */
+} dvdnav_highlight_area_t;
+
+
+#endif /* DVD_H_INCLUDED */
diff --git a/src/input/libdvdnav/dvdnav.c b/src/input/libdvdnav/dvdnav.c
new file mode 100644
index 000000000..63282db69
--- /dev/null
+++ b/src/input/libdvdnav/dvdnav.c
@@ -0,0 +1,1136 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: dvdnav.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/*
+#define LOG_DEBUG
+*/
+
+#include <pthread.h>
+#include <dvdnav.h>
+#include "dvdnav_internal.h"
+#include "read_cache.h"
+
+#include <dvdread/nav_read.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * NOTE:
+ * All NLCK_*() function are not mutex locked, this made them reusable in
+ * a locked context. Take care.
+ *
+ */
+
+/* Current domain (backend to dvdnav_is_domain_() funcs) */
+static int8_t NLCK_dvdnav_is_domain(dvdnav_t *this, domain_t domain) {
+ dvd_state_t *state;
+
+ if((!this) || (!this->started) || (!this->vm))
+ return -1;
+
+ state = &(this->vm->state);
+
+ if(!state)
+ return -1;
+
+ return (state->domain == domain) ? 1 : 0;
+}
+
+static int8_t _dvdnav_is_domain(dvdnav_t *this, domain_t domain) {
+ int8_t retval;
+
+ pthread_mutex_lock(&this->vm_lock);
+ retval = NLCK_dvdnav_is_domain(this, domain);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return retval;
+}
+
+static int8_t NCLK_dvdnav_get_audio_logical_stream(dvdnav_t *this, uint8_t audio_num) {
+ dvd_state_t *state;
+ int8_t logical = -1;
+
+ if(!NLCK_dvdnav_is_domain(this, VTS_DOMAIN))
+ audio_num = 0;
+
+ state = &(this->vm->state);
+
+ if(audio_num < 8) {
+ if(state->pgc->audio_control[audio_num] & (1 << 15)) {
+ logical = (state->pgc->audio_control[audio_num] >> 8) & 0x07;
+ }
+ }
+
+ return logical;
+}
+
+static int8_t NCLK_dvdnav_get_spu_logical_stream(dvdnav_t *this, uint8_t subp_num) {
+ dvd_state_t *state;
+ ifo_handle_t *vtsi;
+
+ if(!this)
+ return -1;
+
+ state = &(this->vm->state);
+ vtsi = this->vm->vtsi;
+
+ if(subp_num >= vtsi->vtsi_mat->nr_of_vts_subp_streams)
+ return -1;
+
+ return vm_get_subp_stream(this->vm, subp_num, 0);
+}
+
+static int8_t NLCK_dvdnav_get_active_spu_stream(dvdnav_t *this) {
+ dvd_state_t *state;
+ int8_t subp_num;
+ int stream_num;
+
+ state = &(this->vm->state);
+ subp_num = state->SPST_REG & ~0x40;
+ stream_num = NCLK_dvdnav_get_spu_logical_stream(this, subp_num);
+
+ if(stream_num == -1)
+ for(subp_num = 0; subp_num < 32; subp_num++)
+ if(state->pgc->subp_control[subp_num] & (1 << 31)) {
+ stream_num = NCLK_dvdnav_get_spu_logical_stream(this, subp_num);
+ break;
+ }
+
+ return stream_num;
+}
+
+uint8_t dvdnav_get_video_aspect(dvdnav_t *this) {
+ uint8_t retval;
+
+ pthread_mutex_lock(&this->vm_lock);
+ retval = (uint8_t) vm_get_video_aspect(this->vm);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return retval;
+}
+
+uint8_t dvdnav_get_video_scale_permission(dvdnav_t *this) {
+ uint8_t retval;
+
+ pthread_mutex_lock(&this->vm_lock);
+ retval = (uint8_t) vm_get_video_scale_permission(this->vm);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return retval;
+}
+
+dvdnav_status_t dvdnav_clear(dvdnav_t * this) {
+ if (!this) {
+ printerr("Passed a NULL pointer");
+ return S_ERR;
+ }
+ /* clear everything except path, file, vm, mutex, readahead */
+
+ /* path */
+ if (this->file) DVDCloseFile(this->file);
+ this->file = NULL;
+ this->open_vtsN = -1;
+ this->open_domain = -1;
+
+ memset(&this->pci,0,sizeof(this->pci));
+ memset(&this->dsi,0,sizeof(this->dsi));
+
+ /* Set initial values of flags */
+ this->position_current.still = 0;
+ this->skip_still = 0;
+ this->stop = 0;
+ this->spu_clut_changed = 0;
+ this->started=0;
+ /* this->use_read_ahead */
+
+ dvdnav_read_cache_clear(this->cache);
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_open(dvdnav_t** dest, char *path) {
+ dvdnav_t *this;
+
+ /* Create a new structure */
+ (*dest) = NULL;
+ this = (dvdnav_t*)malloc(sizeof(dvdnav_t));
+ if(!this)
+ return S_ERR;
+ memset(this, 0, (sizeof(dvdnav_t) ) ); /* Make sure this structure is clean */
+
+ pthread_mutex_init(&this->vm_lock, NULL);
+ /* Initialise the error string */
+ printerr("");
+
+ /* Initialise the VM */
+ this->vm = vm_new_vm();
+ if(!this->vm) {
+ printerr("Error initialising the DVD VM");
+ return S_ERR;
+ }
+ if(vm_reset(this->vm, path) == -1) {
+ printerr("Error starting the VM / opening the DVD device");
+ return S_ERR;
+ }
+
+ /* Set the path. FIXME: Is a deep copy 'right' */
+ strncpy(this->path, path, MAX_PATH_LEN);
+
+ dvdnav_clear(this);
+
+ /* Pre-open and close a file so that the CSS-keys are cached. */
+ this->file = DVDOpenFile(vm_get_dvd_reader(this->vm), 0, DVD_READ_MENU_VOBS);
+ if (this->file) DVDCloseFile(this->file);
+ this->file = NULL;
+
+ //if(!this->started) {
+ // /* Start the VM */
+ // vm_start(this->vm);
+ // this->started = 1;
+ //}
+
+ /* Start the read-ahead cache. */
+ this->cache = dvdnav_read_cache_new(this);
+
+ (*dest) = this;
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_close(dvdnav_t *this) {
+ if(!this) {
+ printerr("Passed a NULL pointer");
+ return S_ERR;
+ }
+#ifdef LOG_DEBUG
+ fprintf(stderr,"dvdnav:close:called\n");
+#endif
+
+ if (this->file) {
+ DVDCloseFile(this->file);
+#ifdef LOG_DEBUG
+ fprintf(stderr,"dvdnav:close:file closing\n");
+#endif
+ this->file = NULL;
+ }
+
+ /* Free the VM */
+ if(this->vm) {
+ vm_free_vm(this->vm);
+ }
+ if (this->file) {
+ DVDCloseFile(this->file);
+#ifdef LOG_DEBUG
+ fprintf(stderr,"dvdnav:close2:file closing\n");
+#endif
+ this->file = NULL;
+ }
+ pthread_mutex_destroy(&this->vm_lock);
+
+ /* We leave the final freeing of the entire structure to the cache,
+ * because we don't know, if there are still buffers out in the wild,
+ * that must return first. */
+ if(this->cache) {
+ dvdnav_read_cache_free(this->cache);
+ } else free(this);
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_reset(dvdnav_t *this) {
+ dvdnav_status_t result;
+
+#ifdef LOG_DEBUG
+ printf("dvdnav:reset:called\n");
+#endif
+ if(!this) {
+ printerr("Passed a NULL pointer");
+ return S_ERR;
+ }
+#ifdef LOG_DEBUG
+ printf("getting lock\n");
+#endif
+ pthread_mutex_lock(&this->vm_lock);
+#ifdef LOG_DEBUG
+ printf("reseting vm\n");
+#endif
+ if(vm_reset(this->vm, NULL) == -1) {
+ printerr("Error restarting the VM");
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_ERR;
+ }
+#ifdef LOG_DEBUG
+ printf("clearing dvdnav\n");
+#endif
+ result=dvdnav_clear(this);
+#ifdef LOG_DEBUG
+ printf("starting vm\n");
+#endif
+// if(!this->started) {
+// /* Start the VM */
+// vm_start(this->vm);
+// this->started = 1;
+// }
+#ifdef LOG_DEBUG
+ printf("unlocking\n");
+#endif
+ pthread_mutex_unlock(&this->vm_lock);
+ return result;
+}
+
+dvdnav_status_t dvdnav_path(dvdnav_t *this, char** path) {
+ if(!this || !path || !(*path)) {
+ return S_ERR;
+ }
+
+ /* FIXME: Is shallow copy 'right'? */
+ (*path) = this->path;
+
+ return S_OK;
+}
+
+char* dvdnav_err_to_string(dvdnav_t *this) {
+ if(!this) {
+ /* Shold this be "passed a NULL pointer?" */
+ return "Hey! You gave me a NULL pointer you naughty person!";
+ }
+
+ return this->err_str;
+}
+
+/**
+ * Returns 1 if block contains NAV packet, 0 otherwise.
+ * Precesses said NAV packet if present.
+ *
+ * Most of the code in here is copied from xine's MPEG demuxer
+ * so any bugs which are found in that should be corrected here also.
+ */
+int dvdnav_decode_packet(dvdnav_t *this, uint8_t *p, dsi_t* nav_dsi, pci_t* nav_pci) {
+ int bMpeg1=0;
+ uint32_t nHeaderLen;
+ uint32_t nPacketLen;
+ uint32_t nStreamID;
+/* uint8_t *p_start=p; */
+
+
+ if (p==NULL) {
+ fprintf(stderr,"Passed a NULL pointer.\n");
+ return 0;
+ }
+
+ /* dprint("Checking packet...\n"); */
+
+ if (p[3] == 0xBA) { /* program stream pack header */
+
+ int nStuffingBytes;
+
+ /* xprintf (VERBOSE|DEMUX, "program stream pack header\n"); */
+
+ bMpeg1 = (p[4] & 0x40) == 0;
+
+ if (bMpeg1) {
+ p += 12;
+ } else { /* mpeg2 */
+ nStuffingBytes = p[0xD] & 0x07;
+ p += 14 + nStuffingBytes;
+ }
+ }
+
+
+ if (p[3] == 0xbb) { /* program stream system header */
+ int nHeaderLen;
+
+ nHeaderLen = (p[4] << 8) | p[5];
+ p += 6 + nHeaderLen;
+ }
+
+ /* we should now have a PES packet here */
+
+ if (p[0] || p[1] || (p[2] != 1)) {
+ fprintf(stderr,"demux error! %02x %02x %02x (should be 0x000001) \n",p[0],p[1],p[2]);
+ return 0;
+ }
+
+ nPacketLen = p[4] << 8 | p[5];
+ nStreamID = p[3];
+
+ nHeaderLen = 6;
+ p += nHeaderLen;
+
+ if (nStreamID == 0xbf) { /* Private stream 2 */
+/*
+ * int i;
+ * printf("dvdnav:nav packet=%u\n",p-p_start-6);
+ * for(i=0;i<80;i++) {
+ * printf("%02x ",p[i-6]);
+ * }
+ * printf("\n");
+ */
+ if(p[0] == 0x00) {
+ navRead_PCI(nav_pci, p+1);
+ }
+
+ p += nPacketLen;
+
+ /* We should now have a DSI packet. */
+ if(p[6] == 0x01) {
+ nPacketLen = p[4] << 8 | p[5];
+ p += 6;
+ /* dprint("NAV DSI packet\n"); */
+ navRead_DSI(nav_dsi, p+1);
+
+ }
+ return 1;
+ }
+ return 0;
+}
+
+/* DSI is used for most angle stuff.
+ * PCI is used for only non-seemless angle stuff
+ */
+int dvdnav_get_vobu(dvdnav_t *self, dsi_t* nav_dsi, pci_t* nav_pci, dvdnav_vobu_t* vobu) {
+ uint32_t next;
+ int angle, num_angle;
+
+ vobu->vobu_start = nav_dsi->dsi_gi.nv_pck_lbn; /* Absolute offset from start of disk */
+ vobu->vobu_length = nav_dsi->dsi_gi.vobu_ea; /* Relative offset from vobu_start */
+
+ /*
+ * If we're not at the end of this cell, we can determine the next
+ * VOBU to display using the VOBU_SRI information section of the
+ * DSI. Using this value correctly follows the current angle,
+ * avoiding the doubled scenes in The Matrix, and makes our life
+ * really happy.
+ *
+ * vobu_next is an offset value, 0x3fffffff = SRI_END_OF_CELL
+ * DVDs are about 6 Gigs, which is only up to 0x300000 blocks
+ * Should really assert if bit 31 != 1
+ */
+
+ /* Relative offset from vobu_start */
+ vobu->vobu_next = ( nav_dsi->vobu_sri.next_vobu & 0x3fffffff );
+
+ /* Old code -- may still be sueful one day
+ if(nav_dsi->vobu_sri.next_vobu != SRI_END_OF_CELL ) {
+ vobu->vobu_next = ( nav_dsi->vobu_sri.next_vobu & 0x3fffffff );
+ } else {
+ vobu->vobu_next = vobu->vobu_length;
+ } */
+
+ dvdnav_get_angle_info(self, &angle, &num_angle);
+#if 0
+ /* FIMXE: The angle reset doesn't work for some reason for the moment */
+
+ if((num_angle < angle) && (angle != 1)) {
+ printf("OOOOOOO angle ends!\n");
+
+ /* This is to switch back to angle one when we
+ * finish with angles. */
+ dvdnav_angle_change(self, 1);
+ }
+#endif
+
+ if(num_angle != 0) {
+ next = nav_pci->nsml_agli.nsml_agl_dsta[angle-1];
+
+ if(next != 0) {
+ if((next & 0x3fffffff) != 0) {
+ if(next & 0x80000000) {
+ vobu->vobu_next = - (int32_t)(next & 0x3fffffff);
+ } else {
+ vobu->vobu_next = + (int32_t)(next & 0x3fffffff);
+ }
+ }
+
+ } else if( nav_dsi->sml_agli.data[angle-1].address != 0 ) {
+ next = nav_dsi->sml_agli.data[angle-1].address;
+ vobu->vobu_length = nav_dsi->sml_pbi.ilvu_ea;
+
+ if((next & 0x80000000) && (next != 0x7fffffff)) {
+ vobu->vobu_next = - (int32_t)(next & 0x3fffffff);
+ } else {
+ vobu->vobu_next = + (int32_t)(next & 0x3fffffff);
+ }
+ }
+ }
+
+ return 1;
+}
+
+/* This is the main get_next_block function which actually gets the media stream video and audio etc.
+ * The use of this function is optional, with the application programmer
+ * free to implement their own version of this function
+ * FIXME: Make the function calls from here public API calls.
+ */
+
+dvdnav_status_t dvdnav_get_next_block(dvdnav_t *this, unsigned char *buf,
+ int *event, int *len) {
+ unsigned char *block;
+ dvdnav_status_t status;
+
+ block = buf;
+ status = dvdnav_get_next_cache_block(this, &block, event, len);
+ if (block != buf) {
+ /* we received a block from the cache, copy it, so we can give it back */
+ memcpy(buf, block, DVD_VIDEO_LB_LEN);
+ dvdnav_free_cache_block(this, block);
+ }
+ return status;
+}
+
+dvdnav_status_t dvdnav_get_next_cache_block(dvdnav_t *this, unsigned char **buf,
+ int *event, int *len) {
+ dvd_state_t *state;
+ int result;
+ if(!this || !event || !len || !buf || !*buf) {
+ printerr("Passed a NULL pointer");
+ return S_ERR;
+ }
+ pthread_mutex_lock(&this->vm_lock);
+
+ if(!this->started) {
+ /* Start the VM */
+ vm_start(this->vm);
+ this->started = 1;
+ }
+
+ state = &(this->vm->state);
+ (*event) = DVDNAV_NOP;
+ (*len) = 0;
+
+ /* Check the STOP flag */
+ if(this->stop) {
+ (*event) = DVDNAV_STOP;
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+
+ /* Check the STILLFRAME flag */
+ /* FIXME: Still cell, not still frame */
+ if(this->position_current.still != 0) {
+ dvdnav_still_event_t still_event;
+
+ still_event.length = this->position_current.still;
+
+ (*event) = DVDNAV_STILL_FRAME;
+ (*len) = sizeof(dvdnav_still_event_t);
+ memcpy(*buf, &(still_event), sizeof(dvdnav_still_event_t));
+
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+
+ vm_position_get(this->vm,&this->position_next);
+ /**********
+ fprintf(stderr, "POS-NEXT ");
+ vm_position_print(this->vm, &this->position_next);
+ fprintf(stderr, "POS-CUR ");
+ vm_position_print(this->vm, &this->position_current);
+ **********/
+
+ if(this->position_current.hop_channel != this->position_next.hop_channel) {
+ this->position_current.hop_channel = this->position_next.hop_channel;
+ (*event) = DVDNAV_HOP_CHANNEL;
+ (*len) = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+
+
+
+ if(this->spu_clut_changed) {
+ (*event) = DVDNAV_SPU_CLUT_CHANGE;
+#ifdef LOG_DEBUG
+ fprintf(stderr,"libdvdnav:SPU_CLUT_CHANGE\n");
+#endif
+ (*len) = sizeof(dvdnav_still_event_t);
+ memcpy(*buf, &(state->pgc->palette), 16 * sizeof(uint32_t));
+ this->spu_clut_changed = 0;
+#ifdef LOG_DEBUG
+ fprintf(stderr,"libdvdnav:SPU_CLUT_CHANGE returning S_OK\n");
+#endif
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+
+ if(this->position_current.spu_channel != this->position_next.spu_channel) {
+ dvdnav_spu_stream_change_event_t stream_change;
+ (*event) = DVDNAV_SPU_STREAM_CHANGE;
+#ifdef LOG_DEBUG
+ fprintf(stderr,"libdvdnav:SPU_STREAM_CHANGE\n");
+#endif
+ (*len) = sizeof(dvdnav_spu_stream_change_event_t);
+ stream_change.physical_wide = vm_get_subp_active_stream(this->vm, 0);
+ stream_change.physical_letterbox = vm_get_subp_active_stream(this->vm, 1);
+ stream_change.physical_pan_scan = vm_get_subp_active_stream(this->vm, 2);
+ memcpy(*buf, &(stream_change), sizeof( dvdnav_spu_stream_change_event_t));
+ this->position_current.spu_channel = this->position_next.spu_channel;
+#ifdef LOG_DEBUG
+ fprintf(stderr,"libdvdnav:SPU_STREAM_CHANGE stream_id_wide=%d\n",stream_change.physical_wide);
+ fprintf(stderr,"libdvdnav:SPU_STREAM_CHANGE stream_id_letterbox=%d\n",stream_change.physical_letterbox);
+ fprintf(stderr,"libdvdnav:SPU_STREAM_CHANGE stream_id_pan_scan=%d\n",stream_change.physical_pan_scan);
+#endif
+ pthread_mutex_unlock(&this->vm_lock);
+ if (stream_change.physical_wide != -1 &&
+ stream_change.physical_letterbox != -1 &&
+ stream_change.physical_pan_scan != -1) {
+#ifdef LOG_DEBUG
+ fprintf(stderr,"libdvdnav:SPU_STREAM_CHANGE returning S_OK\n");
+#endif
+ return S_OK;
+ }
+ }
+
+ if(this->position_current.audio_channel != this->position_next.audio_channel) {
+ dvdnav_audio_stream_change_event_t stream_change;
+ (*event) = DVDNAV_AUDIO_STREAM_CHANGE;
+#ifdef LOG_DEBUG
+ fprintf(stderr,"libdvdnav:AUDIO_STREAM_CHANGE\n");
+#endif
+ (*len) = sizeof(dvdnav_audio_stream_change_event_t);
+ stream_change.physical= vm_get_audio_active_stream( this->vm );
+ memcpy(*buf, &(stream_change), sizeof( dvdnav_audio_stream_change_event_t));
+ this->position_current.audio_channel = this->position_next.audio_channel;
+#ifdef LOG_DEBUG
+ fprintf(stderr,"libdvdnav:AUDIO_STREAM_CHANGE stream_id=%d returning S_OK\n",stream_change.physical);
+#endif
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+
+ /* Check the HIGHLIGHT flag */
+ /* FIXME: Use BUTTON instead of HIGHLIGHT. */
+ if(this->position_current.button != this->position_next.button) {
+ dvdnav_highlight_event_t hevent;
+
+ hevent.display = 1;
+ hevent.buttonN = this->position_next.button;
+
+ this->position_current.button = this->position_next.button;
+
+ (*event) = DVDNAV_HIGHLIGHT;
+ (*len) = sizeof(hevent);
+ memcpy(*buf, &(hevent), sizeof(hevent));
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+
+ /* Check to see if we need to change the currently opened VOB */
+ if((this->position_current.vts != this->position_next.vts) ||
+ (this->position_current.domain != this->position_next.domain)) {
+ dvd_read_domain_t domain;
+ int vtsN;
+ dvdnav_vts_change_event_t vts_event;
+
+ if(this->file) {
+ dvdnav_read_cache_clear(this->cache);
+ DVDCloseFile(this->file);
+ this->file = NULL;
+ }
+
+ vts_event.old_vtsN = this->open_vtsN;
+ vts_event.old_domain = this->open_domain;
+
+ /* Use the current DOMAIN to find whether to open menu or title VOBs */
+ switch(this->position_next.domain) {
+ case FP_DOMAIN:
+ case VMGM_DOMAIN:
+ domain = DVD_READ_MENU_VOBS;
+ vtsN = 0;
+ break;
+ case VTSM_DOMAIN:
+ domain = DVD_READ_MENU_VOBS;
+ vtsN = this->position_next.vts;
+ break;
+ case VTS_DOMAIN:
+ domain = DVD_READ_TITLE_VOBS;
+ vtsN = this->position_next.vts;
+ break;
+ default:
+ printerr("Unknown domain when changing VTS.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_ERR;
+ }
+
+ this->position_current.vts = this->position_next.vts;
+ this->position_current.domain = this->position_next.domain;
+ dvdnav_read_cache_clear(this->cache);
+ this->file = DVDOpenFile(vm_get_dvd_reader(this->vm), vtsN, domain);
+ vts_event.new_vtsN = this->position_next.vts;
+ vts_event.new_domain = this->position_next.domain;
+
+ /* If couldn't open the file for some reason, moan */
+ if(this->file == NULL) {
+ printerrf("Error opening vtsN=%i, domain=%i.", vtsN, domain);
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_ERR;
+ }
+
+ /* File opened successfully so return a VTS change event */
+ (*event) = DVDNAV_VTS_CHANGE;
+ memcpy(*buf, &(vts_event), sizeof(vts_event));
+ (*len) = sizeof(vts_event);
+
+ /* On a VTS change, we want to disable any highlights which
+ * may have been shown (FIXME: is this valid?) */
+ this->spu_clut_changed = 1;
+ this->position_current.cell = -1; /* Force an update */
+ this->position_current.spu_channel = -1; /* Force an update */
+ this->position_current.audio_channel = -1; /* Force an update */;
+
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+ /* FIXME: Don't really need "cell", we only need vobu_start */
+ if( (this->position_current.cell != this->position_next.cell) ||
+ (this->position_current.cell_restart != this->position_next.cell_restart) ||
+ (this->position_current.vobu_start != this->position_next.vobu_start) ||
+ (this->position_current.vobu_next != this->position_next.vobu_next) ) {
+ this->position_current.cell = this->position_next.cell;
+ this->position_current.cell_restart = this->position_next.cell_restart;
+ /* vobu_start changes when PGC or PG changes. */
+ this->position_current.vobu_start = this->position_next.vobu_start;
+ this->position_current.vobu_next = this->position_next.vobu_next;
+ /* FIXME: Need to set vobu_start, vobu_next */
+ this->vobu.vobu_start = this->position_next.vobu_start;
+ /* vobu_next is use for mid cell resumes */
+ this->vobu.vobu_next = this->position_next.vobu_next;
+ this->vobu.vobu_length = 0;
+ this->vobu.blockN = this->vobu.vobu_length + 1;
+ /* Make blockN > vobu_lenght to do expected_nav */
+ (*event) = DVDNAV_CELL_CHANGE;
+ (*len) = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+
+
+ if (this->vobu.blockN > this->vobu.vobu_length) {
+ /* End of VOBU */
+
+ if(this->vobu.vobu_next == SRI_END_OF_CELL) {
+ /* End of Cell from NAV DSI info */
+#ifdef LOG_DEBUG
+ fprintf(stderr, "Still set to %x\n", this->position_next.still);
+#endif
+ this->position_current.still = this->position_next.still;
+
+ if( this->position_current.still == 0 || this->skip_still ) {
+ vm_get_next_cell(this->vm);
+ vm_position_get(this->vm,&this->position_next);
+ /* FIXME: Need to set vobu_start, vobu_next */
+ this->position_current.still = 0; /* still gets activated at end of cell */
+ this->skip_still = 0;
+ this->position_current.cell = this->position_next.cell;
+ this->position_current.vobu_start = this->position_next.vobu_start;
+ this->position_current.vobu_next = this->position_next.vobu_next;
+ this->vobu.vobu_start = this->position_next.vobu_start;
+ /* vobu_next is use for mid cell resumes */
+ this->vobu.vobu_next = this->position_next.vobu_next;
+ this->vobu.vobu_length = 0;
+ this->vobu.blockN = this->vobu.vobu_length + 1;
+ /* Make blockN > vobu_next to do expected_nav */
+ (*event) = DVDNAV_CELL_CHANGE;
+ (*len) = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ } else {
+ dvdnav_still_event_t still_event;
+ still_event.length = this->position_current.still;
+ (*event) = DVDNAV_STILL_FRAME;
+ (*len) = sizeof(dvdnav_still_event_t);
+ memcpy(*buf, &(still_event), sizeof(dvdnav_still_event_t));
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+
+ /* Only set still after whole VOBU has been output. */
+ /*
+ if(this->position_next.still != 0) {
+ this->position_current.still = this->position_next.still;
+ }
+ */
+
+ }
+ /* Perform the jump if necessary (this is always a
+ * VOBU boundary). */
+
+ //result = DVDReadBlocks(this->file, this->vobu.vobu_start + this->vobu.vobu_next, 1, buf);
+ result = dvdnav_read_cache_block(this->cache, this->vobu.vobu_start + this->vobu.vobu_next, 1, buf);
+
+ if(result <= 0) {
+ printerr("Error reading NAV packet.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_ERR;
+ }
+ /* Decode nav into pci and dsi. */
+ /* Then get next VOBU info. */
+ if(dvdnav_decode_packet(this, *buf, &this->dsi, &this->pci) == 0) {
+ printerr("Expected NAV packet but none found.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_ERR;
+ }
+ dvdnav_get_vobu(this, &this->dsi,&this->pci, &this->vobu);
+ this->vobu.blockN=1;
+ /* FIXME: We need to update the vm state->blockN with which VOBU we are in.
+ * This is so RSM resumes to the VOBU level and not just the CELL level.
+ * This should be implemented with a new Public API call.
+ */
+ /* We cache one past the end of the VOBU,
+ * in the hope it might catch the next NAV packet as well.
+ * This reduces the amount of read commands sent to the DVD device.
+ * A cache miss will only happen for 3 reasons.
+ * 1) Seeking
+ * 2) Menu change
+ * 3) The next VOBU does not immeadiately follow the current one. E.g. Multi Angles, ILVU.
+ */
+ dvdnav_pre_cache_blocks(this->cache, this->vobu.vobu_start+1, this->vobu.vobu_length+1);
+
+ /* Successfully got a NAV packet */
+ (*event) = DVDNAV_NAV_PACKET;
+ (*len) = 2048;
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+
+ /* If we've got here, it must just be a normal block. */
+ if(!this->file) {
+ printerr("Attempting to read without opening file");
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_ERR;
+ }
+
+ result = dvdnav_read_cache_block(this->cache, this->vobu.vobu_start + this->vobu.blockN, 1, buf);
+ if(result <= 0) {
+ printerr("Error reading from DVD.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_ERR;
+ }
+ this->vobu.blockN++;
+ (*len) = 2048;
+ (*event) = DVDNAV_BLOCK_OK;
+
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+}
+
+uint16_t dvdnav_audio_stream_to_lang(dvdnav_t *this, uint8_t stream) {
+ audio_attr_t attr;
+
+ if(!this)
+ return -1;
+
+ pthread_mutex_lock(&this->vm_lock);
+ attr = vm_get_audio_attr(this->vm, stream);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ if(attr.lang_type != 1)
+ return 0xffff;
+
+ return attr.lang_code;
+}
+
+int8_t dvdnav_get_audio_logical_stream(dvdnav_t *this, uint8_t audio_num) {
+ int8_t retval;
+
+ if(!this)
+ return -1;
+
+ pthread_mutex_lock(&this->vm_lock);
+ retval = NCLK_dvdnav_get_audio_logical_stream(this, audio_num);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return retval;
+}
+
+uint16_t dvdnav_spu_stream_to_lang(dvdnav_t *this, uint8_t stream) {
+ subp_attr_t attr;
+
+ if(!this)
+ return -1;
+
+ pthread_mutex_lock(&this->vm_lock);
+ attr = vm_get_subp_attr(this->vm, stream);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ if(attr.type != 1)
+ return 0xffff;
+
+ return attr.lang_code;
+}
+
+int8_t dvdnav_get_spu_logical_stream(dvdnav_t *this, uint8_t subp_num) {
+ int8_t retval;
+
+ if(!this)
+ return -1;
+
+ pthread_mutex_lock(&this->vm_lock);
+ retval = NCLK_dvdnav_get_spu_logical_stream(this, subp_num);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return retval;
+}
+
+int8_t dvdnav_get_active_spu_stream(dvdnav_t *this) {
+ int8_t retval;
+
+ if(!this)
+ return -1;
+
+ pthread_mutex_lock(&this->vm_lock);
+ retval = NLCK_dvdnav_get_active_spu_stream(this);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return retval;
+}
+
+/* First Play domain. (Menu) */
+int8_t dvdnav_is_domain_fp(dvdnav_t *this) {
+ return _dvdnav_is_domain(this, FP_DOMAIN);
+}
+/* Video management Menu domain. (Menu) */
+int8_t dvdnav_is_domain_vmgm(dvdnav_t *this) {
+ return _dvdnav_is_domain(this, VMGM_DOMAIN);
+}
+/* Video Title Menu domain (Menu) */
+int8_t dvdnav_is_domain_vtsm(dvdnav_t *this) {
+ return _dvdnav_is_domain(this, VTSM_DOMAIN);
+}
+/* Video Title domain (playing movie). */
+int8_t dvdnav_is_domain_vts(dvdnav_t *this) {
+ return _dvdnav_is_domain(this, VTS_DOMAIN);
+}
+
+/* Generally delegate angle information handling to
+ * VM */
+dvdnav_status_t dvdnav_angle_change(dvdnav_t *this, int angle) {
+ int num, current;
+
+ if(!this) {
+ return S_ERR;
+ }
+
+ if(dvdnav_get_angle_info(this, &current, &num) != S_OK) {
+ printerr("Error getting angle info");
+ return S_ERR;
+ }
+
+ /* Set angle SPRM if valid */
+ if((angle > 0) && (angle <= num)) {
+ this->vm->state.AGL_REG = angle;
+ } else {
+ printerr("Passed an invalid angle number");
+ return S_ERR;
+ }
+
+ return S_OK;
+}
+/* FIXME: change order of current_angle, number_of_angles */
+dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *this, int* current_angle,
+ int *number_of_angles) {
+ if(!this || !this->vm) {
+ return S_ERR;
+ }
+
+ if(!current_angle || !number_of_angles) {
+ printerr("Passed a NULL pointer");
+ return S_ERR;
+ }
+
+ vm_get_angle_info(this->vm, number_of_angles, current_angle);
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_get_cell_info(dvdnav_t *this, int* current_angle,
+ int *number_of_angles) {
+ if(!this || !this->vm) {
+ return S_ERR;
+ }
+ *current_angle=this->position_next.cell;
+ return S_OK;
+}
+
+pci_t* dvdnav_get_current_nav_pci(dvdnav_t *this) {
+ if(!this || !this->vm) return 0;
+ return &this->pci;
+}
+
+dsi_t* dvdnav_get_current_nav_dsi(dvdnav_t *this) {
+ if(!this || !this->vm) return 0;
+ return &this->dsi;
+}
+
+uint32_t dvdnav_get_next_still_flag(dvdnav_t *this) {
+ if(!this || !this->vm) {
+ return S_ERR;
+ }
+ return this->position_next.still;
+}
+
+/*
+ * $Log: dvdnav.c,v $
+ * Revision 1.1 2002/08/08 17:49:21 richwareham
+ * First stage of DVD plugin -> dvdnav conversion
+ *
+ * Revision 1.29 2002/07/25 14:51:40 richwareham
+ * Moved get_current_nav_pci into dvdnac.c, changed example to use it instead of 'home-rolled'
+ * check_packet.
+ *
+ * Revision 1.28 2002/07/25 14:42:33 richwareham
+ * Patch from aschultz@cs.uni-magdeburg.de to allow for still-frame 'peek-ahead'
+ *
+ * Revision 1.27 2002/07/12 15:46:44 mroi
+ * use new memcopy'less read ahead cache
+ *
+ * Revision 1.26 2002/07/06 16:24:54 mroi
+ * * fix debug messages
+ * * send spu stream change event only, when there are new streams
+ * (should fix problems with Terminator disk 2)
+ *
+ * Revision 1.25 2002/07/05 14:18:54 mroi
+ * report all spu types (widescreen, letterbox and pan&scan), not widescreen
+ * only and report the stream's scale permissions to detect pan&scan material
+ *
+ * Revision 1.24 2002/07/05 01:42:30 jcdutton
+ * Add more debug info for Menu language selection.
+ * Only do vm_start when we have to.
+ *
+ * Revision 1.23 2002/07/02 22:57:09 jcdutton
+ * Rename some of the functions in vm.c to help readability.
+ * Hopefully fix __FUNCTION__ problem. Use __func_ as recommended in C99.
+ * Fix bug where libdvdnav would not immeadiately replay the same cell due to menu buttons.
+ *
+ * Revision 1.22 2002/06/25 13:37:11 jcdutton
+ * Revert back to old read_cache method.
+ * Some new optimizations added to the old read_cache method, thus reducing the amount of calls to read blocks from the DVD device.
+ *
+ * Revision 1.21 2002/06/06 15:03:09 richwareham
+ * Biiiiiiig change to let doxygen generate some docs for the library. Note that I'm in no way sure that the autoconf stuff plays nice.
+ *
+ * Revision 1.20 2002/06/04 13:35:16 richwareham
+ * Removed more C++ style comments
+ *
+ * Revision 1.19 2002/05/30 23:15:14 richwareham
+ * First draft of removing HAVE_DVDREAD9
+ *
+ * Revision 1.18 2002/05/30 09:52:29 richwareham
+ * 'Objectified' the read-ahead cache in preparation to implement a 'proper' threaded cache a-la that recommended in the DVD Demystified book.
+ *
+ * Revision 1.17 2002/05/09 11:57:24 richwareham
+ * Angles now work (still a few wrinkles though -- e.g. angle does not reset to '1' when returning to menus)
+ *
+ * Revision 1.16 2002/04/24 21:15:25 jcdutton
+ * Quiet please!!!
+ *
+ * Revision 1.15 2002/04/24 00:47:46 jcdutton
+ * Some more cleanups.
+ * Improve button passing.
+ *
+ * Revision 1.14 2002/04/23 13:26:08 jcdutton
+ * Add some comments, FIXMEs.
+ * The main point being that dvdnav_get_next_block is almost in a state where it can be optional whether the application programmer uses it, or implements their own version of the function. That is been the main reason for the re-write of this function recently.
+ *
+ * Revision 1.13 2002/04/23 12:55:40 jcdutton
+ * Removed un-needed variables.
+ * General Clean up.
+ *
+ * Revision 1.12 2002/04/23 12:34:39 f1rmb
+ * Why rewrite vm function, use it instead (this remark is for me, of course ;-) ).
+ * Comment unused var, shut compiler warnings.
+ *
+ * Revision 1.11 2002/04/23 02:12:27 jcdutton
+ * Re-implemented seeking.
+ *
+ * Revision 1.10 2002/04/23 00:07:16 jcdutton
+ * Name stills work better.
+ *
+ * Revision 1.9 2002/04/22 22:00:48 jcdutton
+ * Start of rewrite of libdvdnav. Still need to re-implement seeking.
+ *
+ * Revision 1.8 2002/04/22 20:57:14 f1rmb
+ * Change/fix SPU active stream id. Same for audio. Few new functions, largely
+ * inspired from libogle ;-).
+ *
+ * Revision 1.7 2002/04/10 16:45:57 jcdutton
+ * Actually fix the const this time!
+ *
+ * Revision 1.6 2002/04/07 14:10:11 richwareham
+ * Stop C++ bitching about some things and extend the menus example
+ *
+ * Revision 1.5 2002/04/06 18:42:05 jcdutton
+ * Slight correction to handle quicker menu transitions.
+ *
+ * Revision 1.4 2002/04/06 18:31:50 jcdutton
+ * Some cleaning up.
+ * changed exit(1) to assert(0) so they actually get seen by the user so that it helps developers more.
+ *
+ * Revision 1.3 2002/04/02 18:22:27 richwareham
+ * Added reset patch from Kees Cook <kees@outflux.net>
+ *
+ * Revision 1.2 2002/04/01 18:56:28 richwareham
+ * Added initial example programs directory and make sure all debug/error output goes to stderr.
+ *
+ * Revision 1.1.1.1 2002/03/12 19:45:57 richwareham
+ * Initial import
+ *
+ * Revision 1.28 2002/02/02 23:26:20 richwareham
+ * Restored title selection
+ *
+ * Revision 1.27 2002/02/01 15:48:10 richwareham
+ * Re-implemented angle selection and title/chapter display
+ *
+ * Revision 1.26 2002/01/31 16:53:49 richwareham
+ * Big patch from Daniel Caujolle-Bert to (re)implement SPU/Audio language display
+ *
+ * Revision 1.25 2002/01/24 20:53:50 richwareham
+ * Added option to _not_ use DVD read-ahead to options
+ *
+ * Revision 1.24 2002/01/20 15:54:59 jcdutton
+ * Implement seeking.
+ * It is still a bit buggy, but works sometimes.
+ * I need to find out how to make the jump clean.
+ * At the moment, some corruption of the mpeg2 stream occurs,
+ * which causes libmpeg2 to crash.
+ *
+ * Revision 1.23 2002/01/18 00:23:52 jcdutton
+ * Support Ejecting of DVD.
+ * It will first un-mount the DVD, then eject it.
+ *
+ * Revision 1.22 2002/01/17 14:50:32 jcdutton
+ * Fix corruption of stream during menu transitions.
+ * Menu transitions are now clean.
+ *
+ * Revision 1.21 2002/01/15 00:37:03 jcdutton
+ * Just a few cleanups, and a assert fix. (memset fixed it)
+ *
+ * Revision 1.20 2002/01/13 22:17:57 jcdutton
+ * Change logging.
+ *
+ *
+ */
diff --git a/src/input/libdvdnav/dvdnav.h b/src/input/libdvdnav/dvdnav.h
new file mode 100644
index 000000000..4d03341cc
--- /dev/null
+++ b/src/input/libdvdnav/dvdnav.h
@@ -0,0 +1,879 @@
+/*
+ * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: dvdnav.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+/**
+ * \file dvdnav.h
+ * The main file you should include if you want to access dvdnav
+ * functionality.
+ */
+
+#ifndef DVDNAV_H_INCLUDED
+#define DVDNAV_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Defines the various events and dvdnav_event_t */
+#include "dvdnav_events.h"
+
+/* Various useful types */
+#include "dvd_types.h"
+
+#include <dvdread/dvd_reader.h>
+
+/**
+ * Opaque data-type can be viewed as a 'DVD handle'. You should get
+ * a pointer to a dvdnav_t from the dvdnav_open() function.
+ * \sa dvdnav_open()
+ */
+typedef struct dvdnav_s dvdnav_t;
+
+/* Status */
+typedef int dvdnav_status_t;
+
+#define DVDNAV_STATUS_ERR 0
+#define DVDNAV_STATUS_OK 1
+
+/**
+ * NOTE: */
+
+/**
+ * \defgroup init Initialisation & housekeeping functions
+ * These functions allow you to open a DVD device and associate it
+ * with a dvdnav_t.
+ *
+ * Unless otherwise stated, all functions return DVDNAV_STATUS_OK if
+ * they succeeded, otherwise DVDNAV_STATUS_ERR is returned and the error may
+ * be obtained by calling dvdnav_err_to_string().
+ *
+ * A minimal <tt>libdvdnav</tt> program should always call dvdnav_open()
+ * and dvdnav_close().
+ *
+ * \par Example:
+ * \include minimal.c
+ *
+ * @{
+ */
+
+/**
+ * Attempts to open the DVD drive at the specifiec path and pre-cache
+ * any CSS-keys that your hacked libdvdread may use.
+ *
+ * \param dest Pointer to a dvdnav_t pointer to fill in.
+ * \param path Any libdvdread acceptable path
+ */
+dvdnav_status_t dvdnav_open(dvdnav_t** dest, char *path);
+
+/**
+ * Closes a dvdnav_t previously opened with dvdnav_open(), freeing any
+ * memory associated with it.
+ *
+ * \param self dvdnav_t to close.
+ */
+dvdnav_status_t dvdnav_close(dvdnav_t *self);
+
+/**
+ * Resets the virtual machine and buffers in a previously opened dvdnav
+ *
+ * \param self dvdnav_t to reset.
+ */
+dvdnav_status_t dvdnav_reset(dvdnav_t *self);
+
+/**
+ * Fills a pointer with a value pointing to a string describing
+ * the path associated with an open dvdnav_t. It assigns it NULL
+ * on error.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param path Pointer to char* to fill in.
+ */
+dvdnav_status_t dvdnav_path(dvdnav_t *self, char** path);
+
+/**
+ * Returns a human-readable string describing the last error.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \returns A pointer to said string.
+ */
+char* dvdnav_err_to_string(dvdnav_t *self);
+
+/** \@} */
+
+/**
+ * \defgroup char Changing and Reading DVD Player characteristics
+ *
+ * These functions allow you to manipulate the various global characteristics
+ * of the DVD playback engine.
+ *
+ * @{
+ */
+
+/**
+ * Returns the region mask (bit 0 set implies region 1, bit 1 set implies
+ * region 2, etc) of the virtual machine. Generally you will only need to set
+ * this if you are playing RCE discs which query the virtual machine as to its
+ * region setting.
+ *
+ * \par Note:
+ * This has <b>nothing</b> to do with the region setting of the DVD drive.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param region Pointer to an int which will receive the region code mask.
+ */
+dvdnav_status_t dvdnav_get_region_mask(dvdnav_t *self, int *region);
+
+/**
+ * Sets the region mask of the virtual machine.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param mask 0x00..0xff -- the desired region mask.
+ *
+ * \sa dvdnav_get_region_mask()
+ */
+dvdnav_status_t dvdnav_set_region_mask(dvdnav_t *self, int mask);
+
+/**
+ * Specify whether read-ahead caching should be used. You may not want this if your
+ * decoding engine does its own buffering or if you don't like the fact that this is
+ * implemented in a multithreaded manner.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param use_readahead 0 - no, 1 - yes
+ */
+dvdnav_status_t dvdnav_set_readahead_flag(dvdnav_t *self, int use_readahead);
+
+/**
+ * Query whether readahead caching/buffering will be used.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param flag Pointer to int to recieve flag value.
+ *
+ * \sa dvdnav_get_readahead_flag()
+ */
+dvdnav_status_t dvdnav_get_readahead_flag(dvdnav_t *self, int* flag);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup data Reading Data
+ *
+ * These functions are used to poll the playback enginge and actually get data
+ * off the DVD.
+ *
+ * @{
+ */
+
+/**
+ * Attempts to get the next block off the DVD and copies it into the buffer 'buf'.
+ * If there is any special actions that may need to be performed, the value
+ * pointed to by 'event' gets set
+ * accordingly.
+ *
+ * If 'event' is DVDNAV_BLOCK_OK then 'buf' is filled with the next block
+ * (note that means it has to be at /least/ 2048 bytes big). 'len' is
+ * then set to 2048.
+ *
+ * Otherwise, buf is filled with an appropriate event structure and
+ * len is set to the length of that structure.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param buf Buffer (at least 2048 octets) to fill with next block/event structure.
+ * \param event Pointer to int to get event type.
+ * \param len Pointer to int to get the number of octets written into buf.
+ */
+dvdnav_status_t dvdnav_get_next_block(dvdnav_t *self, unsigned char *buf,
+ int *event, int *len);
+
+/**
+ * This basically does the same as dvdnav_get_next_block. The only difference is
+ * that it avoids a memcopy, when the requested block was found in the cache.
+ * I such a case (cache hit) this function will return a different pointer than
+ * the one handed in, pointing directly into the relevant block in the cache.
+ * Those pointer must _never_ be freed but instead returned to the library via
+ + dvdnav_free_cache_block.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param buf Buffer (at least 2048 octets) to fill with next block/event structure.
+ * A different buffer might be returned, if the block was found in the internal cache.
+ * \param event Pointer to int to get event type.
+ * \param len Pointer to int to get the number of octets written into buf.
+ */
+dvdnav_status_t dvdnav_get_next_cache_block(dvdnav_t *self, unsigned char **buf,
+ int *event, int *len);
+
+/**
+ * All buffers which came from the internal cache (when dvdnav_get_next_cache_block
+ * returned a buffer different from the one handed in) have to be freed with this
+ * function. Although handing in other buffers not from the cache doesn't cause any harm.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param buf Buffer received from internal cache.
+ */
+dvdnav_status_t dvdnav_free_cache_block(dvdnav_t *self, unsigned char *buf);
+
+/**
+ * Get video aspect code.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ *
+ * \returns Video aspect ratio code, 0 -- 4:3, 2 -- 16:9
+ */
+uint8_t dvdnav_get_video_aspect(dvdnav_t *self);
+
+/**
+ * Get video scaling permissions.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ *
+ * \returns Video scaling permissions, bit0 - deny letterboxing, bit1 - deny pan&scan
+ */
+uint8_t dvdnav_get_video_scale_permission(dvdnav_t *self);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup nav Navigation Commands
+ *
+ * @{
+ */
+
+/**
+ * Returns the number of titles on the disk.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param titles Pointer to int to receive number of titles.
+ */
+dvdnav_status_t dvdnav_get_number_of_titles(dvdnav_t *self, int *titles);
+
+/**
+ * Returns the number of programs within the current title.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param programs Pointer to int to receive number of programs.
+ */
+dvdnav_status_t dvdnav_get_number_of_programs(dvdnav_t *self, int *programs);
+
+/**
+ * If we are currently in a still-frame this function skips it (or attempts to).
+ * This might fail if this still-frame is of infinite duration as most DVD
+ * authors wouldn't expect you to be able to do this <tt>:)</tt>
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+dvdnav_status_t dvdnav_still_skip(dvdnav_t *self);
+
+/**
+ * Plays a specified title of the DVD.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param title 1..99 -- Title number to play.
+ */
+dvdnav_status_t dvdnav_title_play(dvdnav_t *self, int title);
+
+/**
+ * Plays the specifiec title, starting from the specified
+ * part (chapter).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param title 1..99 -- Title number to play.
+ * \param part 1..999 -- Part to start from.
+ */
+dvdnav_status_t dvdnav_part_play(dvdnav_t *self, int title, int part);
+
+/**
+ * Play the specified amount of parts of the specified title of
+ * the DVD then STOP.
+ *
+ * \par Note:
+ * Currently unimplemented!
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param title 1..99 -- Title number to play.
+ * \param part 1..999 -- Part to start from.
+ * \param parts_to_play 1..999 -- Number of parts to play.
+ */
+dvdnav_status_t dvdnav_part_play_auto_stop(dvdnav_t *self, int title,
+ int part, int parts_to_play);
+
+/**
+ * Play the specified title starting from the specified time
+ *
+ * \par Note:
+ * Currently unimplemented!
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param title 1..99 -- Title number to play.
+ * \param time Timecode to start from (hours, minutes, seconds + frames).
+ */
+dvdnav_status_t dvdnav_time_play(dvdnav_t *self, int title,
+ unsigned long int time);
+
+/**
+ * Stops playing the current title (causes a STOP action in
+ * dvdnav_get_next_block()).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ *
+ * \sa dvdnav_get_next_block()
+ */
+dvdnav_status_t dvdnav_stop(dvdnav_t *self);
+
+/**
+ * Stop playing current title and play the "GoUp"-program chain
+ * (which generally leads to the title menu or a higer-level menu).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+dvdnav_status_t dvdnav_go_up(dvdnav_t *self);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup search Searching
+ *
+ * @{
+ */
+
+/**
+ * Stop playing the current title and start playback of the title
+ * from the specified timecode.
+ *
+ * \par Note:
+ * Currently unimplemented!
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param time Timecode to start from.
+ */
+dvdnav_status_t dvdnav_time_search(dvdnav_t *self,
+ unsigned long int time);
+
+/**
+ * Stop playing the current title and start playback of the title
+ * from the specified sector offset.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param offset Sector offset to start from.
+ * \param origin Start from here, start or end.
+ */
+dvdnav_status_t dvdnav_sector_search(dvdnav_t *self,
+ unsigned long int offset, int origin);
+
+/**
+ * Stop playing the current title and start playback of the title
+ * from the specified part (chapter).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param part 1..999 -- Part to start from.
+ */
+dvdnav_status_t dvdnav_part_search(dvdnav_t *self, int part);
+
+/**
+ * Stop playing the current title and start playback of the title
+ * from the previous program (if it exists).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+dvdnav_status_t dvdnav_prev_pg_search(dvdnav_t *self);
+
+/**
+ * Stop playing the current title and start playback of the title
+ * from the first program.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+dvdnav_status_t dvdnav_top_pg_search(dvdnav_t *self);
+
+/**
+ * Stop playing the current title and start playback of the title
+ * from the next program (if it exists).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *self);
+
+/**
+ * Stop playing the current title and jump to the specified menu.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param menu Which menu to call (see DVDMenuID_t).
+ *
+ * \sa DVDMenuID_t (from <tt>libdvdread</tt>)
+ */
+dvdnav_status_t dvdnav_menu_call(dvdnav_t *self, DVDMenuID_t menu);
+
+/**
+ * Return the title number and chapter currently being played or
+ * -1 if in a menu.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param title Pointer to into which will receive the current title number.
+ * \param part Pointer to into which will receive the current part number.
+ */
+dvdnav_status_t dvdnav_current_title_info(dvdnav_t *self, int *title,
+ int *part);
+
+/**
+ * Return a string describing the title. This is an ID string encoded on the
+ * disc byt the author. In many cases this is a descriptive string such as
+ * `<tt>THE_MATRIX</tt>' but sometimes is sigularly uninformative such as
+ * `<tt>PDVD-011421</tt>'.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param title_str Pointer to C-style string to receive a string describing the title.
+ */
+dvdnav_status_t dvdnav_get_title_string(dvdnav_t *self, char **title_str);
+
+/**
+ * Return the current position (in blocks) within the current
+ * part and the length (in blocks) of said part.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param pos Pointer to unsigned int to get the current position.
+ * \param len Pointer to unsinged int to hold the length of the current part.
+ */
+dvdnav_status_t dvdnav_get_position(dvdnav_t *self, unsigned int* pos,
+ unsigned int *len);
+
+/**
+ * Return the current position (in blocks) within the current
+ * title and the length (in blocks) of said title.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param pos Pointer to unsigned int to get the current position.
+ * \param len Pointer to unsinged int to hold the length of the current title.
+ */
+dvdnav_status_t dvdnav_get_position_in_title(dvdnav_t *self,
+ unsigned int* pos,
+ unsigned int *len);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup highlight Highlights
+ *
+ * @{
+ */
+
+/**
+ * Get the currently highlighted button
+ * number (1..36) or 0 if no button is highlighed.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param button Pointer to the value to fill in.
+ */
+dvdnav_status_t dvdnav_get_current_highlight(dvdnav_t *self, int* button);
+
+/**
+ * Returns the Presentation Control Information (PCI) structure associated
+ * with the current position.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ *
+ * \sa pci_t (in <tt>libdvdread</tt>)
+ */
+pci_t* dvdnav_get_current_nav_pci(dvdnav_t *self);
+
+/**
+ * Returns the DSI (data seach information) structure associated
+ * with the current position.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ *
+ * \sa pci_t (in <tt>libdvdread</tt>)
+ */
+dsi_t* dvdnav_get_current_nav_dsi(dvdnav_t *self);
+
+/**
+ * Get the area associated with a certain button.
+ *
+ * \param nav_pci Pointer to the PCI structure you may have got via
+ * dvdnav_get_current_nav_pci().
+ *
+ * \param button Button number to query.
+ * \param mode 0..3 -- Button mode to query.
+ * \param highlight Pointer to dvdnav_highlight_area_t to fill in.
+ *
+ * \sa dvdnav_highlight_area_t
+ */
+dvdnav_status_t dvdnav_get_highlight_area(pci_t* nav_pci , int32_t button, int32_t mode,
+ dvdnav_highlight_area_t* highlight);
+
+/**
+ * Move button highlight around as suggested by function name (e.g. with arrow keys).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+dvdnav_status_t dvdnav_upper_button_select(dvdnav_t *self);
+/**
+ * Move button highlight around as suggested by function name (e.g. with arrow keys).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+dvdnav_status_t dvdnav_lower_button_select(dvdnav_t *self);
+/**
+ * Move button highlight around as suggested by function name (e.g. with arrow keys).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+dvdnav_status_t dvdnav_right_button_select(dvdnav_t *self);
+/**
+ * Move button highlight around as suggested by function name (e.g. with arrow keys).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+dvdnav_status_t dvdnav_left_button_select(dvdnav_t *self);
+
+/**
+ * Activate (press) the currently highlighted button.
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+dvdnav_status_t dvdnav_button_activate(dvdnav_t *self);
+
+/**
+ * Highlight a specific button.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param button 1..39 -- Button number to activate.
+ */
+dvdnav_status_t dvdnav_button_select(dvdnav_t *self, int button);
+
+/**
+ * Activate (press) specified button.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param button 1..39 -- Button number to activate.
+ */
+dvdnav_status_t dvdnav_button_select_and_activate(dvdnav_t *self, int button);
+
+/**
+ * Select button at specified (image) co-ordinates.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param x X co-ordinate in image.
+ * \param y Y xo-ordinate in image.
+ */
+dvdnav_status_t dvdnav_mouse_select(dvdnav_t *self, int x, int y);
+
+/**
+ * Activate (press) button at specified co-ordinates.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param x X co-ordinate in image.
+ * \param y Y xo-ordinate in image.
+ */
+dvdnav_status_t dvdnav_mouse_activate(dvdnav_t *self, int x, int y);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup languages Languages
+ *
+ * @{
+ */
+
+/**
+ * Set which menu language we should use.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param code 2 char ISO639 Language code in a C-style string.
+ */
+dvdnav_status_t dvdnav_menu_language_select(dvdnav_t *self,
+ char *code);
+
+/**
+ * Set which audio language we should use.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param code 2 char ISO639 Language code in a C-style string.
+ */
+dvdnav_status_t dvdnav_audio_language_select(dvdnav_t *self,
+ char *code);
+
+/**
+ * Set which spu language we should use.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param code 2 char ISO639 Language code in a C-style string.
+ */
+dvdnav_status_t dvdnav_spu_language_select(dvdnav_t *self,
+ char *code);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup streams Sub-Picture Unit (Subtitles) and Audio Streams
+ *
+ * All these commands manipulate the audio/subtitle stream numbers that the
+ * player engine thinks is playing. Note that all the streams are still returned
+ * multiplexed by dvdnav_get_next_block(). You should try to make sure that the
+ * MPEG demuxer and the player engine both have the same thoughts on what stream the
+ * user is currently listening to.
+ *
+ * \sa dvdnav_get_next_block()
+ *
+ * @{
+ */
+
+/**
+ * Set a specific PHYSICAL MPEG stream.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param audio 0..7 -- Stream number.
+ */
+dvdnav_status_t dvdnav_physical_audio_stream_change(dvdnav_t *self,
+ int audio);
+
+/**
+ * Set a specific logical audio stream.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param audio 0..7 -- Stream number.
+ */
+dvdnav_status_t dvdnav_logical_audio_stream_change(dvdnav_t *self,
+ int audio);
+
+/**
+ * Set the int pointed to to the current PHYSICAL audio
+ * stream.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param audio Pointer to int which will receive value.
+ */
+dvdnav_status_t dvdnav_get_physical_audio_stream(dvdnav_t *self, int* audio);
+
+/**
+ * Set the int pointed to to the current LOGICAL audio
+ * stream.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param audio Pointer to int which will receive value.
+ */
+dvdnav_status_t dvdnav_get_logical_audio_stream(dvdnav_t *self, int* audio);
+
+/**
+ * Set a specific PHYSICAL MPEG SPU stream and whether it should be
+ * displayed.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param stream 0..31 or 63 (dummy) -- Stram number.
+ * \param display: 0..1 -- Is this actually being displayed?
+ */
+dvdnav_status_t dvdnav_physical_spu_stream_change(dvdnav_t *self,
+ int stream, int display);
+
+/**
+ * Set a specific LOGICAL SPU stream and whether it should be
+ * displayed.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param stream 0..31 or 63 (dummy) -- Stram number.
+ * \param display: 0..1 -- Is this actually being displayed?
+ */
+dvdnav_status_t dvdnav_logical_spu_stream_change(dvdnav_t *self,
+ int stream, int display);
+
+/**
+ * Set the ints pointed to to the current PHYSICAL SPU
+ * stream & display flag.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param stream Pointer which will receive value.
+ * \param display Pointer which will receive value.
+ */
+dvdnav_status_t dvdnav_get_physical_spu_stream(dvdnav_t *self,
+ int* stream, int* display);
+
+/**
+ * Set the ints pointed to to the current LOGICAL SPU
+ * stream & display flag.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param stream Pointer which will receive value.
+ * \param display Pointer which will receive value.
+ */
+dvdnav_status_t dvdnav_get_logical_spu_stream(dvdnav_t *self,
+ int* stream, int* disply);
+
+/**
+ * Converts a *logical* audio stream id into country code
+ * (returns <tt>0xffff</tt> if no such stream).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param stream Stream number
+ */
+uint16_t dvdnav_audio_stream_to_lang(dvdnav_t *self, uint8_t stream);
+
+/**
+ * Converts a *logical* subpicture stream id into country code
+ * (returns <tt>0xffff</tt> if no such stream).
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param stream Stream number
+ */
+uint16_t dvdnav_spu_stream_to_lang(dvdnav_t *self, uint8_t stream);
+
+/**
+ * Converts a *physical* audio stream id into a logical stream number
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param audio_num Stream number
+ */
+int8_t dvdnav_get_audio_logical_stream(dvdnav_t *self, uint8_t audio_num);
+
+/**
+ * Converts a *physical* subpicture stream id into a logical stream number
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param subp_num Stream number
+ */
+int8_t dvdnav_get_spu_logical_stream(dvdnav_t *self, uint8_t subp_num);
+
+/**
+ * Get active spu stream.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+int8_t dvdnav_get_active_spu_stream(dvdnav_t *self);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup angles Multiple angles
+ *
+ * The <tt>libdvdnav</tt> library abstracts away the difference between seamless and
+ * non-seamless angles. From the point of view of the programmer you just set the
+ * angle number and all is well in the world.
+ *
+ * \par Note:
+ * It is quite possible that some tremendously strange DVD feature might change the
+ * angle number from under you. Generally you should always view the results from
+ * dvdnav_get_angle_info() as definitive only up to the next time you call
+ * dvdnav_get_next_block().
+ *
+ * @{
+ */
+
+/**
+ * Sets the current angle. If you try to follow a non existant angle
+ * the call fails.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param angle 1..9 -- Currentl angle to follow.
+ */
+dvdnav_status_t dvdnav_angle_change(dvdnav_t *self, int angle);
+
+/**
+ * Returns the current angle and number of angles present.
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \param current_angle Pointer to int which will get the current angle.
+ * \param number_of_angles Pointer to int which will get the number of angles.
+ */
+dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *self, int* current_angle,
+ int *number_of_angles);
+
+/**
+ * FIXME: WTF does this do? -- High qulaity documentation huh?
+ */
+dvdnav_status_t dvdnav_get_cell_info(dvdnav_t *self, int* current_angle,
+ int *number_of_angles);
+
+/**
+ * Returns the still time status from the next cell
+ *
+ * \param self Pointer to dvdnav_t associated with this operation.
+ */
+uint32_t dvdnav_get_next_still_flag(dvdnav_t *self);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup domain Domain Queries
+ *
+ * The following functions can be used to query whether we are in
+ * particular domains or not.
+ *
+ * @{
+ */
+
+/**
+ * Are we in the First Play domain. (Menu)
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \returns -1 on failure, 1 if condition is true, 0 if condition is false
+ */
+int8_t dvdnav_is_domain_fp(dvdnav_t *self);
+/**
+ * Are we in the Video management Menu domain. (Menu)
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \returns -1 on failure, 1 if condition is true, 0 if condition is false
+ */
+int8_t dvdnav_is_domain_vmgm(dvdnav_t *self);
+/**
+ * Are we in the Video Title Menu domain (Menu)
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \returns -1 on failure, 1 if condition is true, 0 if condition is false
+ */
+int8_t dvdnav_is_domain_vtsm(dvdnav_t *self);
+/**
+ * Are we in the Video Title domain (playing movie)?
+ * \param self Pointer to dvdnav_t associated with this operation.
+ * \returns -1 on failure, 1 if condition is true, 0 if condition is false
+ */
+int8_t dvdnav_is_domain_vts(dvdnav_t *self);
+
+/**
+ * @}
+ */
+
+/*
+ * The following info appears on the front page of the reference manual. It is
+ * included here to keep it with the main dvdnav source files. The copyright notice
+ * refers to the comments _only_. All machine-readable source code is covered by
+ * the copyright notice at the top of this file.
+ *
+ * Oh, and the official language of the documentation is English --
+ * English English (as in the English spoken in England) not US English.
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DVDNAV_H_INCLUDED */
diff --git a/src/input/libdvdnav/dvdnav_events.h b/src/input/libdvdnav/dvdnav_events.h
new file mode 100644
index 000000000..427c7896c
--- /dev/null
+++ b/src/input/libdvdnav/dvdnav_events.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: dvdnav_events.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifndef DVDNAV_EVENTS_H_INCLUDED
+#define DVDNAV_EVENTS_H_INCLUDED
+
+#include <dvdread/ifo_types.h>
+#include <dvdread/nav_types.h>
+#include <dvdread/dvd_reader.h>
+
+/**
+ * \file dvdnav_events.h
+ * This header defines events and event types
+ */
+
+/*** EVENTS ***/
+
+#define DVDNAV_BLOCK_OK 0 /*!< The next black was returned */
+#define DVDNAV_NOP 1 /*!< No action should be taken */
+#define DVDNAV_STILL_FRAME 2 /*!< The preceeding block was the last in a
+ still frame. */
+#define DVDNAV_SPU_STREAM_CHANGE 3 /*!< The SPU stream was changed */
+#define DVDNAV_AUDIO_STREAM_CHANGE 4 /*!< The Audio stream was changed */
+#define DVDNAV_VTS_CHANGE 5 /*!< We have changed VTS */
+#define DVDNAV_CELL_CHANGE 6 /*!< We have jumped to a new cell */
+#define DVDNAV_NAV_PACKET 7 /*!< The packet just passed was the NAV packet */
+#define DVDNAV_STOP 8 /*!< The last block was final, no more are coming */
+#define DVDNAV_HIGHLIGHT 9 /*!< Change highlight region */
+#define DVDNAV_SPU_CLUT_CHANGE 10 /*!< SPU CLUT */
+#define DVDNAV_SEEK_DONE 11 /*!< Seek done, subtitles should be reset */
+#define DVDNAV_HOP_CHANNEL 12 /*!< Sent when non-seemless stream change has happed
+ E.g. Menu button pressed causing change in menu */
+
+/*** EVENT TYPES ***/
+
+/**
+ * Structure providing information on DVDNAV_STILL_FRAME events.
+ */
+typedef struct {
+ int length; /*!<
+ The length (in seconds) the still frame
+ should be displayed for, or 0xff if
+ indefinate. */
+} dvdnav_still_event_t;
+
+/**
+ * Structure providing information on DVDNAV_SPU_STREAM_CHANGE events.
+ */
+typedef struct {
+ int physical_wide; /*!< The physical (MPEG) stream number for widescreen display. */
+ int physical_letterbox; /*!< The physical (MPEG) stream number for letterboxed display. */
+ int physical_pan_scan; /*!< The physical (MPEG) stream number for pan&scan display. */
+ int logical; /*!< The logical (DVD) stream number. */
+} dvdnav_spu_stream_change_event_t;
+
+/**
+ * Structure providing information on DVDNAV_AUDIO_STREAM_CHANGE events.
+ */
+typedef struct {
+ int physical; /*!< The physical (MPEG) stream number. */
+ int logical; /*!< The logical (DVD) stream number. */
+} dvdnav_audio_stream_change_event_t;
+
+/**
+ * Structure providing information on DVDNAV_VTS_CHANGE events.
+ */
+typedef struct {
+ int old_vtsN; /*!< The old VTS number */
+ dvd_read_domain_t old_domain; /*!< The old domain */
+ int new_vtsN; /*!< The new VTS number */
+ dvd_read_domain_t new_domain; /*!< The new domain */
+} dvdnav_vts_change_event_t;
+
+/**
+ * Structure providing information on DVDNAV_CELL_CHANGE events.
+ */
+typedef struct {
+ cell_playback_t *old_cell; /*!< The old cell (or NULL if this is
+ the first cell) */
+ cell_playback_t *new_cell; /*!< The cell_playback_t for the new cell */
+} dvdnav_cell_change_event_t;
+
+/**
+ * Structure providing information on DVDNAV_NAV_PACKET events.
+ */
+typedef struct {
+ pci_t *pci;
+ dsi_t *dsi;
+} dvdnav_nav_packet_event_t;
+
+/**
+ * Structure providing information on DVDNAV_HIGHLIGHT events.
+ */
+typedef struct {
+ int display; /*!< 0 - hide, 1 - show, entries below only guaranteed useful
+ if this is '1' */
+ uint32_t palette; /*!< The CLUT entries for the highlight palette
+ (4-bits per entry -> 4 entries) */
+ uint16_t sx,sy,ex,ey; /*!< The start/end x,y positions */
+ uint32_t pts; /*!< Highlight PTS to match with SPU */
+ uint32_t buttonN; /*!< Button number for the SPU decoder. */
+} dvdnav_highlight_event_t;
+
+#endif /* DVDNAV_EVENTS_H_INCLUDED */
diff --git a/src/input/libdvdnav/dvdnav_internal.h b/src/input/libdvdnav/dvdnav_internal.h
new file mode 100644
index 000000000..8b589af77
--- /dev/null
+++ b/src/input/libdvdnav/dvdnav_internal.h
@@ -0,0 +1,168 @@
+/* !! DO NO EDIT THIS FILE, it is automatically generated */
+/*
+ * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: dvdnav_internal.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifndef DVDNAV_INTERNAL_H_INCLUDED
+#define DVDNAV_INTERNAL_H_INCLUDED
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "dvdnav.h"
+#include "vm.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include <string.h>
+#include <pthread.h>
+
+#undef WORDS_BIGENDIAN
+
+#include <dvdread/dvd_reader.h>
+#include <dvdread/ifo_read.h>
+#include <dvdread/ifo_types.h>
+
+
+/* Maximum length of an error string */
+#define MAX_ERR_LEN 255
+
+/* Use the POSIX PATH_MAX if available */
+#ifdef PATH_MAX
+#define MAX_PATH_LEN PATH_MAX
+#else
+#define MAX_PATH_LEN 255 /* Arbitary */
+#endif
+
+#ifndef DVD_VIDEO_LB_LEN
+#define DVD_VIDEO_LB_LEN 2048
+#endif
+
+typedef struct read_cache_s read_cache_t;
+
+/*
+ * These are defined here because they are
+ * not in ifo_types.h, they maybe one day
+ */
+
+#ifndef audio_status_t
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+ unsigned int available : 1;
+ unsigned int zero1 : 4;
+ unsigned int stream_number : 3;
+ uint8_t zero2;
+#else
+ uint8_t zero2;
+ unsigned int stream_number : 3;
+ unsigned int zero1 : 4;
+ unsigned int available : 1;
+#endif
+} ATTRIBUTE_PACKED audio_status_t;
+#endif
+
+#ifndef spu_status_t
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+ unsigned int available : 1;
+ unsigned int zero1 : 2;
+ unsigned int stream_number_4_3 : 5;
+ unsigned int zero2 : 3;
+ unsigned int stream_number_wide : 5;
+ unsigned int zero3 : 3;
+ unsigned int stream_number_letterbox : 5;
+ unsigned int zero4 : 3;
+ unsigned int stream_number_pan_scan : 5;
+#else
+ unsigned int stream_number_pan_scan : 5;
+ unsigned int zero4 : 3;
+ unsigned int stream_number_letterbox : 5;
+ unsigned int zero3 : 3;
+ unsigned int stream_number_wide : 5;
+ unsigned int zero2 : 3;
+ unsigned int stream_number_4_3 : 5;
+ unsigned int zero1 : 2;
+ unsigned int available : 1;
+#endif
+} ATTRIBUTE_PACKED spu_status_t;
+#endif
+
+typedef struct dvdnav_vobu_s {
+ int32_t vobu_start; /* Logical Absolute. MAX needed is 0x300000 */
+ int32_t vobu_length; /* Relative offset */
+ int32_t blockN; /* Relative offset */
+ int32_t vobu_next; /* Relative offset */
+} dvdnav_vobu_t;
+
+/* The main DVDNAV type */
+
+struct dvdnav_s {
+ /* General data */
+ char path[MAX_PATH_LEN]; /* Path to DVD device/dir */
+ dvd_file_t *file; /* Currently opened file */
+ int open_vtsN; /* The domain and number of the... */
+ int open_domain; /* ..currently opened VOB */
+
+ /* Position data */
+ vm_position_t position_next;
+ vm_position_t position_current;
+ dvdnav_vobu_t vobu;
+
+ /* NAV data */
+ pci_t pci;
+ dsi_t dsi;
+
+ /* Flags */
+ int skip_still; /* Set when skipping a still */
+ int stop; /* Are we stopped? (note not paused, actually stopped) */
+ int spu_clut_changed; /* The SPU CLUT changed */
+ int started; /* vm_start has been called? */
+ int use_read_ahead; /* 1 - use read-ahead cache, 0 - don't */
+ /* VM */
+ vm_t* vm;
+ pthread_mutex_t vm_lock;
+
+ /* Highlight */
+ int hli_state; /* State of highlight: 0 - disabled,
+ 1 - selected,
+ 2 - activated */
+
+ /* Read-ahead cache */
+ read_cache_t *cache;
+
+ /* Errors */
+ char err_str[MAX_ERR_LEN];
+};
+
+/** USEFUL MACROS **/
+
+#define printerrf(format, args...) snprintf(this->err_str, MAX_ERR_LEN, format, ## args);
+#define printerr(str) strncpy(this->err_str, str, MAX_ERR_LEN);
+/* Save my typing */
+
+#define S_ERR DVDNAV_STATUS_ERR
+#define S_OK DVDNAV_STATUS_OK
+
+#endif /* DVDNAV_INTERNAL_H_INCLUDED */
diff --git a/src/input/libdvdnav/highlight.c b/src/input/libdvdnav/highlight.c
new file mode 100644
index 000000000..dc4f3877a
--- /dev/null
+++ b/src/input/libdvdnav/highlight.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: highlight.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/*
+#define BUTTON_TESTING
+*/
+#include <assert.h>
+
+#include <dvdnav.h>
+#include "dvdnav_internal.h"
+
+#include "vm.h"
+#
+#include <dvdread/nav_types.h>
+
+#ifdef BUTTON_TESTING
+#include <dvdread/nav_print.h>
+#endif
+
+/* Highlighting API calls */
+
+dvdnav_status_t dvdnav_get_current_highlight(dvdnav_t *this, int* button) {
+ if(!this)
+ return S_ERR;
+
+ /* Simply return the appropriate value based on the SPRM */
+ (*button) = (this->vm->state.HL_BTNN_REG) >> 10;
+
+ return S_OK;
+}
+
+btni_t *__get_current_button(dvdnav_t *this) {
+ int button = 0;
+
+ if(dvdnav_get_current_highlight(this, &button) != S_OK) {
+ printerrf("Unable to get information on current highlight.");
+ return NULL;
+ }
+#ifdef BUTTON_TESTING
+ navPrint_PCI(&(this->pci));
+#endif
+
+ return &(this->pci.hli.btnit[button-1]);
+}
+
+dvdnav_status_t dvdnav_button_auto_action(dvdnav_t *this) {
+ btni_t *button_ptr;
+
+ if(!this)
+ return S_ERR;
+
+ if((button_ptr = __get_current_button(this)) == NULL) {
+ return S_ERR;
+ }
+ if (button_ptr->auto_action_mode == 1) {
+ return S_OK;
+ }
+ return S_ERR;
+}
+
+
+dvdnav_status_t dvdnav_upper_button_select(dvdnav_t *this) {
+ btni_t *button_ptr;
+
+ if(!this)
+ return S_ERR;
+
+ if((button_ptr = __get_current_button(this)) == NULL) {
+ return S_ERR;
+ }
+
+ dvdnav_button_select(this, button_ptr->up);
+ if (dvdnav_button_auto_action(this) ) {
+ dvdnav_button_activate(this);
+ }
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_lower_button_select(dvdnav_t *this) {
+ btni_t *button_ptr;
+
+ if(!this)
+ return S_ERR;
+
+ if((button_ptr = __get_current_button(this)) == NULL) {
+ return S_ERR;
+ }
+
+ dvdnav_button_select(this, button_ptr->down);
+ if (dvdnav_button_auto_action(this) ) {
+ dvdnav_button_activate(this);
+ }
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_right_button_select(dvdnav_t *this) {
+ btni_t *button_ptr;
+
+ if(!this)
+ return S_ERR;
+
+ if((button_ptr = __get_current_button(this)) == NULL) {
+ printerr("Error fetching information on current button.");
+ return S_ERR;
+ }
+
+ dvdnav_button_select(this, button_ptr->right);
+ if (dvdnav_button_auto_action(this) ) {
+ dvdnav_button_activate(this);
+ }
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_left_button_select(dvdnav_t *this) {
+ btni_t *button_ptr;
+
+ if(!this)
+ return S_ERR;
+
+ if((button_ptr = __get_current_button(this)) == NULL) {
+ return S_ERR;
+ }
+
+ dvdnav_button_select(this, button_ptr->left);
+ if (dvdnav_button_auto_action(this) ) {
+ dvdnav_button_activate(this);
+ }
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_get_highlight_area(pci_t* nav_pci , int32_t button, int32_t mode,
+ dvdnav_highlight_area_t* highlight) {
+ btni_t *button_ptr;
+#ifdef BUTTON_TESTING
+ fprintf(stderr,"Button get_highlight_area %i\n", button);
+#endif
+
+ /* Set the highlight SPRM if the passed button was valid*/
+ if((button <= 0) || (button > nav_pci->hli.hl_gi.btn_ns)) {
+ fprintf(stderr,"Unable to select button number %i as it doesn't exist\n",
+ button);
+ return S_ERR;
+ }
+ button_ptr = &nav_pci->hli.btnit[button-1];
+
+ highlight->sx = button_ptr->x_start;
+ highlight->sy = button_ptr->y_start;
+ highlight->ex = button_ptr->x_end;
+ highlight->ey = button_ptr->y_end;
+ if(button_ptr->btn_coln != 0) {
+ highlight->palette = nav_pci->hli.btn_colit.btn_coli[button_ptr->btn_coln-1][mode];
+ } else {
+ highlight->palette = 0;
+ }
+ highlight->pts = nav_pci->hli.hl_gi.hli_s_ptm;
+ highlight->buttonN = button;
+#ifdef BUTTON_TESTING
+ fprintf(stderr,"highlight.c:Highlight area is (%u,%u)-(%u,%u), display = %i, button = %u\n",
+ button_ptr->x_start, button_ptr->y_start,
+ button_ptr->x_end, button_ptr->y_end,
+ 1,
+ button);
+#endif
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_button_activate(dvdnav_t *this) {
+ int button;
+ btni_t *button_ptr = NULL;
+
+ if(!this)
+ return S_ERR;
+ pthread_mutex_lock(&this->vm_lock);
+
+ /* Precisely the same as selecting a button except we want
+ * a different palette */
+ if(dvdnav_get_current_highlight(this, &button) != S_OK) {
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_ERR;
+ }
+/* FIXME: dvdnav_button_select should really return a
+ * special case for explicit NO-BUTTONS.
+ */
+ if(dvdnav_button_select(this, button) != S_OK) {
+ /* Special code to handle still menus with no buttons.
+ * the navigation is expected to report to the appicatino that a STILL is
+ * underway. In turn, the application is supposed to report to the user
+ * that the playback is pause. The user is then expected to undo the pause.
+ * ie: hit play. At that point, the navigation should release the still and
+ * go to the next Cell.
+ * Explanation by Mathieu Lavage <mathieu_lacage@realmagic.fr>
+ * Code added by jcdutton.
+ */
+ if (this->position_current.still != 0) {
+ /* In still, but no buttons. */
+ vm_get_next_cell(this->vm);
+ this->position_current.still = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+ }
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_ERR;
+ }
+ /* FIXME: The button command should really be passed in the API instead. */
+ button_ptr = __get_current_button(this);
+ /* Finally, make the VM execute the appropriate code and
+ * scedule a jump */
+#ifdef BUTTON_TESTING
+ fprintf(stderr, "libdvdnav: Evaluating Button Activation commands.\n");
+#endif
+ if(vm_eval_cmd(this->vm, &(button_ptr->cmd)) == 1) {
+ /* Command caused a jump */
+ this->vm->hop_channel++;
+ this->position_current.still = 0;
+ }
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_button_select(dvdnav_t *this, int button) {
+
+ if(!this) {
+ printerrf("Unable to select button number %i as this state bad",
+ button);
+ return S_ERR;
+ }
+
+#ifdef BUTTON_TESTING
+ fprintf(stderr,"libdvdnav: Button select %i\n", button);
+#endif
+
+ /* Set the highlight SPRM if the passed button was valid*/
+ /* FIXME: this->pci should be provided by the application. */
+ if((button <= 0) || (button > this->pci.hli.hl_gi.btn_ns)) {
+ printerrf("Unable to select button number %i as it doesn't exist",
+ button);
+ return S_ERR;
+ }
+ this->vm->state.HL_BTNN_REG = (button << 10);
+
+ this->hli_state = 1; /* Selected */
+
+ this->position_current.button = -1; /* Force Highligh change */
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_button_select_and_activate(dvdnav_t *this,
+ int button) {
+ /* A trivial function */
+ if(dvdnav_button_select(this, button) != S_ERR) {
+ return dvdnav_button_activate(this);
+ }
+
+ /* Should never get here without an error */
+ return S_ERR;
+}
+
+dvdnav_status_t dvdnav_mouse_select(dvdnav_t *this, int x, int y) {
+ int button, cur_button;
+ uint32_t best,dist;
+ int mx,my,dx,dy,d;
+
+ /* FIXME: At the moment, the case of no button matchin (x,y) is
+ * silently ignored, is this OK? */
+ if(!this)
+ return S_ERR;
+
+ if(dvdnav_get_current_highlight(this, &cur_button) != S_OK) {
+ return S_ERR;
+ }
+
+ best = 0;
+ dist = 0x08000000; /* >> than (720*720)+(567*567); */
+
+ /* Loop through each button */
+ for(button=1; button <= this->pci.hli.hl_gi.btn_ns; button++) {
+ btni_t *button_ptr = NULL;
+ button_ptr = &(this->pci.hli.btnit[button-1]);
+ if((x >= button_ptr->x_start) && (x <= button_ptr->x_end) &&
+ (y >= button_ptr->y_start) && (y <= button_ptr->y_end)) {
+ mx = (button_ptr->x_start + button_ptr->x_end)/2;
+ my = (button_ptr->y_start + button_ptr->y_end)/2;
+ dx = mx - x;
+ dy = my - y;
+ d = (dx*dx) + (dy*dy);
+ /* If the mouse is within the button and the mouse is closer
+ * to the center of this button then it is the best choice. */
+ if(d < dist) {
+ dist = d; best=button;
+ }
+ }
+ }
+
+ if (best!=0) {
+ /* As an efficiency measure, only re-select the button
+ * if it is different to the previously selected one. */
+ if(best != cur_button) {
+ dvdnav_button_select(this, best);
+ }
+ }
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_mouse_activate(dvdnav_t *this, int x, int y) {
+ /* A trivial function */
+ if(dvdnav_mouse_select(this, x,y) != S_ERR) {
+ return dvdnav_button_activate(this);
+ }
+
+ /* Should never get here without an error */
+ return S_ERR;
+}
+
diff --git a/src/input/libdvdnav/navigation.c b/src/input/libdvdnav/navigation.c
new file mode 100644
index 000000000..ba04b88c4
--- /dev/null
+++ b/src/input/libdvdnav/navigation.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: navigation.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <dvdnav.h>
+#include "dvdnav_internal.h"
+
+#include "vm.h"
+
+/* Navigation API calls */
+
+dvdnav_status_t dvdnav_still_skip(dvdnav_t *this) {
+ if(!this)
+ return S_ERR;
+
+ this->position_current.still = 0;
+ this->skip_still = 1;
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_get_number_of_titles(dvdnav_t *this, int *titles) {
+ if(!this)
+ return S_ERR;
+
+ if(!titles) {
+ printerr("Passed a NULL pointer");
+ return S_ERR;
+ }
+
+ (*titles) = vm_get_vmgi(this->vm)->tt_srpt->nr_of_srpts;
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_get_number_of_programs(dvdnav_t *this, int *programs) {
+ if(!this)
+ return S_ERR;
+
+ if(!programs) {
+ printerr("Passed a NULL pointer");
+ return S_ERR;
+ }
+
+ (*programs) = this->vm->state.pgc->nr_of_programs;
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_title_play(dvdnav_t *this, int title) {
+ int num_titles;
+
+ if(!this) {
+ return S_ERR;
+ }
+
+ /* Check number of titles */
+ dvdnav_get_number_of_titles(this, &num_titles);
+ if((title > num_titles) || (title <= 0)) {
+ printerrf("Invalid title passed (%i, maximum %i)", title,
+ num_titles);
+ return S_ERR;
+ }
+
+ vm_start_title(this->vm, title);
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_part_play(dvdnav_t *this, int title, int part) {
+ int num_titles, num_progs;
+
+ if(!this) {
+ return S_ERR;
+ }
+
+ /* Check number of titles */
+ dvdnav_get_number_of_titles(this, &num_titles);
+ if((title > num_titles) || (title <= 0)) {
+ printerrf("Invalid title passed (%i, maximum %i)", title,
+ num_titles);
+ return S_ERR;
+ }
+
+ vm_start_title(this->vm, title);
+
+
+ /* Check number of parts */
+ num_progs = this->vm->state.pgc->nr_of_programs;
+ if((part > num_progs) || (part <= 0)) {
+ printerrf("Invalid program passed (%i, maximum %i)", part,
+ num_progs);
+ return S_ERR;
+ }
+
+ vm_jump_prog(this->vm, part);
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_part_play_auto_stop(dvdnav_t *this, int title,
+ int part, int parts_to_play) {
+ /* Perform jump as per usual */
+
+ return dvdnav_part_play(this, title, part);
+
+ /* FIXME: Impement auto-stop */
+
+ /* return S_OK;*/
+}
+
+dvdnav_status_t dvdnav_time_play(dvdnav_t *this, int title,
+ unsigned long int time) {
+ /* FIXME: Implement */
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_stop(dvdnav_t *this) {
+ if(!this)
+ return S_ERR;
+
+ /* Set the STOP flag */
+
+ this->stop = 1;
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_go_up(dvdnav_t *this) {
+ if(!this)
+ return S_ERR;
+
+ /* A nice easy function... delegate to the VM */
+ vm_go_up(this->vm);
+
+ return S_OK;
+}
+
+
+
diff --git a/src/input/libdvdnav/read_cache.c b/src/input/libdvdnav/read_cache.c
new file mode 100644
index 000000000..1d6e68727
--- /dev/null
+++ b/src/input/libdvdnav/read_cache.c
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: read_cache.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "dvdnav.h"
+#include "read_cache.h"
+#include <pthread.h>
+#include <sys/time.h>
+#include <time.h>
+
+/*
+#define DVDNAV_PROFILE
+*/
+
+/* Read-ahead cache structure. */
+#if _MULTITHREAD_
+
+/* For the multithreaded cache, the cache is a ring buffer + writing
+ * thread that continuously reads data into the buffer until it is
+ * full or the 'upper-bound' has been reached.
+ */
+
+#define CACHE_BUFFER_SIZE 2048 /* Cache this number of blocks at a time */
+
+struct read_cache_s {
+ pthread_mutex_t cache_lock;
+ pthread_t read_thread;
+
+ /* Buffer */
+ uint8_t *buffer;
+
+ /* Size of buffer */
+ int32_t size;
+ /* block offset from sector start of buffer 'head' */
+ uint32_t pos;
+ /* block offset from sector start of read point */
+ uint32_t read_point;
+ /* block offset from buffer start to ring-boundary */
+ uint32_t start;
+
+ /* Bit of strange cross-linking going on here :) -- Gotta love C :) */
+ dvdnav_t *dvd_self;
+};
+
+#else
+
+#define READ_CACHE_CHUNKS 10
+
+typedef struct read_cache_chunk_s {
+ uint8_t *cache_buffer;
+ int32_t cache_start_sector; /* -1 means cache invalid */
+ size_t cache_block_count;
+ size_t cache_malloc_size;
+ int cache_valid;
+ int usage_count; /* counts how many buffers where issued from this chunk */
+} read_cache_chunk_t;
+
+struct read_cache_s {
+ read_cache_chunk_t chunk[READ_CACHE_CHUNKS];
+ int current;
+ int freeing; /* is set to one when we are about to dispose the cache */
+ pthread_mutex_t lock;
+
+ /* Bit of strange cross-linking going on here :) -- Gotta love C :) */
+ dvdnav_t *dvd_self;
+};
+#endif
+
+#define _MT_TRACE 0
+
+#if _MT_TRACE
+#define dprintf(fmt, args...) fprintf(stderr, "%s: "fmt, __func__ , ## args);
+#else
+#define dprintf(fmt, args...) /* Nowt */
+#endif
+
+#if _MULTITHREAD_
+
+void * read_cache_read_thread (void * this_gen) {
+ int cont = 1;
+ int32_t diff, start;
+ uint32_t pos, size, startp, endp;
+ uint32_t s,c;
+ uint8_t *at;
+ read_cache_t *self = (read_cache_t*)this_gen;
+
+ while(cont) {
+
+ pthread_mutex_lock(&self->cache_lock);
+
+ if(self->size >= 0) {
+ diff = self->read_point - self->pos;
+ if(diff >= self->size/2) {
+ dprintf("(II) Read thread -- ");
+
+ startp = (self->start) % CACHE_BUFFER_SIZE;
+ endp = abs((self->start + diff - 1) % CACHE_BUFFER_SIZE);
+ dprintf("startp = %i, endp = %i -- ",startp, endp);
+
+ pos = self->pos + diff;
+ size = self->size - diff;
+ start = (self->start + diff) % CACHE_BUFFER_SIZE;
+
+ /* Fill remainder of buffer */
+
+ if(startp > endp) {
+ s = pos + size; c = CACHE_BUFFER_SIZE - startp;
+ at = self->buffer + (startp * DVD_VIDEO_LB_LEN);
+ if(c > 0) {
+ dprintf("(1) Reading from %i to %i to %i ", s, s+c-1, startp);
+ pthread_mutex_unlock(&self->cache_lock);
+ DVDReadBlocks(self->dvd_self->file, s,c, at);
+ pthread_mutex_lock(&self->cache_lock);
+ }
+
+ s = pos + size + c; c = CACHE_BUFFER_SIZE - size - c;
+ at = self->buffer;
+ if(c > 0) {
+ dprintf("(2) Reading from %i to %i to %i ", s, s+c-1, 0);
+ pthread_mutex_unlock(&self->cache_lock);
+ DVDReadBlocks(self->dvd_self->file, s,c, at);
+ pthread_mutex_lock(&self->cache_lock);
+ }
+ } else {
+ s = pos + size; c = CACHE_BUFFER_SIZE - size;
+ at = self->buffer + (startp * DVD_VIDEO_LB_LEN);
+ if(c > 0) {
+ dprintf("(3) Reading from %i to %i to %i ", s, s+c-1, startp);
+ pthread_mutex_unlock(&self->cache_lock);
+ DVDReadBlocks(self->dvd_self->file, s,c, at);
+ pthread_mutex_lock(&self->cache_lock);
+ }
+ }
+
+ dprintf("\n");
+
+ self->pos = pos;
+ self->start = start; self->size = CACHE_BUFFER_SIZE;
+ }
+ }
+
+ pthread_mutex_unlock(&self->cache_lock);
+ cont = (self->buffer != NULL);
+ usleep(100);
+ }
+
+ return NULL;
+}
+
+read_cache_t *dvdnav_read_cache_new(dvdnav_t* dvd_self) {
+ read_cache_t *me;
+
+ me = (read_cache_t*)malloc(sizeof(struct read_cache_s));
+
+ if(me) {
+ int err;
+
+ me->dvd_self = dvd_self;
+ me->buffer = (uint8_t*)malloc(CACHE_BUFFER_SIZE * DVD_VIDEO_LB_LEN);
+ me->start = 0;
+ me->pos = 0;
+ me->read_point = 0;
+ me->size = -1;
+
+ /* Initialise the mutex */
+ pthread_mutex_init(&me->cache_lock, NULL);
+
+ if ((err = pthread_create (&me->read_thread,
+ NULL, read_cache_read_thread, me)) != 0) {
+ dprintf("read_cache: can't create new thread (%s)\n",strerror(err));
+ }
+ }
+
+ return me;
+}
+
+void dvdnav_read_cache_free(read_cache_t* self) {
+ dvdnav_t *tmp;
+
+ pthread_mutex_lock(&self->cache_lock);
+
+ if(self->buffer) {
+ free(self->buffer);
+ self->buffer = NULL;
+ self->size = -2;
+ }
+
+ pthread_mutex_unlock(&self->cache_lock);
+
+ pthread_join(self->read_thread, NULL);
+
+ pthread_mutex_destroy(&self->cache_lock);
+
+ tmp = self->dvd_self;
+ free(self);
+
+ /* We free the main structure, too, because we have no buffers out there. */
+ free(tmp);
+}
+
+/* This function MUST be called whenever self->file changes. */
+void dvdnav_read_cache_clear(read_cache_t *self) {
+ if(!self)
+ return;
+
+ pthread_mutex_lock(&self->cache_lock);
+ self->size = -1;
+ self->start = 0;
+ self->pos = 0;
+ self->read_point = 0;
+ pthread_mutex_unlock(&self->cache_lock);
+}
+
+/* This function is called just after reading the NAV packet. */
+void dvdnav_pre_cache_blocks(read_cache_t *self, int sector, size_t block_count) {
+ if(!self)
+ return;
+
+ if(!self->dvd_self->use_read_ahead) {
+ return;
+ }
+
+ pthread_mutex_lock(&self->cache_lock);
+ dprintf("Requested pre-cache (%i -> +%i) : current state pos=%i, size=%i.\n",
+ sector, block_count, self->pos, self->size);
+
+ /* Are the contents of the buffer in any way relevant? */
+ if((self->size > 0) && (sector >= self->pos) && (sector <= self->pos+self->size)) {
+ dprintf("Contents relevant ... adjusting\n");
+ self->read_point = sector;
+ } else {
+ /* Flush the cache as its not much use */
+ dprintf("Contents irrelevent... flushing\n");
+ self->size = 0;
+ self->start = 0;
+ self->pos = sector;
+ self->read_point = sector;
+ }
+
+ pthread_mutex_unlock(&self->cache_lock);
+}
+
+/* This function will do the cache read once implemented */
+int dvdnav_read_cache_block( read_cache_t *self, int sector, size_t block_count, uint8_t **buf) {
+ int result, diff;
+
+ if(!self)
+ return 0;
+
+ pthread_mutex_lock(&self->cache_lock);
+ dprintf("Read from %i -> +%i (buffer pos=%i, read_point=%i, size=%i)... ", sector, block_count,
+ self->pos, self->read_point, self->size);
+ if((self->size > 0) && (sector >= self->read_point) &&
+ (sector + block_count <= self->pos + self->size)) {
+ /* Hit */
+
+ /* Drop any skipped blocks */
+ diff = sector - self->read_point;
+ if(diff > 0)
+ self->read_point += diff;
+
+ diff = self->read_point - self->pos;
+
+ if(((self->start + diff) % CACHE_BUFFER_SIZE) + block_count <= CACHE_BUFFER_SIZE) {
+ dprintf("************** Single read\n");
+ memcpy(*buf, self->buffer + (((self->start + diff) % CACHE_BUFFER_SIZE) * DVD_VIDEO_LB_LEN),
+ block_count * DVD_VIDEO_LB_LEN);
+ self->read_point += block_count;
+ pthread_mutex_unlock(&self->cache_lock);
+
+ return (int)block_count;
+ } else {
+ int32_t boundary = CACHE_BUFFER_SIZE - self->start;
+
+ dprintf("************** Multiple read\n");
+ memcpy(*buf, self->buffer + (((self->start + diff) % CACHE_BUFFER_SIZE) * DVD_VIDEO_LB_LEN),
+ boundary * DVD_VIDEO_LB_LEN);
+ memcpy(*buf + (boundary * DVD_VIDEO_LB_LEN), self->buffer,
+ (block_count-boundary) * DVD_VIDEO_LB_LEN);
+ self->read_point += block_count;
+ pthread_mutex_unlock(&self->cache_lock);
+
+ return (int)block_count;
+ }
+ } else {
+ /* Miss */
+
+ fprintf(stderr, "DVD read cache miss! (not bad but a performance hit) sector=%d\n", sector);
+ result = DVDReadBlocks( self->dvd_self->file, sector, block_count, *buf);
+ self->read_point = sector+block_count;
+ if(self->read_point > self->pos + self->size) {
+ /* Flush the cache as its not much use */
+ dprintf("Contents irrelevent... flushing\n");
+ self->size = 0;
+ self->start = 0;
+ self->pos = sector+block_count;
+ }
+ pthread_mutex_unlock(&self->cache_lock);
+ usleep(300);
+ return result;
+ }
+
+ /* Should never get here */
+ return 0;
+}
+
+dvdnav_status_t dvdnav_free_cache_block(dvdnav_t *self, unsigned char *buf) {
+ return DVDNAV_STATUS_OK;
+}
+
+#else
+
+read_cache_t *dvdnav_read_cache_new(dvdnav_t* dvd_self) {
+ read_cache_t *self;
+ int i;
+
+ self = (read_cache_t *)malloc(sizeof(read_cache_t));
+
+ if(self) {
+ self->current = 0;
+ self->freeing = 0;
+ self->dvd_self = dvd_self;
+ pthread_mutex_init(&self->lock, NULL);
+ dvdnav_read_cache_clear(self);
+ for (i = 0; i < READ_CACHE_CHUNKS; i++) {
+ self->chunk[i].cache_buffer = NULL;
+ self->chunk[i].usage_count = 0;
+ }
+ }
+
+ return self;
+}
+
+void dvdnav_read_cache_free(read_cache_t* self) {
+ dvdnav_t *tmp;
+ int i;
+
+ pthread_mutex_lock(&self->lock);
+ self->freeing = 1;
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ if (self->chunk[i].cache_buffer && self->chunk[i].usage_count == 0) {
+ free(self->chunk[i].cache_buffer);
+ self->chunk[i].cache_buffer = NULL;
+ }
+ pthread_mutex_unlock(&self->lock);
+
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ if (self->chunk[i].cache_buffer) return;
+
+ /* all buffers returned, free everything */
+ tmp = self->dvd_self;
+ pthread_mutex_destroy(&self->lock);
+ free(self);
+ free(tmp);
+}
+
+/* This function MUST be called whenever self->file changes. */
+void dvdnav_read_cache_clear(read_cache_t *self) {
+ int i;
+
+ if(!self)
+ return;
+
+ pthread_mutex_lock(&self->lock);
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ self->chunk[i].cache_valid = 0;
+ pthread_mutex_unlock(&self->lock);
+}
+
+#ifdef DVDNAV_PROFILE
+//#ifdef ARCH_X86
+__inline__ unsigned long long int dvdnav_rdtsc()
+{
+ unsigned long long int x;
+ __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
+ return x;
+}
+//#endif
+#endif
+
+/* This function is called just after reading the NAV packet. */
+void dvdnav_pre_cache_blocks(read_cache_t *self, int sector, size_t block_count) {
+ int i, use, result;
+#ifdef DVDNAV_PROFILE
+ struct timeval tv1, tv2, tv3;
+ unsigned long long p1, p2, p3;
+#endif
+
+ if(!self)
+ return;
+
+ if(!self->dvd_self->use_read_ahead)
+ return;
+
+ pthread_mutex_lock(&self->lock);
+
+ /* find a free cache chunk that best fits the required size */
+ use = -1;
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ if (self->chunk[i].usage_count == 0 && self->chunk[i].cache_buffer &&
+ self->chunk[i].cache_malloc_size >= block_count &&
+ (use == -1 || self->chunk[use].cache_malloc_size > self->chunk[i].cache_malloc_size))
+ use = i;
+
+ if (use == -1) {
+ /* we haven't found a cache chunk, so we try to reallocate an existing one */
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ if (self->chunk[i].usage_count == 0 && self->chunk[i].cache_buffer &&
+ (use == -1 || self->chunk[use].cache_malloc_size < self->chunk[i].cache_malloc_size))
+ use = i;
+ if (use >= 0) {
+ self->chunk[use].cache_buffer = realloc(self->chunk[use].cache_buffer,
+ block_count * DVD_VIDEO_LB_LEN);
+ dprintf("pre_cache DVD read realloc happened\n");
+ self->chunk[use].cache_malloc_size = block_count;
+ } else {
+ /* we still haven't found a cache chunk, let's allocate a new one */
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ if (!self->chunk[i].cache_buffer) {
+ use = i;
+ break;
+ }
+ if (use >= 0) {
+ /* We start with a sensible figure for the first malloc of 500 blocks.
+ * Some DVDs I have seen venture to 450 blocks.
+ * This is so that fewer realloc's happen if at all.
+ */
+ self->chunk[i].cache_buffer = malloc((block_count > 500 ? block_count : 500) * DVD_VIDEO_LB_LEN);
+ self->chunk[i].cache_malloc_size = block_count > 500 ? block_count : 500;
+ dprintf("pre_cache DVD read malloc %d blocks\n",
+ (block_count > 500 ? block_count : 500 ));
+ }
+ }
+ }
+
+ if (use >= 0) {
+ self->chunk[use].cache_start_sector = sector;
+ self->chunk[use].cache_block_count = block_count;
+ self->current = use;
+#ifdef DVDNAV_PROFILE
+ gettimeofday(&tv1, NULL);
+ p1 = dvdnav_rdtsc();
+#endif
+ result = DVDReadBlocks (self->dvd_self->file, sector, block_count, self->chunk[use].cache_buffer);
+#ifdef DVDNAV_PROFILE
+ p2 = dvdnav_rdtsc();
+ gettimeofday(&tv2, NULL);
+ timersub(&tv2, &tv1, &tv3);
+ dprintf("pre_cache DVD read %ld us, profile = %lld, block_count = %d\n",
+ tv3.tv_usec, p2-p1, block_count);
+#endif
+ self->chunk[use].cache_valid = 1;
+ } else
+ dprintf("pre_caching was impossible, no cache chunk available\n");
+
+ pthread_mutex_unlock(&self->lock);
+}
+
+int dvdnav_read_cache_block(read_cache_t *self, int sector, size_t block_count, uint8_t **buf) {
+ int i, use;
+
+ if(!self)
+ return 0;
+
+ pthread_mutex_lock(&self->lock);
+
+ use = -1;
+ if(self->dvd_self->use_read_ahead) {
+ /* first check, if sector is in current chunk */
+ read_cache_chunk_t cur = self->chunk[self->current];
+ if (cur.cache_valid && sector >= cur.cache_start_sector &&
+ sector + block_count <= cur.cache_start_sector + cur.cache_block_count)
+ use = self->current;
+ else
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ if (self->chunk[i].cache_valid && sector >= self->chunk[i].cache_start_sector &&
+ sector + block_count <= self->chunk[i].cache_start_sector + self->chunk[i].cache_block_count)
+ use = i;
+ }
+
+ if (use >= 0) {
+ self->chunk[use].usage_count++;
+ *buf = &self->chunk[use].cache_buffer[(sector - self->chunk[use].cache_start_sector) *
+ DVD_VIDEO_LB_LEN * block_count];
+ pthread_mutex_unlock(&self->lock);
+ return DVD_VIDEO_LB_LEN * block_count;
+ } else {
+ if (self->dvd_self->use_read_ahead)
+ dprintf("cache miss on sector %d\n", sector);
+ pthread_mutex_unlock(&self->lock);
+ return DVDReadBlocks(self->dvd_self->file, sector, block_count, *buf);
+ }
+}
+
+dvdnav_status_t dvdnav_free_cache_block(dvdnav_t *self, unsigned char *buf) {
+ read_cache_t *cache;
+ int i;
+
+ if (!self)
+ return DVDNAV_STATUS_ERR;
+
+ cache = self->cache;
+ if (!cache)
+ return DVDNAV_STATUS_ERR;
+
+ pthread_mutex_lock(&cache->lock);
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ if (cache->chunk[i].cache_buffer && buf >= cache->chunk[i].cache_buffer &&
+ buf < cache->chunk[i].cache_buffer + cache->chunk[i].cache_malloc_size * DVD_VIDEO_LB_LEN)
+ cache->chunk[i].usage_count--;
+ pthread_mutex_unlock(&cache->lock);
+
+ if (cache->freeing)
+ /* when we want to dispose the cache, try freeing it now */
+ dvdnav_read_cache_free(cache);
+
+ return DVDNAV_STATUS_OK;
+}
+
+#endif
diff --git a/src/input/libdvdnav/read_cache.h b/src/input/libdvdnav/read_cache.h
new file mode 100644
index 000000000..23c58c58e
--- /dev/null
+++ b/src/input/libdvdnav/read_cache.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: read_cache.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifndef __DVDNAV_READ_CACHE_H
+#define __DVDNAV_READ_CACHE_H
+
+#include "dvdnav_internal.h"
+
+/* Opaque cache type -- defined in dvdnav_internal.h */
+/* typedef struct read_cache_s read_cache_t; */
+
+/* EXPERIMENTAL: Setting the following to 1 will use an experimental multi-threaded
+ * read-ahead cache.
+ */
+#define _MULTITHREAD_ 0
+
+/* Constructor/destructors */
+read_cache_t *dvdnav_read_cache_new(dvdnav_t* dvd_self);
+void dvdnav_read_cache_free(read_cache_t* self);
+
+/* This function MUST be called whenever self->file changes. */
+void dvdnav_read_cache_clear(read_cache_t *self);
+/* This function is called just after reading the NAV packet. */
+void dvdnav_pre_cache_blocks(read_cache_t *self, int sector, size_t block_count);
+/* This function will do the cache read.
+ * The buffer handed in must be malloced to take one dvd block.
+ * On a cache hit, a different buffer will be returned though.
+ * Those buffers must _never_ be freed. */
+int dvdnav_read_cache_block(read_cache_t *self, int sector, size_t block_count, uint8_t **buf);
+
+#endif /* __DVDNAV_READ_CACHE_H */
diff --git a/src/input/libdvdnav/searching.c b/src/input/libdvdnav/searching.c
new file mode 100644
index 000000000..43ac770f4
--- /dev/null
+++ b/src/input/libdvdnav/searching.c
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: searching.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <dvdnav.h>
+#include "dvdnav_internal.h"
+
+#include "vm.h"
+#include <dvdread/nav_types.h>
+
+/* Searching API calls */
+
+dvdnav_status_t dvdnav_time_search(dvdnav_t *this,
+ unsigned long int time) {
+/* Time search the current PGC based on the xxx table */
+ return S_OK;
+}
+
+/* Scan the ADMAP for a particular block number. */
+/* Return placed in vobu. */
+/* Returns error status */
+
+dvdnav_status_t dvdnav_scan_admap(dvdnav_t *this, int32_t domain, int32_t seekto_block, int32_t *vobu) {
+ /* FIXME:Need to handle seeking outside current cell. */
+ vobu_admap_t *admap = NULL;
+ *vobu = -1;
+ fprintf(stderr,"Seeking to target %u ...\n",
+ seekto_block);
+
+ /* Search through the VOBU_ADMAP for the nearest VOBU
+ * to the target block */
+ switch(domain) {
+ case FP_DOMAIN:
+ case VMGM_DOMAIN:
+ admap = this->vm->vmgi->menu_vobu_admap;
+ break;
+ case VTSM_DOMAIN:
+ admap = this->vm->vtsi->menu_vobu_admap;
+ break;
+ case VTS_DOMAIN:
+ admap = this->vm->vtsi->vts_vobu_admap;
+ break;
+ default:
+ fprintf(stderr,"Error: Unknown domain for seeking seek.\n");
+ }
+ if(admap) {
+ int32_t address = 0;
+ int32_t vobu_start, next_vobu;
+ int found = 0;
+
+ /* Search through ADMAP for best sector */
+ vobu_start = 0x3fffffff;
+ /* FIXME: Implement a faster search algorithm */
+ while((!found) && ((address<<2) < admap->last_byte)) {
+ next_vobu = admap->vobu_start_sectors[address];
+
+ /* printf("Found block %u\n", next_vobu); */
+
+ if(vobu_start <= seekto_block &&
+ next_vobu > seekto_block) {
+ found = 1;
+ } else {
+ vobu_start = next_vobu;
+ }
+
+ address ++;
+ }
+ if(found) {
+ *vobu = vobu_start;
+ return S_OK;
+ } else {
+ fprintf(stderr,"Could not locate block\n");
+ return S_ERR;
+ }
+ }
+ fprintf(stderr,"admap not located\n");
+ return S_ERR;
+}
+
+dvdnav_status_t dvdnav_sector_search(dvdnav_t *this,
+ unsigned long int offset, int origin) {
+/* FIXME: Implement */
+
+ uint32_t target = 0;
+ uint32_t length = 0;
+ uint32_t first_cell_nr, last_cell_nr, cell_nr, fnd_cell_nr;
+ int found;
+ cell_playback_t *cell, *fnd_cell;
+ dvd_state_t *state;
+ dvdnav_status_t result;
+
+ if((!this) || (!this->vm) )
+ return -1;
+
+ state = &(this->vm->state);
+ if((!state) || (!state->pgc) )
+ return -1;
+
+ if(offset == 0)
+ return -1;
+
+ if(this->position_current.still != 0)
+ /* Cannot do seeking in a still frame. */
+ return -1;
+
+ pthread_mutex_lock(&this->vm_lock);
+ result = dvdnav_get_position(this, &target, &length);
+ fprintf(stderr,"FIXME: seeking to offset=%lu pos=%u length=%u\n", offset, target, length);
+ fprintf(stderr,"FIXME: Before cellN=%u blockN=%u\n" ,
+ state->cellN,
+ state->blockN);
+ if(!result) {
+ pthread_mutex_unlock(&this->vm_lock);
+ return -1;
+ }
+
+ switch(origin) {
+ case SEEK_SET:
+ if(offset > length) {
+ pthread_mutex_unlock(&this->vm_lock);
+ return -1;
+ }
+ target = offset;
+ break;
+ case SEEK_CUR:
+ if(target + offset > length) {
+ pthread_mutex_unlock(&this->vm_lock);
+ return -1;
+ }
+ target += offset;
+ break;
+ case SEEK_END:
+ if(length - offset < 0) {
+ pthread_mutex_unlock(&this->vm_lock);
+ return -1;
+ }
+ target = length - offset;
+ default:
+ /* Error occured */
+ pthread_mutex_unlock(&this->vm_lock);
+ return -1;
+ }
+
+ /* First find closest cell number in program */
+ first_cell_nr = state->pgc->program_map[state->pgN-1];
+ if(state->pgN < state->pgc->nr_of_programs) {
+ last_cell_nr = state->pgc->program_map[state->pgN] - 1;
+ } else {
+ last_cell_nr = state->pgc->nr_of_cells;
+ }
+
+ found = 0; target += state->pgc->cell_playback[first_cell_nr-1].first_sector;
+ fnd_cell_nr = last_cell_nr + 1;
+ for(cell_nr = first_cell_nr; (cell_nr <= last_cell_nr) && !found; cell_nr ++) {
+ cell = &(state->pgc->cell_playback[cell_nr-1]);
+ if((cell->first_sector <= target) && (cell->last_sector >= target)) {
+ state->cellN = cell_nr;
+ state->blockN = 0;
+ found = 1;
+ fnd_cell_nr = cell_nr;
+ fnd_cell = cell;
+ }
+ }
+
+ if(fnd_cell_nr <= last_cell_nr) {
+ int32_t vobu, start;
+ dvdnav_status_t status;
+ fprintf(stderr,"Seeking to cell %i from choice of %i to %i\n",
+ fnd_cell_nr, first_cell_nr, last_cell_nr);
+ status = dvdnav_scan_admap(this, state->domain, target, &vobu);
+ /*
+ * Clut does not actually change,
+ * but as the decoders have been closed then opened,
+ * A new clut has to be sent.
+ */
+ start =(state->pgc->cell_playback[state->cellN - 1].first_sector);
+ fprintf(stderr,"FIXME: After cellN=%u blockN=%u target=%x vobu=%x start=%x\n" ,
+ state->cellN,
+ state->blockN,
+ target,
+ vobu,
+ start);
+ state->blockN = vobu - start;
+ fprintf(stderr,"FIXME: After vobu=%x start=%x blockN=%x\n" ,
+ vobu,
+ start,
+ state->blockN);
+ pthread_mutex_unlock(&this->vm_lock);
+ return target;
+ } else {
+ fprintf(stderr, "Error when seeking, asked to seek outside program\n");
+ }
+
+ fprintf(stderr,"FIXME: Implement seeking to location %u\n", target);
+
+ pthread_mutex_unlock(&this->vm_lock);
+ return -1;
+}
+
+dvdnav_status_t dvdnav_part_search(dvdnav_t *this, int part) {
+
+ if((!this) || (!this->vm) )
+ return S_ERR;
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_prev_pg_search(dvdnav_t *this) {
+ dvd_state_t *state;
+
+ if((!this) || (!this->vm) )
+ return S_ERR;
+
+ state = &(this->vm->state);
+ if((!state) || (!state->pgc) )
+ return S_ERR;
+
+ /* Make sure this is not the first chapter */
+ if(state->pgN <= 1 ) {
+ fprintf(stderr,"dvdnav: at first chapter. prev chapter failed.\n");
+ return S_ERR;
+ }
+ fprintf(stderr,"dvdnav: previous chapter\n");
+ vm_jump_prog(this->vm, state->pgN - 1);
+ this->vm->hop_channel++;
+ fprintf(stderr,"dvdnav: previous chapter done\n");
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_top_pg_search(dvdnav_t *this) {
+
+ if((!this) || (!this->vm) )
+ return S_ERR;
+
+ fprintf(stderr,"dvdnav: top chapter. NOP.\n");
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *this) {
+ dvd_state_t *state;
+
+ if((!this) || (!this->vm) )
+ return S_ERR;
+
+ state = &(this->vm->state);
+ if((!state) || (!state->pgc) )
+ return S_ERR;
+
+ /* Make sure this is not the last chapter */
+ if(state->pgN >= state->pgc->nr_of_programs) {
+ fprintf(stderr,"dvdnav: at last chapter. next chapter failed.\n");
+ return S_ERR;
+ }
+ fprintf(stderr,"dvdnav: next chapter\n");
+ vm_jump_prog(this->vm, state->pgN + 1);
+ this->vm->hop_channel++;
+ fprintf(stderr,"dvdnav: next chapter done\n");
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_menu_call(dvdnav_t *this, DVDMenuID_t menu) {
+ dvd_state_t *state;
+
+ if((!this) || (!this->vm) )
+ return S_ERR;
+
+ pthread_mutex_lock(&this->vm_lock);
+ state = &(this->vm->state);
+ if (vm_menu_call(this->vm, menu, 0))
+ this->vm->hop_channel++;
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_current_title_info(dvdnav_t *this, int *tt, int *pr) {
+int vts_ttn = 0;
+ int vts, i;
+ domain_t domain;
+ tt_srpt_t* srpt;
+
+ if(!this)
+ return S_ERR;
+
+ if(!tt || !pr) {
+ printerr("Passed a NULL pointer");
+ }
+
+ if(tt)
+ *tt = -1;
+ if(*pr)
+ *pr = -1;
+
+ domain = this->vm->state.domain;
+ if((domain == FP_DOMAIN) || (domain == VMGM_DOMAIN)) {
+ /* Not in a title */
+ return S_OK;
+ }
+
+ vts_ttn = this->vm->state.VTS_TTN_REG;
+ vts = this->vm->state.vtsN;
+
+ if(pr) {
+ *pr = this->vm->state.pgN;
+ }
+
+ /* Search TT_SRPT for title */
+ if(!(vm_get_vmgi(this->vm))) {
+ printerr("Oh poo, no SRPT");
+ return S_ERR;
+ }
+
+ srpt = vm_get_vmgi(this->vm)->tt_srpt;
+ for(i=0; i<srpt->nr_of_srpts; i++) {
+ title_info_t* info = &(srpt->title[i]);
+ if((info->title_set_nr == vts) && (info->vts_ttn == vts_ttn)) {
+ if(tt)
+ *tt = i+1;
+ }
+ }
+
+ return S_OK;
+}
+
+static char __title_str[] = "DVDNAV";
+
+dvdnav_status_t dvdnav_get_title_string(dvdnav_t *this, char **title_str) {
+ if(!this)
+ return S_ERR;
+
+ if(!title_str) {
+ printerr("Passed a NULL pointer");
+ return S_ERR;
+ }
+
+ (*title_str) = __title_str;
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_get_position(dvdnav_t *this, unsigned int* pos,
+ unsigned int *len) {
+ uint32_t cur_sector;
+ uint32_t first_cell_nr;
+ uint32_t last_cell_nr;
+ cell_playback_t *first_cell;
+ cell_playback_t *last_cell;
+ dvd_state_t *state;
+ if((!this) || (!this->vm) )
+ return 0;
+
+ state = &(this->vm->state);
+ if((!state) || (!state->pgc) )
+ return 0;
+
+ /* Sanity check */
+ if(state->pgN > state->pgc->nr_of_programs) {
+ return 0;
+ }
+
+ /* Get current sector */
+ cur_sector = this->vobu.vobu_start + this->vobu.blockN;
+
+ /* Find start cell of program. */
+ first_cell_nr = state->pgc->program_map[state->pgN-1];
+ first_cell = &(state->pgc->cell_playback[first_cell_nr-1]);
+ if(state->pgN < state->pgc->nr_of_programs) {
+ last_cell_nr = state->pgc->program_map[state->pgN] - 1;
+ } else {
+ last_cell_nr = state->pgc->nr_of_cells;
+ }
+ last_cell = &(state->pgc->cell_playback[last_cell_nr-1]);
+
+ *pos= cur_sector - first_cell->first_sector;
+ *len= last_cell->last_sector - first_cell->first_sector;
+ /* printf("dvdnav:searching:current pos=%u length=%u\n",*pos,*len); */
+
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_get_position_in_title(dvdnav_t *this,
+ unsigned int *pos,
+ unsigned int *len) {
+ uint32_t cur_sector;
+ uint32_t first_cell_nr;
+ uint32_t last_cell_nr;
+ cell_playback_t *first_cell;
+ cell_playback_t *last_cell;
+ dvd_state_t *state;
+ if((!this) || (!this->vm) )
+ return S_ERR;
+
+ state = &(this->vm->state);
+ if((!state) || (!state->pgc) )
+ return S_ERR;
+
+ /* Sanity check */
+ if(state->pgN > state->pgc->nr_of_programs) {
+ return S_ERR;
+ }
+
+ /* Get current sector */
+ cur_sector = this->vobu.vobu_start + this->vobu.blockN;
+
+ /* Now find first and last cells in title. */
+ first_cell_nr = state->pgc->program_map[0];
+ first_cell = &(state->pgc->cell_playback[first_cell_nr-1]);
+ last_cell_nr = state->pgc->nr_of_cells;
+ last_cell = &(state->pgc->cell_playback[last_cell_nr-1]);
+
+ *pos = cur_sector - first_cell->first_sector;
+ *len = last_cell->last_sector - first_cell->first_sector;
+
+ return S_OK;
+}
+
+
diff --git a/src/input/libdvdnav/settings.c b/src/input/libdvdnav/settings.c
new file mode 100644
index 000000000..a9a5df45e
--- /dev/null
+++ b/src/input/libdvdnav/settings.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: settings.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <dvdnav.h>
+#include "dvdnav_internal.h"
+
+#include "vm.h"
+
+/* Characteristics/setting API calls */
+
+dvdnav_status_t dvdnav_get_region_mask(dvdnav_t *this, int *region) {
+ if(!this)
+ return S_ERR;
+
+ if(!region) {
+ printerr("Passed a NULL pointer");
+ return S_ERR;
+ }
+
+ if(!this->vm) {
+ printerr("VM not yet initialised");
+ return S_ERR;
+ }
+
+ (*region) = this->vm->state.registers.SPRM[20];
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_set_region_mask(dvdnav_t *this, int mask) {
+ if(!this)
+ return S_ERR;
+
+ if(!this->vm) {
+ printerr("VM not yet initialised");
+ return S_ERR;
+ }
+
+ this->vm->state.registers.SPRM[20] = (mask & 0xff);
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_set_readahead_flag(dvdnav_t *this, int use_readahead) {
+ if(!this)
+ return S_ERR;
+
+ this->use_read_ahead = use_readahead;
+
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_get_readahead_flag(dvdnav_t *this, int* flag) {
+ if(!this)
+ return S_ERR;
+
+ if(!flag) {
+ printerr("Passed a NULL pointer");
+ return S_ERR;
+ }
+
+ (*flag) = this->use_read_ahead;
+ return S_OK;
+}
+
+static dvdnav_status_t set_language_register(dvdnav_t *this, char *code, int reg) {
+ if(!this)
+ return S_ERR;
+
+ if(!code[0] || !code[1]) {
+ printerr("Passed illegal language code");
+ return S_ERR;
+ }
+
+ if(!this->vm) {
+ printerr("VM not yet initialised");
+ return S_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ this->vm->state.registers.SPRM[reg] = (code[0] << 8) | code[1];
+ pthread_mutex_unlock(&this->vm_lock);
+ return S_OK;
+}
+
+dvdnav_status_t dvdnav_menu_language_select(dvdnav_t *this, char *code) {
+ return set_language_register(this, code, 0);
+}
+
+dvdnav_status_t dvdnav_audio_language_select(dvdnav_t *this, char *code) {
+ return set_language_register(this, code, 16);
+}
+
+dvdnav_status_t dvdnav_spu_language_select(dvdnav_t *this, char *code) {
+ return set_language_register(this, code, 18);
+}
diff --git a/src/input/libdvdnav/vm.c b/src/input/libdvdnav/vm.c
new file mode 100644
index 000000000..ec4ac75fe
--- /dev/null
+++ b/src/input/libdvdnav/vm.c
@@ -0,0 +1,1905 @@
+/*
+ * Copyright (C) 2000, 2001 Håkan Hjort
+ * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: vm.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include <dvdread/ifo_types.h>
+#include <dvdread/ifo_read.h>
+
+#include "decoder.h"
+#include "vmcmd.h"
+#include "vm.h"
+#include "dvdnav_internal.h"
+
+/* Local prototypes */
+
+static void saveRSMinfo(vm_t *vm,int cellN, int blockN);
+static link_t play_PGC(vm_t *vm);
+static link_t play_PGC_PG(vm_t *vm, int pgN);
+static link_t play_PGC_post(vm_t *vm);
+static link_t play_PG(vm_t *vm);
+static link_t play_Cell(vm_t *vm);
+static link_t play_Cell_post(vm_t *vm);
+static link_t process_command(vm_t *vm,link_t link_values);
+
+static void ifoOpenNewVTSI(vm_t *vm,dvd_reader_t *dvd, int vtsN);
+static pgcit_t* get_PGCIT(vm_t *vm);
+
+/* get_XYZ returns a value.
+ * ser_XYZ sets state using passed parameters.
+ * returns success/failure.
+ */
+
+/* Can only be called when in VTS_DOMAIN */
+static int set_FP_PGC(vm_t *vm); /* FP */
+static int set_TT(vm_t *vm,int tt);
+static int set_VTS_TT(vm_t *vm,int vtsN, int vts_ttn);
+static int set_VTS_PTT(vm_t *vm,int vtsN, int vts_ttn, int part);
+
+static int set_MENU(vm_t *vm,int menu); /* VTSM & VMGM */
+
+/* Called in any domain */
+static int get_TT(vm_t *vm, int vtsN, int vts_ttn);
+static int get_ID(vm_t *vm,int id);
+static int get_PGCN(vm_t *vm);
+static int set_PGN(vm_t *vm); /* Set PGN based on (vm->state).CellN */
+static int set_PGC(vm_t *vm,int pgcN);
+
+/* Initialisation */
+
+vm_t* vm_new_vm() {
+ vm_t *vm = (vm_t*)calloc(sizeof(vm_t), sizeof(char));
+
+ return vm;
+}
+
+static void vm_print_current_domain_state(vm_t *vm) {
+ switch((vm->state).domain) {
+ case VTS_DOMAIN:
+ fprintf(stderr, "Video Title Domain: -\n");
+ break;
+
+ case VTSM_DOMAIN:
+ fprintf(stderr, "Video Title Menu Domain: -\n");
+ break;
+
+ case VMGM_DOMAIN:
+ fprintf(stderr, "Video Manager Menu Domain: -\n");
+ break;
+
+ case FP_DOMAIN:
+ fprintf(stderr, "First Play Domain: -\n");
+ break;
+
+ default:
+ fprintf(stderr, "Unknown Domain: -\n");
+ break;
+ }
+ fprintf(stderr, "VTS:%d PG:%u CELL:%u BLOCK:%u VTS_TTN:%u TTN:%u TT_PGCN:%u\n",
+ (vm->state).vtsN,
+ (vm->state).pgN,
+ (vm->state).cellN,
+ (vm->state).blockN,
+ (vm->state).VTS_TTN_REG,
+ (vm->state).TTN_REG,
+ (vm->state).TT_PGCN_REG);
+}
+
+void vm_stop(vm_t *vm) {
+ if(!vm)
+ return;
+
+ if(vm->vmgi) {
+ ifoClose(vm->vmgi);
+ vm->vmgi=NULL;
+ }
+
+ if(vm->vtsi) {
+ ifoClose(vm->vtsi);
+ vm->vmgi=NULL;
+ }
+
+ if(vm->dvd) {
+ DVDClose(vm->dvd);
+ vm->dvd=NULL;
+ }
+}
+
+void vm_free_vm(vm_t *vm) {
+ if(vm) {
+ vm_stop(vm);
+ free(vm);
+ }
+}
+
+/* IFO Access */
+
+ifo_handle_t *vm_get_vmgi(vm_t *vm) {
+ if(!vm)
+ return NULL;
+
+ return vm->vmgi;
+}
+
+ifo_handle_t *vm_get_vtsi(vm_t *vm) {
+ if(!vm)
+ return NULL;
+
+ return vm->vtsi;
+}
+
+/* Reader Access */
+
+dvd_reader_t *vm_get_dvd_reader(vm_t *vm) {
+ if(!vm)
+ return NULL;
+
+ return vm->dvd;
+}
+
+int vm_reset(vm_t *vm, char *dvdroot) /* , register_t regs) */ {
+ /* Setup State */
+ memset((vm->state).registers.SPRM, 0, sizeof(uint16_t)*24);
+ memset((vm->state).registers.GPRM, 0, sizeof((vm->state).registers.GPRM));
+ memset((vm->state).registers.GPRM_mode, 0, sizeof((vm->state).registers.GPRM_mode));
+ memset((vm->state).registers.GPRM_mode, 0, sizeof((vm->state).registers.GPRM_mode));
+ memset((vm->state).registers.GPRM_time, 0, sizeof((vm->state).registers.GPRM_time));
+ (vm->state).registers.SPRM[0] = ('e'<<8)|'n'; /* Player Menu Languange code */
+ (vm->state).AST_REG = 15; /* 15 why? */
+ (vm->state).SPST_REG = 62; /* 62 why? */
+ (vm->state).AGL_REG = 1;
+ (vm->state).TTN_REG = 1;
+ (vm->state).VTS_TTN_REG = 1;
+ /* (vm->state).TT_PGCN_REG = 0 */
+ (vm->state).PTTN_REG = 1;
+ (vm->state).HL_BTNN_REG = 1 << 10;
+
+ (vm->state).PTL_REG = 15; /* Parental Level */
+ (vm->state).registers.SPRM[12] = ('U'<<8)|'S'; /* Parental Management Country Code */
+ (vm->state).registers.SPRM[16] = ('e'<<8)|'n'; /* Initial Language Code for Audio */
+ (vm->state).registers.SPRM[18] = ('e'<<8)|'n'; /* Initial Language Code for Spu */
+ /* Player Regional Code Mask.
+ * bit0 = Region 1
+ * bit1 = Region 2
+ */
+ (vm->state).registers.SPRM[20] = 0x1; /* Player Regional Code Mask. Region free! */
+ (vm->state).registers.SPRM[14] = 0x100; /* Try Pan&Scan */
+
+ (vm->state).pgN = 0;
+ (vm->state).cellN = 0;
+ (vm->state).cell_restart = 0;
+
+ (vm->state).domain = FP_DOMAIN;
+ (vm->state).rsm_vtsN = 0;
+ (vm->state).rsm_cellN = 0;
+ (vm->state).rsm_blockN = 0;
+
+ (vm->state).vtsN = -1;
+
+ if (vm->dvd && dvdroot) {
+ /* a new dvd device has been requested */
+ vm_stop(vm);
+ }
+ if (!vm->dvd) {
+ vm->dvd = DVDOpen(dvdroot);
+ if(!vm->dvd) {
+ fprintf(stderr, "vm: faild to open/read the DVD\n");
+ return -1;
+ }
+
+ vm->vmgi = ifoOpenVMGI(vm->dvd);
+ if(!vm->vmgi) {
+ fprintf(stderr, "vm: faild to read VIDEO_TS.IFO\n");
+ return -1;
+ }
+ if(!ifoRead_FP_PGC(vm->vmgi)) {
+ fprintf(stderr, "vm: ifoRead_FP_PGC failed\n");
+ return -1;
+ }
+ if(!ifoRead_TT_SRPT(vm->vmgi)) {
+ fprintf(stderr, "vm: ifoRead_TT_SRPT failed\n");
+ return -1;
+ }
+ if(!ifoRead_PGCI_UT(vm->vmgi)) {
+ fprintf(stderr, "vm: ifoRead_PGCI_UT failed\n");
+ return -1;
+ }
+ if(!ifoRead_PTL_MAIT(vm->vmgi)) {
+ fprintf(stderr, "vm: ifoRead_PTL_MAIT failed\n");
+ ; /* return -1; Not really used for now.. */
+ }
+ if(!ifoRead_VTS_ATRT(vm->vmgi)) {
+ fprintf(stderr, "vm: ifoRead_VTS_ATRT failed\n");
+ ; /* return -1; Not really used for now.. */
+ }
+ if(!ifoRead_VOBU_ADMAP(vm->vmgi)) {
+ fprintf(stderr, "vm: ifoRead_VOBU_ADMAP vgmi failed\n");
+ ; /* return -1; Not really used for now.. */
+ }
+ /* ifoRead_TXTDT_MGI(vmgi); Not implemented yet */
+ }
+ else fprintf(stderr, "vm: reset\n");
+ if (vm->vmgi) {
+ fprintf(stderr, "DVD disk reports itself with Region mask 0x%08x. Maybe region %u.\n",
+ vm->vmgi->vmgi_mat->vmg_category,
+ (((vm->vmgi->vmgi_mat->vmg_category >> 16) ^ 0xff) & 0xff) );
+ }
+ return 0;
+}
+
+/* FIXME TODO XXX $$$ Handle error condition too... */
+int vm_start(vm_t *vm)
+{
+ link_t link_values;
+
+ /* Set pgc to FP(First Play) pgc */
+ set_FP_PGC(vm);
+ link_values = play_PGC(vm);
+ link_values = process_command(vm,link_values);
+ assert(link_values.command == PlayThis);
+ (vm->state).blockN = link_values.data1;
+#ifdef TRACE
+ fprintf(stderr, "vm_start: blockN set to 0x%x\n", (vm->state).blockN);
+#endif
+ assert( (vm->state).blockN == 0 );
+
+
+ return 0; /* ?? */
+}
+
+int vm_position_get(vm_t *vm, vm_position_t *position) {
+ position->button = (vm->state).HL_BTNN_REG >> 10;
+ position->spu_channel = (vm->state).SPST_REG;
+ position->audio_channel = (vm->state).AST_REG;
+ position->angle_channel = (vm->state).AGL_REG;
+ position->hop_channel = vm->hop_channel; /* Increases by one on each hop */
+ position->vts = (vm->state).vtsN;
+ position->domain = (vm->state).domain;
+ position->cell = (vm->state).cellN;
+ position->cell_restart = (vm->state).cell_restart;
+ position->still = (vm->state).pgc->cell_playback[(vm->state).cellN - 1].still_time;
+ position->vobu_start = (vm->state).pgc->cell_playback[(vm->state).cellN - 1].first_sector;
+ position->vobu_next = (vm->state).blockN;
+ /* position->vobu_next = 0; Just for now */
+ return 1;
+}
+
+int vm_position_print(vm_t *vm, vm_position_t *position) {
+ fprintf(stderr, "But=%x Spu=%x Aud=%x Ang=%x Hop=%x vts=%x dom=%x cell=%x cell_restart=%x still=%x start=%x next=%x\n",
+ position->button,
+ position->spu_channel,
+ position->audio_channel,
+ position->angle_channel,
+ position->hop_channel,
+ position->vts,
+ position->domain,
+ position->cell,
+ position->cell_restart,
+ position->still,
+ position->vobu_start,
+ position->vobu_next);
+ return 1;
+}
+
+
+int vm_start_title(vm_t *vm, int tt) {
+ link_t link_values;
+
+ set_TT(vm, tt);
+ link_values = play_PGC(vm);
+ link_values = process_command(vm, link_values);
+ assert(link_values.command == PlayThis);
+ (vm->state).blockN = link_values.data1;
+#ifdef TRACE
+ fprintf(stderr, "vm_start_title: blockN set to 0x%x\n", (vm->state).blockN);
+#endif
+ assert( (vm->state).blockN == 0 );
+
+ return 0; /* ?? */
+}
+
+int vm_jump_prog(vm_t *vm, int pr) {
+ link_t link_values;
+
+
+ set_PGC(vm, get_PGCN(vm));
+ (vm->state).pgN = pr; /* ?? set_PGC() clobbers pgN */
+ link_values = play_PG(vm);
+ link_values = process_command(vm, link_values);
+ assert(link_values.command == PlayThis);
+ (vm->state).blockN = link_values.data1;
+#ifdef TRACE
+ fprintf(stderr, "vm_jump_prog: blockN set to 0x%x\n", (vm->state).blockN);
+#endif
+ assert( (vm->state).blockN == 0 );
+
+ return 0; /* ?? */
+}
+
+int vm_eval_cmd(vm_t *vm, vm_cmd_t *cmd)
+{
+ link_t link_values;
+
+ if(vmEval_CMD(cmd, 1, &(vm->state).registers, &link_values)) {
+ link_values = process_command(vm, link_values);
+ assert(link_values.command == PlayThis);
+ (vm->state).blockN = link_values.data1;
+#ifdef TRACE
+ fprintf(stderr, "vm_eval_cmd: blockN set to 0x%x\n", (vm->state).blockN);
+#endif
+ assert( (vm->state).blockN == 0 );
+ return 1; /* Something changed, Jump */
+ } else {
+ return 0; /* It updated some state thats all... */
+ }
+}
+
+int vm_get_next_cell(vm_t *vm)
+{
+ link_t link_values;
+ link_values = play_Cell_post(vm);
+ link_values = process_command(vm,link_values);
+ assert(link_values.command == PlayThis);
+ (vm->state).blockN = link_values.data1;
+#ifdef TRACE
+ fprintf(stderr, "vm_get_next_cell: blockN set to 0x%x\n", (vm->state).blockN);
+#endif
+ assert( (vm->state).blockN == 0 );
+
+ return 0; /* ?? */
+}
+
+int vm_top_pg(vm_t *vm)
+{
+ link_t link_values;
+ link_values = play_PG(vm);
+ link_values = process_command(vm,link_values);
+ assert(link_values.command == PlayThis);
+ (vm->state).blockN = link_values.data1;
+#ifdef TRACE
+ fprintf(stderr, "vm_top_pg: blockN set to 0x%x\n", (vm->state).blockN);
+#endif
+ assert( (vm->state).blockN == 0 );
+
+ return 1; /* Jump */
+}
+
+int vm_go_up(vm_t *vm)
+{
+ link_t link_values;
+
+ if(set_PGC(vm, (vm->state).pgc->goup_pgc_nr))
+ assert(0);
+
+ link_values = play_PGC(vm);
+ link_values = process_command(vm,link_values);
+ assert(link_values.command == PlayThis);
+ (vm->state).blockN = link_values.data1;
+#ifdef TRACE
+ fprintf(stderr, "vm_go_up: blockN set to 0x%x\n", (vm->state).blockN);
+#endif
+ assert( (vm->state).blockN == 0 );
+
+ return 1; /* Jump */
+}
+
+int vm_next_pg(vm_t *vm)
+{
+ /* Do we need to get a updated pgN value first? */
+ (vm->state).pgN += 1;
+ return vm_top_pg(vm);
+}
+
+int vm_prev_pg(vm_t *vm)
+{
+ /* Do we need to get a updated pgN value first? */
+ (vm->state).pgN -= 1;
+ if((vm->state).pgN == 0) {
+ /* Check for previous PGCN ? */
+ (vm->state).pgN = 1;
+ /* return 0; */
+ }
+ return vm_top_pg(vm);
+}
+
+
+static domain_t menuid2domain(DVDMenuID_t menuid)
+{
+ domain_t result = VTSM_DOMAIN; /* Really shouldn't have to.. */
+
+ switch(menuid) {
+ case DVD_MENU_Title:
+ result = VMGM_DOMAIN;
+ break;
+ case DVD_MENU_Root:
+ case DVD_MENU_Subpicture:
+ case DVD_MENU_Audio:
+ case DVD_MENU_Angle:
+ case DVD_MENU_Part:
+ result = VTSM_DOMAIN;
+ break;
+ }
+
+ return result;
+}
+
+int vm_menu_call(vm_t *vm, DVDMenuID_t menuid, int block)
+{
+ domain_t old_domain;
+ link_t link_values;
+
+ /* Should check if we are allowed/can acces this menu */
+
+
+ /* FIXME XXX $$$ How much state needs to be restored
+ * when we fail to find a menu? */
+
+ old_domain = (vm->state).domain;
+
+ switch((vm->state).domain) {
+ case VTS_DOMAIN:
+ saveRSMinfo(vm, 0, block);
+ /* FALL THROUGH */
+ case VTSM_DOMAIN:
+ case VMGM_DOMAIN:
+ (vm->state).domain = menuid2domain(menuid);
+ if(get_PGCIT(vm) != NULL && set_MENU(vm, menuid) != -1) {
+ link_values = play_PGC(vm);
+ link_values = process_command(vm, link_values);
+ assert(link_values.command == PlayThis);
+ (vm->state).blockN = link_values.data1;
+#ifdef TRACE
+ fprintf(stderr, "vm_menu_call: blockN set to 0x%x\n", (vm->state).blockN);
+#endif
+ assert( (vm->state).blockN == 0 );
+ return 1; /* Jump */
+ } else {
+ (vm->state).domain = old_domain;
+ }
+ break;
+ case FP_DOMAIN: /* FIXME XXX $$$ What should we do here? */
+ break;
+ }
+
+ return 0;
+}
+
+
+int vm_resume(vm_t *vm)
+{
+ int i;
+ link_t link_values;
+
+ /* Check and see if there is any rsm info!! */
+ if((vm->state).rsm_vtsN == 0) {
+ return 0;
+ }
+
+ (vm->state).domain = VTS_DOMAIN;
+ ifoOpenNewVTSI(vm, vm->dvd, (vm->state).rsm_vtsN);
+ set_PGC(vm, (vm->state).rsm_pgcN);
+
+ /* These should never be set in SystemSpace and/or MenuSpace */
+ /* (vm->state).TTN_REG = (vm->state).rsm_tt; */
+ /* (vm->state).TT_PGCN_REG = (vm->state).rsm_pgcN; */
+ /* (vm->state).HL_BTNN_REG = (vm->state).rsm_btnn; */
+ for(i = 0; i < 5; i++) {
+ (vm->state).registers.SPRM[4 + i] = (vm->state).rsm_regs[i];
+ }
+
+ if((vm->state).rsm_cellN == 0) {
+ assert((vm->state).cellN); /* Checking if this ever happens */
+ (vm->state).pgN = 1;
+ link_values = play_PG(vm);
+ link_values = process_command(vm, link_values);
+ assert(link_values.command == PlayThis);
+ (vm->state).blockN = link_values.data1;
+#ifdef TRACE
+ fprintf(stderr, "vm_resume1: blockN set to 0x%x\n", (vm->state).blockN);
+#endif
+ assert( (vm->state).blockN == 0 );
+ } else {
+ (vm->state).cellN = (vm->state).rsm_cellN;
+ (vm->state).blockN = (vm->state).rsm_blockN;
+#ifdef TRACE
+ fprintf(stderr, "vm_resume2: blockN set to 0x%x\n", (vm->state).blockN);
+#endif
+ /* (vm->state).pgN = ?? does this gets the righ value in play_Cell, no! */
+ if(set_PGN(vm)) {
+ /* Were at or past the end of the PGC, should not happen for a RSM */
+ assert(0);
+ play_PGC_post(vm);
+ }
+ }
+
+ return 1; /* Jump */
+}
+
+/**
+ * Return the substream id for 'logical' audio stream audioN.
+ * 0 <= audioN < 8
+ */
+int vm_get_audio_stream(vm_t *vm, int audioN)
+{
+ int streamN = -1;
+#ifdef TRACE
+ fprintf(stderr,"dvdnav:vm.c:get_audio_stream audioN=%d\n",audioN);
+#endif
+ if((vm->state).domain == VTSM_DOMAIN
+ || (vm->state).domain == VMGM_DOMAIN
+ || (vm->state).domain == FP_DOMAIN) {
+ audioN = 0;
+ }
+
+ if(audioN < 8) {
+ /* Is there any contol info for this logical stream */
+ if((vm->state).pgc->audio_control[audioN] & (1<<15)) {
+ streamN = ((vm->state).pgc->audio_control[audioN] >> 8) & 0x07;
+ }
+ }
+
+ if((vm->state).domain == VTSM_DOMAIN
+ || (vm->state).domain == VMGM_DOMAIN
+ || (vm->state).domain == FP_DOMAIN) {
+ if(streamN == -1)
+ streamN = 0;
+ }
+
+ /* Should also check in vtsi/vmgi status that what kind of stream
+ * it is (ac3/lpcm/dts/sdds...) to find the right (sub)stream id */
+ return streamN;
+}
+
+/**
+ * Return the substream id for 'logical' subpicture stream subpN and given mode.
+ * 0 <= subpN < 32
+ * mode == 0 - widescreen
+ * mode == 1 - letterbox
+ * mode == 2 - pan&scan
+ */
+int vm_get_subp_stream(vm_t *vm, int subpN, int mode)
+{
+ int streamN = -1;
+ int source_aspect = vm_get_video_aspect(vm);
+
+ if((vm->state).domain == VTSM_DOMAIN
+ || (vm->state).domain == VMGM_DOMAIN
+ || (vm->state).domain == FP_DOMAIN) {
+ subpN = 0;
+ }
+
+ if(subpN < 32) { /* a valid logical stream */
+ /* Is this logical stream present */
+ if((vm->state).pgc->subp_control[subpN] & (1<<31)) {
+ if(source_aspect == 0) /* 4:3 */
+ streamN = ((vm->state).pgc->subp_control[subpN] >> 24) & 0x1f;
+ if(source_aspect == 3) /* 16:9 */
+ switch (mode) {
+ case 0:
+ streamN = ((vm->state).pgc->subp_control[subpN] >> 16) & 0x1f;
+ break;
+ case 1:
+ streamN = ((vm->state).pgc->subp_control[subpN] >> 8) & 0x1f;
+ break;
+ case 2:
+ streamN = (vm->state).pgc->subp_control[subpN] & 0x1f;
+ }
+ }
+ }
+
+ /* Paranoia.. if no stream select 0 anyway */
+/* I am not paranoid */
+/* if((vm->state).domain == VTSM_DOMAIN
+ || (vm->state).domain == VMGM_DOMAIN
+ || (vm->state).domain == FP_DOMAIN) {
+ if(streamN == -1)
+ streamN = 0;
+ }
+*/
+ /* Should also check in vtsi/vmgi status that what kind of stream it is. */
+ return streamN;
+}
+
+int vm_get_subp_active_stream(vm_t *vm, int mode)
+{
+ int subpN;
+ int streamN;
+ subpN = (vm->state).SPST_REG & ~0x40;
+ streamN = vm_get_subp_stream(vm, subpN, mode);
+
+ /* If no such stream, then select the first one that exists. */
+ if(streamN == -1) {
+ for(subpN = 0; subpN < 32; subpN++) {
+ if((vm->state).pgc->subp_control[subpN] & (1<<31)) {
+
+ streamN = vm_get_subp_stream(vm, subpN, mode);
+ break;
+ }
+ }
+ }
+
+ /* We should instead send the on/off status to the spudecoder / mixer */
+ /* If we are in the title domain see if the spu mixing is on */
+ if((vm->state).domain == VTS_DOMAIN && !((vm->state).SPST_REG & 0x40)) {
+ /* Bit 7 set means hide, and only let Forced display show */
+ return (streamN | 0x80);
+ } else {
+ return streamN;
+ }
+}
+
+int vm_get_audio_active_stream(vm_t *vm)
+{
+ int audioN;
+ int streamN;
+ audioN = (vm->state).AST_REG ;
+ streamN = vm_get_audio_stream(vm, audioN);
+
+ /* If no such stream, then select the first one that exists. */
+ if(streamN == -1) {
+ for(audioN = 0; audioN < 8; audioN++) {
+ if((vm->state).pgc->audio_control[audioN] & (1<<15)) {
+ streamN = vm_get_audio_stream(vm, audioN);
+ break;
+ }
+ }
+ }
+
+ return streamN;
+}
+
+
+void vm_get_angle_info(vm_t *vm, int *num_avail, int *current)
+{
+ *num_avail = 1;
+ *current = 1;
+
+ if((vm->state).domain == VTS_DOMAIN) {
+ /* TTN_REG does not allways point to the correct title.. */
+ title_info_t *title;
+ if((vm->state).TTN_REG > vm->vmgi->tt_srpt->nr_of_srpts)
+ return;
+ title = &vm->vmgi->tt_srpt->title[(vm->state).TTN_REG - 1];
+ if(title->title_set_nr != (vm->state).vtsN ||
+ title->vts_ttn != (vm->state).VTS_TTN_REG)
+ return;
+ *num_avail = title->nr_of_angles;
+ *current = (vm->state).AGL_REG;
+ if(*current > *num_avail) /* Is this really a good idea? */
+ *current = *num_avail;
+ }
+}
+
+
+void vm_get_audio_info(vm_t *vm, int *num_avail, int *current)
+{
+ if((vm->state).domain == VTS_DOMAIN) {
+ *num_avail = vm->vtsi->vtsi_mat->nr_of_vts_audio_streams;
+ *current = (vm->state).AST_REG;
+ } else if((vm->state).domain == VTSM_DOMAIN) {
+ *num_avail = vm->vtsi->vtsi_mat->nr_of_vtsm_audio_streams; /* 1 */
+ *current = 1;
+ } else if((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN) {
+ *num_avail = vm->vmgi->vmgi_mat->nr_of_vmgm_audio_streams; /* 1 */
+ *current = 1;
+ }
+}
+
+void vm_get_subp_info(vm_t *vm, int *num_avail, int *current)
+{
+ if((vm->state).domain == VTS_DOMAIN) {
+ *num_avail = vm->vtsi->vtsi_mat->nr_of_vts_subp_streams;
+ *current = (vm->state).SPST_REG;
+ } else if((vm->state).domain == VTSM_DOMAIN) {
+ *num_avail = vm->vtsi->vtsi_mat->nr_of_vtsm_subp_streams; /* 1 */
+ *current = 0x41;
+ } else if((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN) {
+ *num_avail = vm->vmgi->vmgi_mat->nr_of_vmgm_subp_streams; /* 1 */
+ *current = 0x41;
+ }
+}
+
+subp_attr_t vm_get_subp_attr(vm_t *vm, int streamN)
+{
+ subp_attr_t attr;
+
+ if((vm->state).domain == VTS_DOMAIN) {
+ attr = vm->vtsi->vtsi_mat->vts_subp_attr[streamN];
+ } else if((vm->state).domain == VTSM_DOMAIN) {
+ attr = vm->vtsi->vtsi_mat->vtsm_subp_attr;
+ } else if((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN) {
+ attr = vm->vmgi->vmgi_mat->vmgm_subp_attr;
+ }
+ return attr;
+}
+
+audio_attr_t vm_get_audio_attr(vm_t *vm, int streamN)
+{
+ audio_attr_t attr;
+
+ if((vm->state).domain == VTS_DOMAIN) {
+ attr = vm->vtsi->vtsi_mat->vts_audio_attr[streamN];
+ } else if((vm->state).domain == VTSM_DOMAIN) {
+ attr = vm->vtsi->vtsi_mat->vtsm_audio_attr;
+ } else if((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN) {
+ attr = vm->vmgi->vmgi_mat->vmgm_audio_attr;
+ }
+ return attr;
+}
+
+video_attr_t vm_get_video_attr(vm_t *vm)
+{
+ video_attr_t attr;
+
+ if((vm->state).domain == VTS_DOMAIN) {
+ attr = vm->vtsi->vtsi_mat->vts_video_attr;
+ } else if((vm->state).domain == VTSM_DOMAIN) {
+ attr = vm->vtsi->vtsi_mat->vtsm_video_attr;
+ } else if((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN) {
+ attr = vm->vmgi->vmgi_mat->vmgm_video_attr;
+ }
+ return attr;
+}
+
+void vm_get_video_res(vm_t *vm, int *width, int *height)
+{
+ video_attr_t attr;
+
+ attr = vm_get_video_attr(vm);
+
+ if(attr.video_format != 0)
+ *height = 576;
+ else
+ *height = 480;
+ switch(attr.picture_size) {
+ case 0:
+ *width = 720;
+ break;
+ case 1:
+ *width = 704;
+ break;
+ case 2:
+ *width = 352;
+ break;
+ case 3:
+ *width = 352;
+ *height /= 2;
+ break;
+ }
+}
+
+/* Must be called before domain is changed (get_PGCN()) */
+static void saveRSMinfo(vm_t *vm, int cellN, int blockN)
+{
+ int i;
+
+ if(cellN != 0) {
+ (vm->state).rsm_cellN = cellN;
+ (vm->state).rsm_blockN = 0;
+ } else {
+ (vm->state).rsm_cellN = (vm->state).cellN;
+ (vm->state).rsm_blockN = blockN;
+ }
+ (vm->state).rsm_vtsN = (vm->state).vtsN;
+ (vm->state).rsm_pgcN = get_PGCN(vm);
+
+ /* assert((vm->state).rsm_pgcN == (vm->state).TT_PGCN_REG); for VTS_DOMAIN */
+
+ for(i = 0; i < 5; i++) {
+ (vm->state).rsm_regs[i] = (vm->state).registers.SPRM[4 + i];
+ }
+}
+
+
+
+/* Figure out the correct pgN from the cell and update (vm->state). */
+static int set_PGN(vm_t *vm) {
+ int new_pgN = 0;
+
+ while(new_pgN < (vm->state).pgc->nr_of_programs
+ && (vm->state).cellN >= (vm->state).pgc->program_map[new_pgN])
+ new_pgN++;
+
+ if(new_pgN == (vm->state).pgc->nr_of_programs) /* We are at the last program */
+ if((vm->state).cellN > (vm->state).pgc->nr_of_cells)
+ return 1; /* We are past the last cell */
+
+ (vm->state).pgN = new_pgN;
+
+ if((vm->state).domain == VTS_DOMAIN) {
+ playback_type_t *pb_ty;
+ if((vm->state).TTN_REG > vm->vmgi->tt_srpt->nr_of_srpts)
+ return 0; /* ?? */
+ pb_ty = &vm->vmgi->tt_srpt->title[(vm->state).TTN_REG - 1].pb_ty;
+ if(pb_ty->multi_or_random_pgc_title == /* One_Sequential_PGC_Title */ 0) {
+#if 0 /* TTN_REG can't be trusted to have a correct value here... */
+ vts_ptt_srpt_t *ptt_srpt = vtsi->vts_ptt_srpt;
+ assert((vm->state).VTS_TTN_REG <= ptt_srpt->nr_of_srpts);
+ assert(get_PGCN() == ptt_srpt->title[(vm->state).VTS_TTN_REG - 1].ptt[0].pgcn);
+ assert(1 == ptt_srpt->title[(vm->state).VTS_TTN_REG - 1].ptt[0].pgn);
+#endif
+ (vm->state).PTTN_REG = (vm->state).pgN;
+ }
+ }
+
+ return 0;
+}
+
+static link_t play_PGC(vm_t *vm)
+{
+ link_t link_values;
+
+#ifdef TRACE
+ fprintf(stderr, "vm: play_PGC:");
+ if((vm->state).domain != FP_DOMAIN) {
+ fprintf(stderr, " (vm->state).pgcN (%i)\n", get_PGCN(vm));
+ } else {
+ fprintf(stderr, " first_play_pgc\n");
+ }
+#endif
+
+ /* This must be set before the pre-commands are executed because they */
+ /* might contain a CallSS that will save resume state */
+
+ /* FIXME: This may be only a temporary fix for something... */
+ (vm->state).pgN = 1;
+ (vm->state).cellN = 0;
+
+ /* eval -> updates the state and returns either
+ - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN)
+ - just play video i.e first PG
+ (This is what happens if you fall of the end of the pre_cmds)
+ - or a error (are there more cases?) */
+ if((vm->state).pgc->command_tbl && (vm->state).pgc->command_tbl->nr_of_pre) {
+ if(vmEval_CMD((vm->state).pgc->command_tbl->pre_cmds,
+ (vm->state).pgc->command_tbl->nr_of_pre,
+ &(vm->state).registers, &link_values)) {
+ /* link_values contains the 'jump' return value */
+ return link_values;
+ } else {
+#ifdef TRACE
+ fprintf(stderr, "PGC pre commands didn't do a Jump, Link or Call\n");
+#endif
+ }
+ }
+ return play_PG(vm);
+}
+
+static link_t play_PGC_PG(vm_t *vm, int pgN)
+{
+ link_t link_values;
+
+#ifdef TRACE
+ fprintf(stderr, "vm: play_PGC:");
+ if((vm->state).domain != FP_DOMAIN) {
+ fprintf(stderr, " (vm->state).pgcN (%i)\n", get_PGCN(vm));
+ } else {
+ fprintf(stderr, " first_play_pgc\n");
+ }
+#endif
+
+ /* This must be set before the pre-commands are executed because they */
+ /* might contain a CallSS that will save resume state */
+
+ /* FIXME: This may be only a temporary fix for something... */
+ (vm->state).pgN = pgN;
+ (vm->state).cellN = 0;
+
+ /* eval -> updates the state and returns either
+ - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN)
+ - just play video i.e first PG
+ (This is what happens if you fall of the end of the pre_cmds)
+ - or a error (are there more cases?) */
+ if((vm->state).pgc->command_tbl && (vm->state).pgc->command_tbl->nr_of_pre) {
+ if(vmEval_CMD((vm->state).pgc->command_tbl->pre_cmds,
+ (vm->state).pgc->command_tbl->nr_of_pre,
+ &(vm->state).registers, &link_values)) {
+ /* link_values contains the 'jump' return value */
+ return link_values;
+ } else {
+#ifdef TRACE
+ fprintf(stderr, "PGC pre commands didn't do a Jump, Link or Call\n");
+#endif
+ }
+ }
+ return play_PG(vm);
+}
+
+static link_t play_PG(vm_t *vm)
+{
+#ifdef TRACE
+ fprintf(stderr, "play_PG: (vm->state).pgN (%i)\n", (vm->state).pgN);
+#endif
+
+ assert((vm->state).pgN > 0);
+ if((vm->state).pgN > (vm->state).pgc->nr_of_programs) {
+#ifdef TRACE
+ fprintf(stderr, "(vm->state).pgN (%i) == pgc->nr_of_programs + 1 (%i)\n",
+ (vm->state).pgN, (vm->state).pgc->nr_of_programs + 1);
+#endif
+ /*assert((vm->state).pgN == (vm->state).pgc->nr_of_programs + 1);*/
+ return play_PGC_post(vm);
+ }
+
+ (vm->state).cellN = (vm->state).pgc->program_map[(vm->state).pgN - 1];
+
+ return play_Cell(vm);
+}
+
+
+static link_t play_Cell(vm_t *vm)
+{
+#ifdef TRACE
+ fprintf(stderr, "play_Cell: (vm->state).cellN (%i)\n", (vm->state).cellN);
+#endif
+
+ assert((vm->state).cellN > 0);
+ if((vm->state).cellN > (vm->state).pgc->nr_of_cells) {
+#ifdef TRACE
+ fprintf(stderr, "(vm->state).cellN (%i) == pgc->nr_of_cells + 1 (%i)\n",
+ (vm->state).cellN, (vm->state).pgc->nr_of_cells + 1);
+#endif
+ assert((vm->state).cellN == (vm->state).pgc->nr_of_cells + 1);
+ return play_PGC_post(vm);
+ }
+
+
+ /* Multi angle/Interleaved */
+ switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode) {
+ case 0: /* Normal */
+ assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 0);
+ break;
+ case 1: /* The first cell in the block */
+ switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type) {
+ case 0: /* Not part of a block */
+ assert(0);
+ case 1: /* Angle block */
+ /* Loop and check each cell instead? So we don't get outsid the block. */
+ (vm->state).cellN += (vm->state).AGL_REG - 1;
+ assert((vm->state).cellN <= (vm->state).pgc->nr_of_cells);
+ assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode != 0);
+ assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 1);
+ break;
+ case 2: /* ?? */
+ case 3: /* ?? */
+ default:
+ fprintf(stderr, "Invalid? Cell block_mode (%d), block_type (%d)\n",
+ (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode,
+ (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type);
+ }
+ break;
+ case 2: /* Cell in the block */
+ case 3: /* Last cell in the block */
+ /* These might perhaps happen for RSM or LinkC commands? */
+ default:
+ fprintf(stderr, "Cell is in block but did not enter at first cell!\n");
+ }
+
+ /* Updates (vm->state).pgN and PTTN_REG */
+ if(set_PGN(vm)) {
+ /* Should not happen */
+ link_t tmp = {LinkTailPGC, /* No Button */ 0, 0, 0};
+ assert(0);
+ return tmp;
+ }
+ (vm->state).cell_restart++;
+ fprintf(stderr, "libdvdnav: Cell should restart here\n");
+ {
+ link_t tmp = {PlayThis, /* Block in Cell */ 0, 0, 0};
+ return tmp;
+ }
+
+}
+
+static link_t play_Cell_post(vm_t *vm)
+{
+ cell_playback_t *cell;
+
+#ifdef TRACE
+ fprintf(stderr, "play_Cell_post: (vm->state).cellN (%i)\n", (vm->state).cellN);
+#endif
+
+ cell = &(vm->state).pgc->cell_playback[(vm->state).cellN - 1];
+
+ /* Still time is already taken care of before we get called. */
+
+ /* Deal with a Cell command, if any */
+ if(cell->cell_cmd_nr != 0) {
+ link_t link_values;
+
+ assert((vm->state).pgc->command_tbl != NULL);
+ assert((vm->state).pgc->command_tbl->nr_of_cell >= cell->cell_cmd_nr);
+#ifdef TRACE
+ fprintf(stderr, "Cell command pressent, executing\n");
+#endif
+ if(vmEval_CMD(&(vm->state).pgc->command_tbl->cell_cmds[cell->cell_cmd_nr - 1], 1,
+ &(vm->state).registers, &link_values)) {
+ return link_values;
+ } else {
+ fprintf(stderr, "Cell command didn't do a Jump, Link or Call\n");
+ /* Error ?? goto tail? goto next PG? or what? just continue? */
+ }
+ }
+
+
+ /* Where to continue after playing the cell... */
+ /* Multi angle/Interleaved */
+ switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode) {
+ case 0: /* Normal */
+ assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 0);
+ (vm->state).cellN++;
+ break;
+ case 1: /* The first cell in the block */
+ case 2: /* A cell in the block */
+ case 3: /* The last cell in the block */
+ default:
+ switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type) {
+ case 0: /* Not part of a block */
+ assert(0);
+ case 1: /* Angle block */
+ /* Skip the 'other' angles */
+ (vm->state).cellN++;
+ while((vm->state).cellN <= (vm->state).pgc->nr_of_cells
+ && (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode >= 2) {
+ (vm->state).cellN++;
+ }
+ break;
+ case 2: /* ?? */
+ case 3: /* ?? */
+ default:
+ fprintf(stderr, "Invalid? Cell block_mode (%d), block_type (%d)\n",
+ (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode,
+ (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type);
+ }
+ break;
+ }
+
+
+ /* Figure out the correct pgN for the new cell */
+ if(set_PGN(vm)) {
+#ifdef TRACE
+ fprintf(stderr, "last cell in this PGC\n");
+#endif
+ return play_PGC_post(vm);
+ }
+
+ return play_Cell(vm);
+}
+
+
+static link_t play_PGC_post(vm_t *vm)
+{
+ link_t link_values;
+
+#ifdef TRACE
+ fprintf(stderr, "play_PGC_post:\n");
+#endif
+
+ assert((vm->state).pgc->still_time == 0); /* FIXME $$$ */
+
+ /* eval -> updates the state and returns either
+ - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN)
+ - or a error (are there more cases?)
+ - if you got to the end of the post_cmds, then what ?? */
+ if((vm->state).pgc->command_tbl &&
+ vmEval_CMD((vm->state).pgc->command_tbl->post_cmds,
+ (vm->state).pgc->command_tbl->nr_of_post,
+ &(vm->state).registers, &link_values)) {
+ return link_values;
+ }
+
+ /* Or perhaps handle it here? */
+ {
+ link_t link_next_pgc = {LinkNextPGC, 0, 0, 0};
+ fprintf(stderr, "** Fell of the end of the pgc, continuing in NextPGC\n");
+ assert((vm->state).pgc->next_pgc_nr != 0);
+ /* Should end up in the STOP_DOMAIN if next_pgc is 0. */
+ return link_next_pgc;
+ }
+}
+
+
+static link_t process_command(vm_t *vm, link_t link_values)
+{
+ /* FIXME $$$ Move this to a separate function? */
+ vm->badness_counter++;
+ if (vm->badness_counter > 1) fprintf(stderr, "**** process_command re-entered %d*****\n",vm->badness_counter);
+ while(link_values.command != PlayThis) {
+
+#ifdef TRACE
+ vmPrint_LINK(link_values);
+
+ fprintf(stderr, "Link values %i %i %i %i\n", link_values.command,
+ link_values.data1, link_values.data2, link_values.data3);
+
+ fprintf(stderr, "Before:");
+ vm_print_current_domain_state(vm);
+#endif
+
+ switch(link_values.command) {
+ case LinkNoLink:
+ /* No Link */
+ /* BUTTON number:data1 */
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ fprintf(stderr, "libdvdnav: FIXME: in trouble...LinkNoLink - CRASHING!!!\n");
+ assert(0);
+
+ case LinkTopC:
+ /* Restart playing from the beginning of the current Cell. */
+ /* BUTTON number:data1 */
+ fprintf(stderr, "libdvdnav: FIXME: LinkTopC. Replay current Cell\n");
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ link_values = play_Cell(vm);
+ break;
+ case LinkNextC:
+ /* Link to Next Cell */
+ /* BUTTON number:data1 */
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ (vm->state).cellN += 1; /* if cellN becomes > nr_of_cells? it is handled in play_Cell() */
+ link_values = play_Cell(vm);
+ break;
+ case LinkPrevC:
+ /* Link to Previous Cell */
+ /* BUTTON number:data1 */
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ (vm->state).cellN -= 1; /* If cellN becomes < 1? it is handled in play_Cell() */
+ link_values = play_Cell(vm);
+ break;
+
+ case LinkTopPG:
+ /* Link to Top Program */
+ /* BUTTON number:data1 */
+ fprintf(stderr, "libdvdnav: FIXME: LinkTopPG. This should start the current PG again.\n");
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ /* Does pgN always contain the current value? */
+ link_values = play_PG(vm);
+ break;
+ case LinkNextPG:
+ /* Link to Next Program */
+ /* BUTTON number:data1 */
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ /* Does pgN always contain the current value? */
+ (vm->state).pgN += 1; /* FIXME: What if pgN becomes > pgc.nr_of_programs? */
+ link_values = play_PG(vm);
+ break;
+ case LinkPrevPG:
+ /* Link to Previous Program */
+ /* BUTTON number:data1 */
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ /* Does pgN always contain the current value? */
+ assert((vm->state).pgN > 1);
+ (vm->state).pgN -= 1; /* FIXME: What if pgN becomes < 1? */
+ link_values = play_PG(vm);
+ break;
+
+ case LinkTopPGC:
+ /* Restart playing from beginning of current Program Chain */
+ /* BUTTON number:data1 */
+ fprintf(stderr, "libdvdnav: FIXME: LinkTopPGC. Restart from beginning of current Program Chain\n");
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ link_values = play_PGC(vm);
+ break;
+ case LinkNextPGC:
+ /* Link to Next Program Chain */
+ /* BUTTON number:data1 */
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ assert((vm->state).pgc->next_pgc_nr != 0);
+ if(set_PGC(vm, (vm->state).pgc->next_pgc_nr))
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case LinkPrevPGC:
+ /* Link to Previous Program Chain */
+ /* BUTTON number:data1 */
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ assert((vm->state).pgc->prev_pgc_nr != 0);
+ if(set_PGC(vm, (vm->state).pgc->prev_pgc_nr))
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case LinkGoUpPGC:
+ /* Link to GoUp??? Program Chain */
+ /* BUTTON number:data1 */
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ assert((vm->state).pgc->goup_pgc_nr != 0);
+ if(set_PGC(vm, (vm->state).pgc->goup_pgc_nr))
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case LinkTailPGC:
+ /* Link to Tail??? Program Chain */
+ /* BUTTON number:data1 */
+ /* fprintf(stderr, "libdvdnav: FIXME: LinkTailPGC. What is LinkTailPGC?\n"); */
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+ link_values = play_PGC_post(vm);
+ break;
+
+ case LinkRSM:
+ {
+ /* Link to Resume */
+ int i;
+ /* Check and see if there is any rsm info!! */
+ (vm->state).domain = VTS_DOMAIN;
+ ifoOpenNewVTSI(vm, vm->dvd, (vm->state).rsm_vtsN);
+ set_PGC(vm, (vm->state).rsm_pgcN);
+
+ /* These should never be set in SystemSpace and/or MenuSpace */
+ /* (vm->state).TTN_REG = rsm_tt; ?? */
+ /* (vm->state).TT_PGCN_REG = (vm->state).rsm_pgcN; ?? */
+ for(i = 0; i < 5; i++) {
+ (vm->state).registers.SPRM[4 + i] = (vm->state).rsm_regs[i];
+ }
+
+ if(link_values.data1 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data1 << 10;
+
+ if((vm->state).rsm_cellN == 0) {
+ assert((vm->state).cellN); /* Checking if this ever happens */
+ /* assert( time/block/vobu is 0 ); */
+ (vm->state).pgN = 1;
+ link_values = play_PG(vm);
+ } else {
+ /* assert( time/block/vobu is _not_ 0 ); */
+ /* play_Cell_at_time */
+ /* (vm->state).pgN = ?? this gets the righ value in play_Cell */
+ (vm->state).cellN = (vm->state).rsm_cellN;
+ link_values.command = PlayThis;
+ link_values.data1 = (vm->state).rsm_blockN;
+ if(set_PGN(vm)) {
+ /* Were at the end of the PGC, should not happen for a RSM */
+ assert(0);
+ link_values.command = LinkTailPGC;
+ link_values.data1 = 0; /* No button */
+ }
+ }
+ }
+ break;
+ case LinkPGCN:
+ /* Link to Program Chain Number:data1 */
+ if(set_PGC(vm, link_values.data1))
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case LinkPTTN:
+ /* Link to Part of this Title Number:data1 */
+ /* BUTTON number:data2 */
+ assert((vm->state).domain == VTS_DOMAIN);
+ if(link_values.data2 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data2 << 10;
+ if(set_VTS_PTT(vm, (vm->state).vtsN, (vm->state).VTS_TTN_REG, link_values.data1) == -1)
+ assert(0);
+ link_values = play_PG(vm);
+ break;
+ case LinkPGN:
+ /* Link to Program Number:data1 */
+ /* BUTTON number:data2 */
+ if(link_values.data2 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data2 << 10;
+ /* Update any other state, PTTN perhaps? */
+ (vm->state).pgN = link_values.data1;
+ link_values = play_PG(vm);
+ break;
+ case LinkCN:
+ /* Link to Cell Number:data1 */
+ /* BUTTON number:data2 */
+ if(link_values.data2 != 0)
+ (vm->state).HL_BTNN_REG = link_values.data2 << 10;
+ /* Update any other state, pgN, PTTN perhaps? */
+ (vm->state).cellN = link_values.data1;
+ link_values = play_Cell(vm);
+ break;
+
+ case Exit:
+ fprintf(stderr, "libdvdnav: FIXME:in trouble...Link Exit - CRASHING!!!\n");
+ assert(0); /* What should we do here?? */
+
+ case JumpTT:
+ /* Jump to VTS Title Domain */
+ /* Only allowed from the First Play domain(PGC) */
+ /* or the Video Manager domain (VMG) */
+ assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN); /* ?? */
+ if(set_TT(vm,link_values.data1) == -1)
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case JumpVTS_TT:
+ /* Jump to Title:data1 in same VTS Title Domain */
+ /* Only allowed from the VTS Menu Domain(VTSM) */
+ /* or the Video Title Set Domain(VTS) */
+ assert((vm->state).domain == VTSM_DOMAIN || (vm->state).domain == VTS_DOMAIN); /* ?? */
+ fprintf(stderr, "libdvdnav: FIXME: Should be able to use get_VTS_PTT here.\n");
+ if(set_VTS_TT(vm,(vm->state).vtsN, link_values.data1) == -1)
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case JumpVTS_PTT:
+ /* Jump to Part:data2 of Title:data1 in same VTS Title Domain */
+ /* Only allowed from the VTS Menu Domain(VTSM) */
+ /* or the Video Title Set Domain(VTS) */
+ assert((vm->state).domain == VTSM_DOMAIN || (vm->state).domain == VTS_DOMAIN); /* ?? */
+ if(set_VTS_PTT(vm,(vm->state).vtsN, link_values.data1, link_values.data2) == -1)
+ assert(0);
+ link_values = play_PGC_PG(vm, link_values.data2);
+ break;
+
+ case JumpSS_FP:
+ /* Jump to First Play Domain */
+ /* Only allowed from the VTS Menu Domain(VTSM) */
+ /* or the Video Manager domain (VMG) */
+ assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == VTSM_DOMAIN); /* ?? */
+ set_FP_PGC(vm);
+ link_values = play_PGC(vm);
+ break;
+ case JumpSS_VMGM_MENU:
+ /* Jump to Video Manger domain - Title Menu:data1 or any PGC in VMG */
+ /* Allowed from anywhere except the VTS Title domain */
+ assert((vm->state).domain == VMGM_DOMAIN ||
+ (vm->state).domain == VTSM_DOMAIN ||
+ (vm->state).domain == FP_DOMAIN); /* ?? */
+ (vm->state).domain = VMGM_DOMAIN;
+ if(set_MENU(vm,link_values.data1) == -1)
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case JumpSS_VTSM:
+ /* Jump to a menu in Video Title domain, */
+ /* or to a Menu is the current VTS */
+ /* FIXME: This goes badly wrong for some DVDs. */
+ /* FIXME: Keep in touch with ogle people regarding what to do here */
+ /* ifoOpenNewVTSI:data1 */
+ /* VTS_TTN_REG:data2 */
+ /* get_MENU:data3 */
+#ifdef TRACE
+ fprintf(stderr, "dvdnav: BUG TRACKING *******************************************************************\n");
+ fprintf(stderr, " data1=%u data2=%u data3=%u\n",
+ link_values.data1,
+ link_values.data2,
+ link_values.data3);
+ fprintf(stderr, "dvdnav: *******************************************************************\n");
+#endif
+
+ if(link_values.data1 !=0) {
+ assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN); /* ?? */
+ (vm->state).domain = VTSM_DOMAIN;
+ ifoOpenNewVTSI(vm, vm->dvd, link_values.data1); /* Also sets (vm->state).vtsN */
+ } else {
+ /* This happens on 'The Fifth Element' region 2. */
+ assert((vm->state).domain == VTSM_DOMAIN);
+ }
+ /* I don't know what title is supposed to be used for. */
+ /* Alien or Aliens has this != 1, I think. */
+ /* assert(link_values.data2 == 1); */
+ (vm->state).VTS_TTN_REG = link_values.data2;
+ if(set_MENU(vm, link_values.data3) == -1)
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case JumpSS_VMGM_PGC:
+ /* get_PGC:data1 */
+ assert((vm->state).domain == VMGM_DOMAIN ||
+ (vm->state).domain == VTSM_DOMAIN ||
+ (vm->state).domain == FP_DOMAIN); /* ?? */
+ (vm->state).domain = VMGM_DOMAIN;
+ if(set_PGC(vm,link_values.data1) == -1)
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+
+ case CallSS_FP:
+ /* saveRSMinfo:data1 */
+ assert((vm->state).domain == VTS_DOMAIN); /* ?? */
+ /* Must be called before domain is changed */
+ saveRSMinfo(vm, link_values.data1, /* We dont have block info */ 0);
+ set_FP_PGC(vm);
+ link_values = play_PGC(vm);
+ break;
+ case CallSS_VMGM_MENU:
+ /* get_MENU:data1 */
+ /* saveRSMinfo:data2 */
+ assert((vm->state).domain == VTS_DOMAIN); /* ?? */
+ /* Must be called before domain is changed */
+ saveRSMinfo(vm,link_values.data2, /* We dont have block info */ 0);
+ (vm->state).domain = VMGM_DOMAIN;
+ if(set_MENU(vm,link_values.data1) == -1)
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case CallSS_VTSM:
+ /* get_MENU:data1 */
+ /* saveRSMinfo:data2 */
+ assert((vm->state).domain == VTS_DOMAIN); /* ?? */
+ /* Must be called before domain is changed */
+ saveRSMinfo(vm,link_values.data2, /* We dont have block info */ 0);
+ (vm->state).domain = VTSM_DOMAIN;
+ if(set_MENU(vm,link_values.data1) == -1)
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case CallSS_VMGM_PGC:
+ /* get_PGC:data1 */
+ /* saveRSMinfo:data2 */
+ assert((vm->state).domain == VTS_DOMAIN); /* ?? */
+ /* Must be called before domain is changed */
+ saveRSMinfo(vm,link_values.data2, /* We dont have block info */ 0);
+ (vm->state).domain = VMGM_DOMAIN;
+ if(set_PGC(vm,link_values.data1) == -1)
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+ case PlayThis:
+ /* Should never happen. */
+ assert(0);
+ break;
+ }
+
+#ifdef TRACE
+ fprintf(stderr, "After:");
+ vm_print_current_domain_state(vm);
+#endif
+
+ }
+ vm->badness_counter--;
+ return link_values;
+}
+
+/* Searches the TT tables, to find the current TT.
+ * returns the current TT.
+ * returns 0 if not found.
+ */
+static int get_TT(vm_t *vm, int vtsN, int vts_ttn) {
+ int i;
+ int tt=0;
+ for(i = 1; i <= vm->vmgi->tt_srpt->nr_of_srpts; i++) {
+ if( vm->vmgi->tt_srpt->title[i - 1].title_set_nr == vtsN &&
+ vm->vmgi->tt_srpt->title[i - 1].vts_ttn == vts_ttn) {
+ tt=i;
+ break;
+ }
+ }
+ return tt;
+}
+
+static int set_TT(vm_t *vm, int tt)
+{
+ assert(tt <= vm->vmgi->tt_srpt->nr_of_srpts);
+
+ (vm->state).TTN_REG = tt;
+
+ return set_VTS_TT(vm, vm->vmgi->tt_srpt->title[tt - 1].title_set_nr,
+ vm->vmgi->tt_srpt->title[tt - 1].vts_ttn);
+}
+
+
+static int set_VTS_TT(vm_t *vm, int vtsN, int vts_ttn)
+{
+ fprintf(stderr, "get_VTS_TT called, testing!!! vtsN=%d, vts_ttn=%d\n", vtsN, vts_ttn);
+ return set_VTS_PTT(vm, vtsN, vts_ttn, 1);
+ /* pgcN = get_ID(vm, vts_ttn); This might return -1 */
+ /*
+ assert(pgcN != -1);
+
+ (vm->state).TTN_REG = get_TT(*vm, vtsN, vts_ttn);
+ (vm->state).VTS_TTN_REG = vts_ttn;
+ (vm->state).vtsN =
+ */
+ /* Any other registers? */
+
+ /* return set_PGC(vm, pgcN); */
+}
+
+
+static int set_VTS_PTT(vm_t *vm, int vtsN, int /* is this really */ vts_ttn, int part)
+{
+ int pgcN, pgN, res;
+
+ (vm->state).domain = VTS_DOMAIN;
+ if(vtsN != (vm->state).vtsN)
+ ifoOpenNewVTSI(vm, vm->dvd, vtsN); /* Also sets (vm->state).vtsN */
+
+ assert(vts_ttn <= vm->vtsi->vts_ptt_srpt->nr_of_srpts);
+ assert(part <= vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].nr_of_ptts);
+
+ pgcN = vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].ptt[part - 1].pgcn;
+ pgN = vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].ptt[part - 1].pgn;
+
+ (vm->state).TT_PGCN_REG = pgcN;
+ (vm->state).PTTN_REG = pgN;
+
+ (vm->state).TTN_REG = get_TT(vm, vtsN, vts_ttn);
+ assert( (vm->state.TTN_REG) != 0 );
+ (vm->state).VTS_TTN_REG = vts_ttn;
+ (vm->state).vtsN = vtsN; /* Not sure about this one. We can get to it easily from TTN_REG */
+ /* Any other registers? */
+
+ res = set_PGC(vm, pgcN); // This clobber's state.pgN (sets it to 1), but we don't want clobbering here.
+ (vm->state).pgN = pgN; /* Part?? */
+ return res;
+}
+
+
+
+static int set_FP_PGC(vm_t *vm)
+{
+ (vm->state).domain = FP_DOMAIN;
+
+ (vm->state).pgc = vm->vmgi->first_play_pgc;
+
+ return 0;
+}
+
+
+static int set_MENU(vm_t *vm, int menu)
+{
+ assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == VTSM_DOMAIN);
+ return set_PGC(vm, get_ID(vm, menu));
+}
+
+/* Search for entry_id match of the PGC Category in the current VTS PGCIT table.
+ * Return pgcN based on entry_id match.
+ */
+static int get_ID(vm_t *vm, int id)
+{
+ int pgcN, i;
+ pgcit_t *pgcit;
+
+ /* Relies on state to get the correct pgcit. */
+ pgcit = get_PGCIT(vm);
+ assert(pgcit != NULL);
+ fprintf(stderr, "** Searching for menu (0x%x) entry PGC\n", id);
+
+ /* Get menu/title */
+ for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+ if((pgcit->pgci_srp[i].entry_id & 0x7f) == id) {
+ assert((pgcit->pgci_srp[i].entry_id & 0x80) == 0x80);
+ pgcN = i + 1;
+ return pgcN;
+ }
+ }
+ fprintf(stderr, "** No such id/menu (%d) entry PGC\n", id);
+ for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+ fprintf(stderr, "Available menus: 0x%x\n",
+ pgcit->pgci_srp[i].entry_id);
+ }
+ assert(0); /* Use assert for now, until the error is handled. */
+ return -1; /* error */
+}
+
+/* Set the vm->state to pgcN.
+ * Returns success/failure.
+ */
+static int set_PGC(vm_t *vm, int pgcN)
+{
+ /* FIXME: Keep this up to date with the ogle people */
+ pgcit_t *pgcit;
+
+ pgcit = get_PGCIT(vm);
+
+ assert(pgcit != NULL); /* ?? Make this return -1 instead */
+ if(pgcN < 1 || pgcN > pgcit->nr_of_pgci_srp) {
+ fprintf(stderr, "** No such pgcN = %d\n", pgcN);
+ assert(0);
+ return -1; /* error */
+ }
+
+ /* (vm->state).pgcN = pgcN; */
+ (vm->state).pgc = pgcit->pgci_srp[pgcN - 1].pgc;
+ (vm->state).pgN = 1;
+
+ if((vm->state).domain == VTS_DOMAIN)
+ (vm->state).TT_PGCN_REG = pgcN;
+
+ return 0;
+}
+
+static int get_PGCN(vm_t *vm)
+{
+ pgcit_t *pgcit;
+ int pgcN = 1;
+
+ pgcit = get_PGCIT(vm);
+
+ assert(pgcit != NULL);
+
+ while(pgcN <= pgcit->nr_of_pgci_srp) {
+ if(pgcit->pgci_srp[pgcN - 1].pgc == (vm->state).pgc)
+ return pgcN;
+ pgcN++;
+ }
+ fprintf(stderr, "libdvdnav: get_PGCN failed. Trying to find pgcN in domain %d \n",
+ (vm->state).domain);
+ assert(0);
+ return -1; /* error */
+}
+
+int vm_get_video_aspect(vm_t *vm)
+{
+ int aspect = 0;
+
+ switch ((vm->state).domain) {
+ case VTS_DOMAIN:
+ aspect = vm->vtsi->vtsi_mat->vts_video_attr.display_aspect_ratio;
+ break;
+ case VTSM_DOMAIN:
+ aspect = vm->vtsi->vtsi_mat->vtsm_video_attr.display_aspect_ratio;
+ break;
+ case VMGM_DOMAIN:
+ aspect = vm->vmgi->vmgi_mat->vmgm_video_attr.display_aspect_ratio;
+ break;
+ default:
+ fprintf(stderr, "libdvdnav: vm_get_video_aspect failed. Unknown domain %d\n",
+ (vm->state).domain);
+ assert(0);
+ break;
+ }
+#ifdef TRACE
+ fprintf(stderr, "dvdnav:get_video_aspect:aspect=%d\n",aspect);
+#endif
+ assert(aspect == 0 || aspect == 3);
+ (vm->state).registers.SPRM[14] &= ~(0x3 << 10);
+ (vm->state).registers.SPRM[14] |= aspect << 10;
+
+ return aspect;
+}
+
+int vm_get_video_scale_permission(vm_t *vm)
+{
+ int permission = 0;
+
+ if((vm->state).domain == VTS_DOMAIN) {
+ permission = vm->vtsi->vtsi_mat->vts_video_attr.permitted_df;
+ } else if((vm->state).domain == VTSM_DOMAIN) {
+ permission = vm->vtsi->vtsi_mat->vtsm_video_attr.permitted_df;
+ } else if((vm->state).domain == VMGM_DOMAIN) {
+ permission = vm->vmgi->vmgi_mat->vmgm_video_attr.permitted_df;
+ }
+#ifdef TRACE
+ fprintf(stderr, "dvdnav:get_video_scale_permission:permission=%d\n",permission);
+#endif
+
+ return permission;
+}
+
+static void ifoOpenNewVTSI(vm_t *vm, dvd_reader_t *dvd, int vtsN)
+{
+ if((vm->state).vtsN == vtsN) {
+ return; /* We alread have it */
+ }
+
+ if(vm->vtsi != NULL)
+ ifoClose(vm->vtsi);
+
+ vm->vtsi = ifoOpenVTSI(dvd, vtsN);
+ if(vm->vtsi == NULL) {
+ fprintf(stderr, "ifoOpenVTSI failed - CRASHING!!!\n");
+ assert(0);
+ }
+ if(!ifoRead_VTS_PTT_SRPT(vm->vtsi)) {
+ fprintf(stderr, "ifoRead_VTS_PTT_SRPT failed - CRASHING!!!\n");
+ assert(0);
+ }
+ if(!ifoRead_PGCIT(vm->vtsi)) {
+ fprintf(stderr, "ifoRead_PGCIT failed - CRASHING!!!\n");
+ assert(0);
+ }
+ if(!ifoRead_PGCI_UT(vm->vtsi)) {
+ fprintf(stderr, "ifoRead_PGCI_UT failed - CRASHING!!!\n");
+ assert(0);
+ }
+ if(!ifoRead_VOBU_ADMAP(vm->vtsi)) {
+ fprintf(stderr, "ifoRead_VOBU_ADMAP vtsi failed - CRASHING\n");
+ assert(0);
+ }
+ if(!ifoRead_TITLE_VOBU_ADMAP(vm->vtsi)) {
+ fprintf(stderr, "ifoRead_TITLE_VOBU_ADMAP vtsi failed - CRASHING\n");
+ assert(0);
+ }
+ (vm->state).vtsN = vtsN;
+}
+
+static pgcit_t* get_MENU_PGCIT(vm_t *vm, ifo_handle_t *h, uint16_t lang)
+{
+ int i;
+
+ if(h == NULL || h->pgci_ut == NULL) {
+ fprintf(stderr, "*** pgci_ut handle is NULL ***\n");
+ return NULL; /* error? */
+ }
+
+ i = 0;
+ while(i < h->pgci_ut->nr_of_lus
+ && h->pgci_ut->lu[i].lang_code != lang)
+ i++;
+ if(i == h->pgci_ut->nr_of_lus) {
+ fprintf(stderr, "Language '%c%c' not found, using '%c%c' instead\n",
+ (char)(lang >> 8), (char)(lang & 0xff),
+ (char)(h->pgci_ut->lu[0].lang_code >> 8),
+ (char)(h->pgci_ut->lu[0].lang_code & 0xff));
+ fprintf(stderr, "Menu Languages available: ");
+ for(i=0;i< h->pgci_ut->nr_of_lus;i++) {
+ fprintf(stderr, "%c%c ",
+ (char)(h->pgci_ut->lu[0].lang_code >> 8),
+ (char)(h->pgci_ut->lu[0].lang_code & 0xff));
+ }
+ fprintf(stderr, "\n");
+
+ i = 0; /* error? */
+ }
+
+ return h->pgci_ut->lu[i].pgcit;
+}
+
+/* Uses state to decide what to return */
+static pgcit_t* get_PGCIT(vm_t *vm) {
+ pgcit_t *pgcit;
+
+ switch ((vm->state).domain) {
+ case VTS_DOMAIN:
+ pgcit = vm->vtsi->vts_pgcit;
+ break;
+ case VTSM_DOMAIN:
+ pgcit = get_MENU_PGCIT(vm, vm->vtsi, (vm->state).registers.SPRM[0]);
+ break;
+ case VMGM_DOMAIN:
+ pgcit = get_MENU_PGCIT(vm, vm->vmgi, (vm->state).registers.SPRM[0]);
+ break;
+ default:
+ pgcit = NULL; /* Should never hapen */
+ fprintf(stderr, "libdvdnav: get_PGCIT: Unknown domain:%d\n",
+ (vm->state).domain);
+ assert(0);
+ break;
+ }
+
+ return pgcit;
+}
+
+/*
+ * $Log: vm.c,v $
+ * Revision 1.1 2002/08/08 17:49:21 richwareham
+ * First stage of DVD plugin -> dvdnav conversion
+ *
+ * Revision 1.24 2002/07/05 14:18:55 mroi
+ * report all spu types (widescreen, letterbox and pan&scan), not widescreen
+ * only and report the stream's scale permissions to detect pan&scan material
+ *
+ * Revision 1.23 2002/07/05 01:42:30 jcdutton
+ * Add more debug info for Menu language selection.
+ * Only do vm_start when we have to.
+ *
+ * Revision 1.22 2002/07/04 00:38:51 jcdutton
+ * Add some menu language printf's.
+ *
+ * Revision 1.21 2002/07/03 02:41:31 jcdutton
+ * Fix another long standing bug.
+ * Now changing PGC will force a start at the first PG of the PGC.
+ *
+ * Revision 1.20 2002/07/02 22:57:10 jcdutton
+ * Rename some of the functions in vm.c to help readability.
+ * Hopefully fix __FUNCTION__ problem. Use __func_ as recommended in C99.
+ * Fix bug where libdvdnav would not immeadiately replay the same cell due to menu buttons.
+ *
+ * Revision 1.19 2002/06/04 13:35:16 richwareham
+ * Removed more C++ style comments
+ *
+ * Revision 1.18 2002/05/30 19:25:08 richwareham
+ * Another small fix
+ *
+ * Revision 1.17 2002/05/30 15:56:41 richwareham
+ * Fixed (what appears to be) an error in JumpVTS_PTT implementation, it didn't call play_PGC after jumping.
+ *
+ * Revision 1.16 2002/04/24 21:15:25 jcdutton
+ * Quiet please!!!
+ *
+ * Revision 1.15 2002/04/23 13:18:31 jcdutton
+ * Insert some assert commands to hopefully catch a DVD which will give us information on what to do if these values are != 0.
+ *
+ * Revision 1.14 2002/04/23 12:34:39 f1rmb
+ * Why rewrite vm function, use it instead (this remark is for me, of course ;-) ).
+ * Comment unused var, shut compiler warnings.
+ *
+ * Revision 1.13 2002/04/23 02:12:27 jcdutton
+ * Re-implemented seeking.
+ *
+ * Revision 1.12 2002/04/22 22:00:48 jcdutton
+ * Start of rewrite of libdvdnav. Still need to re-implement seeking.
+ *
+ * Revision 1.11 2002/04/12 20:06:41 jcdutton
+ * Implement General Register Counters or GPRM counters.
+ * Navigation timers are not supported yet. SPRM[9] and SPRM[10].
+ *
+ * Revision 1.10 2002/04/12 12:43:36 jcdutton
+ * Display DVD disk region setting.
+ * Display possible RCE region protection message.
+ *
+ * Revision 1.9 2002/04/10 16:45:57 jcdutton
+ * Actually fix the const this time!
+ *
+ * Revision 1.8 2002/04/10 16:40:52 jcdutton
+ * Fix a const problem.
+ *
+ * Revision 1.7 2002/04/10 13:09:40 jcdutton
+ * Some improvements to decoder.c
+ * Registers should be updated correctly now, but still needs checking.
+ *
+ * Revision 1.6 2002/04/09 15:19:07 jcdutton
+ * Added some debug info, to hopefully help in tracking bugs in libdvdnav.
+ *
+ * Revision 1.5 2002/04/07 19:35:54 jcdutton
+ * Added some comments into the code.
+ *
+ * Revision 1.4 2002/04/06 18:31:50 jcdutton
+ * Some cleaning up.
+ * changed exit(1) to assert(0) so they actually get seen by the user so that it helps developers more.
+ *
+ * Revision 1.3 2002/04/02 18:22:27 richwareham
+ * Added reset patch from Kees Cook <kees@outflux.net>
+ *
+ * Revision 1.2 2002/04/01 18:56:28 richwareham
+ * Added initial example programs directory and make sure all debug/error output goes to stderr.
+ *
+ * Revision 1.1.1.1 2002/03/12 19:45:55 richwareham
+ * Initial import
+ *
+ * Revision 1.18 2002/01/22 16:56:49 jcdutton
+ * Fix clut after seeking.
+ * Add a few virtual machine debug messages, to help diagnose problems with "Deep Purple - Total Abandon" DVD as I don't have the DVD itvm.
+ * Fix a few debug messages, so they do not say FIXME.
+ * Move the FIXME debug messages to comments in the code.
+ *
+ * Revision 1.17 2002/01/21 01:16:30 jcdutton
+ * Added some debug messages, to hopefully get info from users.
+ *
+ * Revision 1.16 2002/01/20 21:40:46 jcdutton
+ * Start to fix some assert failures.
+ *
+ * Revision 1.15 2002/01/19 20:24:38 jcdutton
+ * Just some FIXME notes added.
+ *
+ * Revision 1.14 2002/01/13 22:17:57 jcdutton
+ * Change logging.
+ *
+ *
+ */
diff --git a/src/input/libdvdnav/vm.h b/src/input/libdvdnav/vm.h
new file mode 100644
index 000000000..a4ba8fb83
--- /dev/null
+++ b/src/input/libdvdnav/vm.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2000, 2001 Håkan Hjort
+ * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: vm.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifndef VM_H_INCLUDED
+#define VM_H_INCLUDED
+
+#include "decoder.h"
+#include <dvd_types.h>
+
+/* DOMAIN enum */
+
+typedef enum {
+ FP_DOMAIN = 1,
+ VTS_DOMAIN = 2,
+ VMGM_DOMAIN = 4,
+ VTSM_DOMAIN = 8
+} domain_t;
+
+/**
+ * State: SPRM, GPRM, Domain, pgc, pgN, cellN, ?
+ */
+typedef struct {
+ registers_t registers;
+
+ pgc_t *pgc; /* either this or *pgc is enough? */
+
+ domain_t domain;
+ int vtsN; /* 0 is vmgm? */
+ /* int pgcN; // either this or *pgc is enough. Which to use? */
+ int pgN; /* is this needed? can allways fid pgN from cellN? */
+ int cellN;
+ int32_t cell_restart; /* get cell to restart */
+ int blockN;
+
+ /* Resume info */
+ int rsm_vtsN;
+ int rsm_blockN; /* of nav_packet */
+ uint16_t rsm_regs[5]; /* system registers 4-8 */
+ int rsm_pgcN;
+ int rsm_cellN;
+} dvd_state_t;
+
+typedef struct vm_position_s {
+ int16_t button; /* Button highlighted */
+ uint32_t clut; /* CLUT to use, not needed in this struct */
+ int32_t vts; /* vts number to use */
+ int32_t domain; /* domain to use */
+ int32_t spu_channel; /* spu channel to use */
+ int32_t angle_channel; /* angle channel to use */
+ int32_t audio_channel; /* audio channel to use */
+ int32_t hop_channel; /* channel hopping. E.g menu button pressed */
+ int32_t title; /* title number */
+ int32_t chapter; /* chapter number */
+ int32_t cell; /* cell number */
+ int32_t cell_restart; /* get cell to restart */
+ int32_t still; /* is cell still */
+ int32_t vobu_start; /* block number of start of current VOBU in use */
+ int32_t vobu_next; /* block number within VOBU in use */
+} vm_position_t;
+
+typedef struct {
+ dvd_reader_t *dvd;
+ ifo_handle_t *vmgi;
+ ifo_handle_t *vtsi;
+ dvd_state_t state;
+ int badness_counter;
+ int32_t hop_channel;
+} vm_t;
+
+
+/* Audio stream number */
+#define AST_REG registers.SPRM[1]
+/* Subpicture stream number */
+#define SPST_REG registers.SPRM[2]
+/* Angle number */
+#define AGL_REG registers.SPRM[3]
+/* Title Track Number */
+#define TTN_REG registers.SPRM[4]
+/* VTS Title Track Number */
+#define VTS_TTN_REG registers.SPRM[5]
+/* PGC Number for this Title Track */
+#define TT_PGCN_REG registers.SPRM[6]
+/* Current Part of Title (PTT) number for (One_Sequential_PGC_Title) */
+#define PTTN_REG registers.SPRM[7]
+/* Highlighted Button Number (btn nr 1 == value 1024) */
+#define HL_BTNN_REG registers.SPRM[8]
+/* Parental Level */
+#define PTL_REG registers.SPRM[13]
+
+/* Initialisation & destruction */
+vm_t* vm_new_vm();
+void vm_free_vm(vm_t *vm);
+
+/* IFO access */
+ifo_handle_t *vm_get_vmgi(vm_t *vm);
+ifo_handle_t *vm_get_vtsi(vm_t *vm);
+
+/* Reader Access */
+dvd_reader_t *vm_get_dvd_reader(vm_t *vm);
+
+/* Jumping */
+int vm_start_title(vm_t *vm, int tt);
+int vm_jump_prog(vm_t *vm, int pr);
+
+/* Other calls */
+int vm_reset(vm_t *vm, char *dvdroot); /* , register_t regs); */
+int vm_start(vm_t *vm);
+int vm_position_get(vm_t *vm, vm_position_t *position);
+int vm_position_print(vm_t *vm, vm_position_t *position);
+int vm_eval_cmd(vm_t *vm, vm_cmd_t *cmd);
+int vm_get_next_cell(vm_t *vm);
+int vm_menu_call(vm_t *vm, DVDMenuID_t menuid, int block);
+int vm_resume(vm_t *vm);
+int vm_go_up(vm_t *vm);
+int vm_top_pg(vm_t *vm);
+int vm_next_pg(vm_t *vm);
+int vm_prev_pg(vm_t *vm);
+int vm_get_audio_stream(vm_t *vm, int audioN);
+int vm_get_audio_active_stream(vm_t *vm);
+int vm_get_subp_stream(vm_t *vm, int subpN, int mode);
+int vm_get_subp_active_stream(vm_t *vm, int mode);
+void vm_get_angle_info(vm_t *vm, int *num_avail, int *current);
+void vm_get_audio_info(vm_t *vm, int *num_avail, int *current);
+void vm_get_subp_info(vm_t *vm, int *num_avail, int *current);
+subp_attr_t vm_get_subp_attr(vm_t *vm, int streamN);
+audio_attr_t vm_get_audio_attr(vm_t *vm, int streamN);
+void vm_get_video_res(vm_t *vm, int *width, int *height);
+int vm_get_video_aspect(vm_t *vm);
+int vm_get_video_scale_permission(vm_t *vm);
+
+#endif /* VM_HV_INCLUDED */
+
diff --git a/src/input/libdvdnav/vmcmd.c b/src/input/libdvdnav/vmcmd.c
new file mode 100644
index 000000000..4ae8f92ba
--- /dev/null
+++ b/src/input/libdvdnav/vmcmd.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright (C) 2000, 2001 Martin Norbäck, Håkan Hjort
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: vmcmd.c,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "vmcmd.h"
+
+
+/* freebsd compatibility */
+#ifndef PRIu8
+#define PRIu8 "d"
+#endif
+
+/* freebsd compatibility */
+#ifndef PRIu16
+#define PRIu16 "d"
+#endif
+
+static const char *cmp_op_table[] = {
+ NULL, "&", "==", "!=", ">=", ">", "<=", "<"
+};
+static const char *set_op_table[] = {
+ NULL, "=", "<->", "+=", "-=", "*=", "/=", "%=", "rnd", "&=", "|=", "^="
+};
+
+static const char *link_table[] = {
+ "LinkNoLink", "LinkTopC", "LinkNextC", "LinkPrevC",
+ NULL, "LinkTopPG", "LinkNextPG", "LinkPrevPG",
+ NULL, "LinkTopPGC", "LinkNextPGC", "LinkPrevPGC",
+ "LinkGoUpPGC", "LinkTailPGC", NULL, NULL,
+ "RSM"
+};
+
+static const char *system_reg_table[] = {
+ "Menu Description Language Code",
+ "Audio Stream Number",
+ "Sub-picture Stream Number",
+ "Angle Number",
+ "Title Track Number",
+ "VTS Title Track Number",
+ "VTS PGC Number",
+ "PTT Number for One_Sequential_PGC_Title",
+ "Highlighted Button Number",
+ "Navigation Timer",
+ "Title PGC Number for Navigation Timer",
+ "Audio Mixing Mode for Karaoke",
+ "Country Code for Parental Management",
+ "Parental Level",
+ "Player Configurations for Video",
+ "Player Configurations for Audio",
+ "Initial Language Code for Audio",
+ "Initial Language Code Extension for Audio",
+ "Initial Language Code for Sub-picture",
+ "Initial Language Code Extension for Sub-picture",
+ "Player Regional Code",
+ "Reserved 21",
+ "Reserved 22",
+ "Reserved 23"
+};
+
+static const char *system_reg_abbr_table[] = {
+ NULL,
+ "ASTN",
+ "SPSTN",
+ "AGLN",
+ "TTN",
+ "VTS_TTN",
+ "TT_PGCN",
+ "PTTN",
+ "HL_BTNN",
+ "NVTMR",
+ "NV_PGCN",
+ NULL,
+ "CC_PLT",
+ "PLT",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+static void print_system_reg(uint16_t reg) {
+ if(reg < sizeof(system_reg_abbr_table) / sizeof(char *))
+ fprintf(stderr, "%s (SRPM:%d)", system_reg_table[reg], reg);
+ else
+ fprintf(stderr, " WARNING: Unknown system register ( reg=%d ) ", reg);
+}
+
+static void print_reg(uint8_t reg) {
+ if(reg & 0x80)
+ print_system_reg(reg & 0x7f);
+ else
+ if(reg < 16)
+ fprintf(stderr, "g[%" PRIu8 "]", reg);
+ else
+ fprintf(stderr, " WARNING: Unknown general register ");
+}
+
+static void print_cmp_op(uint8_t op) {
+ if(op < sizeof(cmp_op_table) / sizeof(char *) && cmp_op_table[op] != NULL)
+ fprintf(stderr, " %s ", cmp_op_table[op]);
+ else
+ fprintf(stderr, " WARNING: Unknown compare op ");
+}
+
+static void print_set_op(uint8_t op) {
+ if(op < sizeof(set_op_table) / sizeof(char *) && set_op_table[op] != NULL)
+ fprintf(stderr, " %s ", set_op_table[op]);
+ else
+ fprintf(stderr, " WARNING: Unknown set op ");
+}
+
+static void print_reg_or_data(command_t* command, int immediate, int byte) {
+ if(immediate) {
+ int i = vm_getbits(command, (byte*8), 16);
+
+ fprintf(stderr, "0x%x", i);
+ if(isprint(i & 0xff) && isprint((i>>8) & 0xff))
+ fprintf(stderr, " (\"%c%c\")", (char)((i>>8) & 0xff), (char)(i & 0xff));
+ } else {
+ print_reg(vm_getbits(command, ((byte + 1)*8), 8));
+ }
+}
+
+static void print_reg_or_data_2(command_t* command, int immediate, int byte) {
+ if(immediate)
+ fprintf(stderr, "0x%x", vm_getbits(command, ((byte*8)+1), 7));
+ else
+ fprintf(stderr, "g[%" PRIu8 "]", vm_getbits(command, ((byte*8)+4), 4));
+}
+
+static void print_if_version_1(command_t* command) {
+ uint8_t op = vm_getbits(command, 9, 3);
+
+ if(op) {
+ fprintf(stderr, "if (");
+ print_reg(vm_getbits(command,24,8));
+ print_cmp_op(op);
+ print_reg_or_data(command, vm_getbits(command, 8,1), 4);
+ fprintf(stderr, ") ");
+ }
+}
+
+static void print_if_version_2(command_t* command) {
+ uint8_t op = vm_getbits(command, 9, 3);
+
+ if(op) {
+ fprintf(stderr, "if (");
+ print_reg(vm_getbits(command, 48, 8));
+ print_cmp_op(op);
+ print_reg(vm_getbits(command, 56, 8));
+ fprintf(stderr, ") ");
+ }
+}
+
+static void print_if_version_3(command_t* command) {
+ uint8_t op = vm_getbits(command, 9, 3);
+
+ if(op) {
+ fprintf(stderr, "if (");
+ print_reg(vm_getbits(command, 20, 4));
+ print_cmp_op(op);
+ print_reg_or_data(command, vm_getbits(command, 8, 1), 6);
+ fprintf(stderr, ") ");
+ }
+}
+
+static void print_if_version_4(command_t* command) {
+ uint8_t op = vm_getbits(command, 9, 3);
+
+ if(op) {
+ fprintf(stderr, "if (");
+ print_reg(vm_getbits(command, 12, 4));
+ print_cmp_op(op);
+ print_reg_or_data(command, vm_getbits(command, 8, 1), 4);
+ fprintf(stderr, ") ");
+ }
+}
+
+static void print_special_instruction(command_t* command) {
+ uint8_t op = vm_getbits(command, 12, 4);
+
+ switch(op) {
+ case 0: /* NOP */
+ fprintf(stderr, "Nop");
+ break;
+ case 1: /* Goto line */
+ fprintf(stderr, "Goto %" PRIu8, vm_getbits(command, 56, 8));
+ break;
+ case 2: /* Break */
+ fprintf(stderr, "Break");
+ break;
+ case 3: /* Parental level */
+ fprintf(stderr, "SetTmpPML %" PRIu8 ", Goto %" PRIu8,
+ vm_getbits(command, 52, 4), vm_getbits(command, 56, 8));
+ break;
+ default:
+ fprintf(stderr, "WARNING: Unknown special instruction (%i)",
+ vm_getbits(command, 12, 4));
+ }
+}
+
+static void print_linksub_instruction(command_t* command) {
+ int linkop = vm_getbits(command, 59, 5);
+ int button = vm_getbits(command, 48, 6);
+
+ if(linkop < sizeof(link_table)/sizeof(char *) && link_table[linkop] != NULL)
+ fprintf(stderr, "%s (button %" PRIu8 ")", link_table[linkop], button);
+ else
+ fprintf(stderr, "WARNING: Unknown linksub instruction (%i)", linkop);
+}
+
+static void print_link_instruction(command_t* command, int optional) {
+ uint8_t op = vm_getbits(command, 12, 4);
+
+ if(optional && op)
+ fprintf(stderr, ", ");
+
+ switch(op) {
+ case 0:
+ if(!optional)
+ fprintf(stderr, "WARNING: NOP (link)!");
+ break;
+ case 1:
+ print_linksub_instruction(command);
+ break;
+ case 4:
+ fprintf(stderr, "LinkPGCN %" PRIu16, vm_getbits(command, 49, 15));
+ break;
+ case 5:
+ fprintf(stderr, "LinkPTT %" PRIu16 " (button %" PRIu8 ")",
+ vm_getbits(command, 54, 10), vm_getbits(command, 48, 6));
+ break;
+ case 6:
+ fprintf(stderr, "LinkPGN %" PRIu8 " (button %" PRIu8 ")",
+ vm_getbits(command, 57, 7), vm_getbits(command, 48, 6));
+ break;
+ case 7:
+ fprintf(stderr, "LinkCN %" PRIu8 " (button %" PRIu8 ")",
+ vm_getbits(command, 56, 8), vm_getbits(command, 48, 6));
+ break;
+ default:
+ fprintf(stderr, "WARNING: Unknown link instruction");
+ }
+}
+
+static void print_jump_instruction(command_t* command) {
+ switch(vm_getbits(command, 12, 4)) {
+ case 1:
+ fprintf(stderr, "Exit");
+ break;
+ case 2:
+ fprintf(stderr, "JumpTT %" PRIu8, vm_getbits(command, 41, 7));
+ break;
+ case 3:
+ fprintf(stderr, "JumpVTS_TT %" PRIu8, vm_getbits(command, 41, 7));
+ break;
+ case 5:
+ fprintf(stderr, "JumpVTS_PTT %" PRIu8 ":%" PRIu16,
+ vm_getbits(command, 41, 7), vm_getbits(command, 22, 10));
+ break;
+ case 6:
+ switch(vm_getbits(command, 40, 2)) {
+ case 0:
+ fprintf(stderr, "JumpSS FP");
+ break;
+ case 1:
+ fprintf(stderr, "JumpSS VMGM (menu %" PRIu8 ")", vm_getbits(command, 44, 4));
+ break;
+ case 2:
+ fprintf(stderr, "JumpSS VTSM (vts %" PRIu8 ", title %" PRIu8
+ ", menu %" PRIu8 ")", vm_getbits(command, 32, 8), vm_getbits(command, 24, 8), vm_getbits(command, 44, 4));
+ break;
+ case 3:
+ fprintf(stderr, "JumpSS VMGM (pgc %" PRIu8 ")", vm_getbits(command, 17, 15));
+ break;
+ }
+ break;
+ case 8:
+ switch(vm_getbits(command, 40, 2)) {
+ case 0:
+ fprintf(stderr, "CallSS FP (rsm_cell %" PRIu8 ")",
+ vm_getbits(command, 32, 8));
+ break;
+ case 1:
+ fprintf(stderr, "CallSS VMGM (menu %" PRIu8
+ ", rsm_cell %" PRIu8 ")", vm_getbits(command, 44, 4), vm_getbits(command, 32, 8));
+ break;
+ case 2:
+ fprintf(stderr, "CallSS VTSM (menu %" PRIu8
+ ", rsm_cell %" PRIu8 ")", vm_getbits(command, 44, 4), vm_getbits(command, 32, 8));
+ break;
+ case 3:
+ fprintf(stderr, "CallSS VMGM (pgc %" PRIu8 ", rsm_cell %" PRIu8 ")",
+ vm_getbits(command, 17, 15), vm_getbits(command, 32, 8));
+ break;
+ }
+ break;
+ default:
+ fprintf(stderr, "WARNING: Unknown Jump/Call instruction");
+ }
+}
+
+static void print_system_set(command_t* command) {
+ int i;
+
+ switch(vm_getbits(command, 4, 4)) {
+ case 1: /* Set system reg 1 &| 2 &| 3 (Audio, Subp. Angle) */
+ for(i = 1; i <= 3; i++) {
+ if(vm_getbits(command, ((2+i)*8), 1)) {
+ print_system_reg(i);
+ fprintf(stderr, " = ");
+ print_reg_or_data_2(command, vm_getbits(command, 3, 1), 2 + i);
+ fprintf(stderr, " ");
+ }
+ }
+ break;
+ case 2: /* Set system reg 9 & 10 (Navigation timer, Title PGC number) */
+ print_system_reg(9);
+ fprintf(stderr, " = ");
+ print_reg_or_data(command, vm_getbits(command, 3, 1), 2);
+ fprintf(stderr, " ");
+ print_system_reg(10);
+ fprintf(stderr, " = %" PRIu8, vm_getbits(command, 40, 8)); /* ?? */
+ break;
+ case 3: /* Mode: Counter / Register + Set */
+ fprintf(stderr, "SetMode ");
+ if(vm_getbits(command, 40, 1))
+ fprintf(stderr, "Counter ");
+ else
+ fprintf(stderr, "Register ");
+ print_reg(vm_getbits(command, 44, 4));
+ print_set_op(0x1); /* '=' */
+ print_reg_or_data(command, vm_getbits(command, 3, 1), 2);
+ break;
+ case 6: /* Set system reg 8 (Highlighted button) */
+ print_system_reg(8);
+ if(vm_getbits(command, 3, 1)) /* immediate */
+ fprintf(stderr, " = 0x%x (button no %d)", vm_getbits(command, 32, 16), vm_getbits(command, 32, 6));
+ else
+ fprintf(stderr, " = g[%" PRIu8 "]", vm_getbits(command, 44, 4));
+ break;
+ default:
+ fprintf(stderr, "WARNING: Unknown system set instruction (%i)",
+ vm_getbits(command, 4, 4));
+ }
+}
+
+static void print_set_version_1(command_t* command) {
+ uint8_t set_op = vm_getbits(command, 4, 4);
+
+ if(set_op) {
+ print_reg(vm_getbits(command, 24, 8)); /* FIXME: This is different from decoder.c!!! */
+ print_set_op(set_op);
+ print_reg_or_data(command, vm_getbits(command, 3, 1), 4);
+ } else {
+ fprintf(stderr, "NOP");
+ }
+}
+
+static void print_set_version_2(command_t* command) {
+ uint8_t set_op = vm_getbits(command, 4, 4);
+
+ if(set_op) {
+ print_reg(vm_getbits(command, 12, 4));
+ print_set_op(set_op);
+ print_reg_or_data(command, vm_getbits(command, 3, 1), 2);
+ } else {
+ fprintf(stderr, "NOP");
+ }
+}
+
+void vmPrint_mnemonic(vm_cmd_t *vm_command) {
+ command_t command;
+ command.instruction =( (uint64_t) vm_command->bytes[0] << 56 ) |
+ ( (uint64_t) vm_command->bytes[1] << 48 ) |
+ ( (uint64_t) vm_command->bytes[2] << 40 ) |
+ ( (uint64_t) vm_command->bytes[3] << 32 ) |
+ ( (uint64_t) vm_command->bytes[4] << 24 ) |
+ ( (uint64_t) vm_command->bytes[5] << 16 ) |
+ ( (uint64_t) vm_command->bytes[6] << 8 ) |
+ (uint64_t) vm_command->bytes[7] ;
+ command.examined = 0;
+
+ switch(vm_getbits(&command,0,3)) { /* three first bits */
+ case 0: /* Special instructions */
+ print_if_version_1(&command);
+ print_special_instruction(&command);
+ break;
+ case 1: /* Jump/Call or Link instructions */
+ if(vm_getbits(&command,3,1)) {
+ print_if_version_2(&command);
+ print_jump_instruction(&command);
+ } else {
+ print_if_version_1(&command);
+ print_link_instruction(&command, 0); /* must be pressent */
+ }
+ break;
+ case 2: /* Set System Parameters instructions */
+ print_if_version_2(&command);
+ print_system_set(&command);
+ print_link_instruction(&command, 1); /* either 'if' or 'link' */
+ break;
+ case 3: /* Set General Parameters instructions */
+ print_if_version_3(&command);
+ print_set_version_1(&command);
+ print_link_instruction(&command, 1); /* either 'if' or 'link' */
+ break;
+ case 4: /* Set, Compare -> LinkSub instructions */
+ print_set_version_2(&command);
+ fprintf(stderr, ", ");
+ print_if_version_4(&command);
+ print_linksub_instruction(&command);
+ break;
+ case 5: /* Compare -> (Set and LinkSub) instructions */
+ print_if_version_4(&command);
+ fprintf(stderr, "{ ");
+ print_set_version_2(&command);
+ fprintf(stderr, ", ");
+ print_linksub_instruction(&command);
+ fprintf(stderr, " }");
+ break;
+ case 6: /* Compare -> Set, always LinkSub instructions */
+ print_if_version_4(&command);
+ fprintf(stderr, "{ ");
+ print_set_version_2(&command);
+ fprintf(stderr, " } ");
+ print_linksub_instruction(&command);
+ break;
+ default:
+ fprintf(stderr, "WARNING: Unknown instruction type (%i)", vm_getbits(&command, 0, 3));
+ }
+ /* Check if there still are bits set that were not examined */
+
+ if(command.instruction & ~ command.examined) {
+ fprintf(stderr, " libdvdnav: vmcmd.c: [WARNING, unknown bits:");
+ fprintf(stderr, " %08llx", (command.instruction & ~ command.examined) );
+ fprintf(stderr, "]");
+ }
+}
+
+void vmPrint_CMD(int row, vm_cmd_t *vm_command) {
+ int i;
+
+ fprintf(stderr, "(%03d) ", row + 1);
+ for(i = 0; i < 8; i++)
+ fprintf(stderr, "%02x ", vm_command->bytes[i]);
+ fprintf(stderr, "| ");
+
+ vmPrint_mnemonic(vm_command);
+ fprintf(stderr, "\n");
+}
+
+
diff --git a/src/input/libdvdnav/vmcmd.h b/src/input/libdvdnav/vmcmd.h
new file mode 100644
index 000000000..c4079f245
--- /dev/null
+++ b/src/input/libdvdnav/vmcmd.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2000, 2001 Martin Norbäck, Håkan Hjort
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: vmcmd.h,v 1.1 2002/08/08 17:49:21 richwareham Exp $
+ *
+ */
+
+#ifndef VMCMD_H_INCLUDED
+#define VMCMD_H_INCLUDED
+
+#include <inttypes.h>
+#include <dvdread/ifo_types.h> /* Only for vm_cmd_t */
+#include "decoder.h"
+
+void vmPrint_mnemonic(vm_cmd_t *command);
+void vmPrint_CMD(int row, vm_cmd_t *command);
+
+#endif /* VMCMD_H_INCLUDED */