Mercurial > libdvdnav.hg
changeset 0:3ddf0eaece51 src
Initial revision
author | richwareham |
---|---|
date | Tue, 12 Mar 2002 19:45:53 +0000 |
parents | |
children | ce4358919a67 |
files | Makefile.am decoder.c decoder.h dvd_types.h dvdnav.c dvdnav.h dvdnav_events.h dvdnav_internal.h highlight.c navigation.c read_cache.c read_cache.h searching.c settings.c vm.c vm.h vmcmd.c vmcmd.h |
diffstat | 18 files changed, 6054 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile.am Tue Mar 12 19:45:53 2002 +0000 @@ -0,0 +1,48 @@ +CFLAGS = @GLOBAL_CFLAGS@ +DEBUG_CFLAGS = @DEBUG_CFLAGS@ + +lib_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 = $(DVDREAD_LIBS) + +include_HEADERS = decoder.h dvdnav.h dvdnav_events.h \ + dvdnav_internal.h vm.h vmcmd.h read_cache.h dvd_types.h + +### +# Install header files (default=$includedir/xine) +# +install-includeHEADERS: $(include_HEADERS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(includedir)/dvdnav + @list='$(include_HEADERS)'; for p in $$list; do \ + if test -f "$$p"; then d= ; else d="$(srcdir)/"; fi; \ + echo " $(INSTALL_DATA) $$d$$p $(DESTDIR)$(includedir)/dvdnav/$$p"; \ + $(INSTALL_DATA) $$d$$p $(DESTDIR)$(includedir)/dvdnav/$$p; \ + done + + +### +# Remove them +# +uninstall-includeHEADERS: + @$(NORMAL_UNINSTALL) + list='$(include_HEADERS)'; for p in $$list; do \ + rm -f $(DESTDIR)$(includedir)/dvdnav/$$p; \ + done + +debug: + $(MAKE) CFLAGS="$(DEBUG_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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/decoder.c Tue Mar 12 19:45:53 2002 +0000 @@ -0,0 +1,706 @@ +/* + * 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$ + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <inttypes.h> +#include <string.h> /* For memset */ +#include <dvdread/ifo_types.h> /* vm_cmd_t */ +#include "vmcmd.h" +#include "decoder.h" + +#ifndef bool +typedef int bool; +#endif + +typedef struct +{ + uint8_t bits[8]; + uint8_t examined[8]; +} cmd_t; + +/* Fix theses two.. pass as parameters instead. */ +static cmd_t cmd; +static registers_t *state; + +/* Get count bits of command from byte and bit position. */ +static uint32_t bits(int byte, int bit, int count) { + uint32_t val = 0; + int bit_mask; + + while(count--) { + if(bit > 7) { + bit = 0; + byte++; + } + bit_mask = 0x01 << (7 - bit); + val <<= 1; + if(cmd.bits[byte] & bit_mask) + val |= 1; + cmd.examined[byte] |= bit_mask; + bit++; + } + return val; +} + + +/* 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(uint8_t reg) { + if(reg & 0x80) { + return state->SPRM[reg & 0x1f]; /* FIXME max 24 not 32 */ + } else { + return state->GPRM[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(int imm, int byte) { + if(imm) { /* immediate */ + return bits(byte, 0, 16); + } else { + return eval_reg(bits(byte + 1, 0, 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(int imm, int byte) { + if(imm) /* immediate */ + return bits(byte, 1, 7); + else + return state->GPRM[bits(byte, 4, 4)]; +} + + +/* Compare data using operation, return result from comparison. + Helper function for the different if functions. */ +static bool 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 bool eval_if_version_1(void) { + uint8_t op = bits(1, 1, 3); + if(op) { + return eval_compare(op, eval_reg(bits(3, 0, 8)), + eval_reg_or_data(bits(1, 0, 1), 4)); + } + return 1; +} + +/* Evaluate if version 2. + This version only compares register which are in byte 6 and 7 */ +static bool eval_if_version_2(void) { + uint8_t op = bits(1, 1, 3); + if(op) { + return eval_compare(op, eval_reg(bits(6, 0, 8)), + eval_reg(bits(7, 0, 8))); + } + return 1; +} + +/* Evaluate if version 3. + Has comparison data in byte 2 and 6-7 (immediate or register) */ +static bool eval_if_version_3(void) { + uint8_t op = bits(1, 1, 3); + if(op) { + return eval_compare(op, eval_reg(bits(2, 0, 8)), + eval_reg_or_data(bits(1, 0, 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 (4bits) */ +static bool eval_if_version_4(void) { + uint8_t op = bits(1, 1, 3); + if(op) { + return eval_compare(op, eval_reg(bits(1, 4, 4)), + eval_reg_or_data(bits(1, 0, 1), 4)); + } + return 1; +} + +/* Evaluate special instruction.... returns the new row/line number, + 0 if no new row and 256 if Break. */ +static int eval_special_instruction(bool cond) { + int line, level; + + switch(bits(1, 4, 4)) { + case 0: /* NOP */ + line = 0; + return cond ? line : 0; + case 1: /* Goto line */ + line = bits(7, 0, 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 = bits(7, 0, 8); + level = bits(6, 4, 4); + if(cond) { + /* This always succeeds now, if we want real parental protection */ + /* we need to ask the user and have passwords and stuff. */ + state->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 bool eval_link_subins(bool cond, link_t *return_values) { + uint16_t button = bits(6, 0, 6); + uint8_t linkop = bits(7, 3, 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 bool eval_link_instruction(bool cond, link_t *return_values) { + uint8_t op = bits(1, 4, 4); + + switch(op) { + case 1: + return eval_link_subins(cond, return_values); + case 4: + return_values->command = LinkPGCN; + return_values->data1 = bits(6, 1, 15); + return cond; + case 5: + return_values->command = LinkPTTN; + return_values->data1 = bits(6, 6, 10); + return_values->data2 = bits(6, 0, 6); + return cond; + case 6: + return_values->command = LinkPGN; + return_values->data1 = bits(7, 1, 7); + return_values->data2 = bits(6, 0, 6); + return cond; + case 7: + return_values->command = LinkCN; + return_values->data1 = bits(7, 0, 8); + return_values->data2 = bits(6, 0, 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 bool eval_jump_instruction(bool cond, link_t *return_values) { + + switch(bits(1, 4, 4)) { + case 1: + return_values->command = Exit; + return cond; + case 2: + return_values->command = JumpTT; + return_values->data1 = bits(5, 1, 7); + return cond; + case 3: + return_values->command = JumpVTS_TT; + return_values->data1 = bits(5, 1, 7); + return cond; + case 5: + return_values->command = JumpVTS_PTT; + return_values->data1 = bits(5, 1, 7); + return_values->data2 = bits(2, 6, 10); + return cond; + case 6: + switch(bits(5,0,2)) { + case 0: + return_values->command = JumpSS_FP; + return cond; + case 1: + return_values->command = JumpSS_VMGM_MENU; + return_values->data1 = bits(5, 4, 4); + return cond; + case 2: + return_values->command = JumpSS_VTSM; + return_values->data1 = bits(4, 0, 8); + return_values->data2 = bits(3, 0, 8); + return_values->data3 = bits(5, 4, 4); + return cond; + case 3: + return_values->command = JumpSS_VMGM_PGC; + return_values->data1 = bits(2, 1, 15); + return cond; + } + break; + case 8: + switch(bits(5,0,2)) { + case 0: + return_values->command = CallSS_FP; + return_values->data1 = bits(4, 0, 8); + return cond; + case 1: + return_values->command = CallSS_VMGM_MENU; + return_values->data1 = bits(5, 4, 4); + return_values->data2 = bits(4, 0 ,8); + return cond; + case 2: + return_values->command = CallSS_VTSM; + return_values->data1 = bits(5, 4, 4); + return_values->data2 = bits(4, 0, 8); + return cond; + case 3: + return_values->command = CallSS_VMGM_PGC; + return_values->data1 = bits(2, 1, 15); + return_values->data2 = bits(4, 0, 8); + return cond; + } + break; + } + return 0; +} + +/* Evaluate a set sytem register instruction + May contain a link so return the same as eval_link */ +static bool eval_system_set(int cond, link_t *return_values) { + int i; + uint16_t data, data2; + + switch(bits(0, 4, 4)) { + case 1: /* Set system reg 1 &| 2 &| 3 (Audio, Subp. Angle) */ + for(i = 1; i <= 3; i++) { + if(bits(2 + i, 0, 1)) { + data = eval_reg_or_data_2(bits(0, 3, 1), 2 + i); + if(cond) { + state->SPRM[i] = data; + } + } + } + break; + case 2: /* Set system reg 9 & 10 (Navigation timer, Title PGC number) */ + data = eval_reg_or_data(bits(0, 3, 1), 2); + data2 = bits(5, 0, 8); /* ?? size */ + if(cond) { + state->SPRM[9] = data; /* time */ + state->SPRM[10] = data2; /* pgcN */ + } + break; + case 3: /* Mode: Counter / Register + Set */ + data = eval_reg_or_data(bits(0, 3, 1), 2); + data2 = bits(5, 4, 4); + if(bits(5, 0, 1)) { + fprintf(stderr, "Detected SetGPRMMD Counter!! This is unsupported.\n"); + /* exit(-1); */ + } else { + ; + } + if(cond) { + state->GPRM[data2] = data; + } + break; + case 6: /* Set system reg 8 (Highlighted button) */ + data = eval_reg_or_data(bits(0, 3, 1), 4); /* Not system reg!! */ + if(cond) { + state->SPRM[8] = data; + } + break; + } + if(bits(1, 4, 4)) { + return eval_link_instruction(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(int op, int reg, int reg2, int data) { + switch(op) { + case 1: + state->GPRM[reg] = data; + break; + case 2: /* SPECIAL CASE - SWAP! */ + state->GPRM[reg2] = state->GPRM[reg]; + state->GPRM[reg] = data; + break; + case 3: + state->GPRM[reg] += data; + break; + case 4: + state->GPRM[reg] -= data; + break; + case 5: + state->GPRM[reg] *= data; + break; + case 6: + state->GPRM[reg] /= data; + break; + case 7: + state->GPRM[reg] %= data; + break; + case 8: /* SPECIAL CASE - RND! */ + state->GPRM[reg] += data; /* TODO FIXME */ + break; + case 9: + state->GPRM[reg] &= data; + break; + case 10: + state->GPRM[reg] |= data; + break; + case 11: + state->GPRM[reg] ^= data; + break; + } +} + +/* Evaluate set instruction, combined with either Link or Compare. */ +static void eval_set_version_1(int cond) { + uint8_t op = bits(0, 4, 4); + uint8_t reg = bits(3, 4, 4); /* Erhumm.. */ + uint8_t reg2 = bits(5, 4, 4); + uint16_t data = eval_reg_or_data(bits(0, 3, 1), 4); + + if(cond) { + eval_set_op(op, reg, reg2, data); + } +} + + +/* Evaluate set instruction, combined with both Link and Compare. */ +static void eval_set_version_2(int cond) { + uint8_t op = bits(0, 4, 4); + uint8_t reg = bits(1, 4, 4); + uint8_t reg2 = bits(3, 4, 4); /* Erhumm.. */ + uint16_t data = eval_reg_or_data(bits(0, 3, 1), 2); + + if(cond) { + eval_set_op(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 int eval_command(uint8_t *bytes, link_t *return_values) { + int i, extra_bits; + int cond, res = 0; + + for(i = 0; i < 8; i++) { + cmd.bits[i] = bytes[i]; + cmd.examined[i] = 0; + } + memset(return_values, 0, sizeof(link_t)); + + switch(bits(0, 0, 3)) { /* three first bits */ + case 0: /* Special instructions */ + cond = eval_if_version_1(); + res = eval_special_instruction(cond); + if(res == -1) { + fprintf(stderr, "Unknown Instruction!\n"); + /* exit(0); */ + } + break; + case 1: /* Link/jump instructions */ + if(bits(0, 3, 1)) { + cond = eval_if_version_2(); + res = eval_jump_instruction(cond, return_values); + } else { + cond = eval_if_version_1(); + res = eval_link_instruction(cond, return_values); + } + if(res) + res = -1; + break; + case 2: /* System set instructions */ + cond = eval_if_version_2(); + res = eval_system_set(cond, return_values); + if(res) + res = -1; + break; + case 3: /* Set instructions, either Compare or Link may be used */ + cond = eval_if_version_3(); + eval_set_version_1(cond); + if(bits(1, 4, 4)) { + res = eval_link_instruction(cond, return_values); + } + if(res) + res = -1; + break; + case 4: /* Set, Compare -> Link Sub-Instruction */ + eval_set_version_2(/*True*/ 1); + cond = eval_if_version_4(); + res = eval_link_subins(cond, return_values); + if(res) + res = -1; + break; + case 5: /* Compare -> (Set and Link Sub-Instruction) */ + cond = eval_if_version_4(); + eval_set_version_2(cond); + res = eval_link_subins(cond, return_values); + if(res) + res = -1; + break; + case 6: /* Compare -> Set, allways Link Sub-Instruction */ + cond = eval_if_version_4(); + eval_set_version_2(cond); + res = eval_link_subins(/*True*/ 1, return_values); + if(res) + res = -1; + break; + } + /* Check if there are bits not yet examined */ + + extra_bits = 0; + for(i = 0; i < 8; i++) + if(cmd.bits [i] & ~cmd.examined [i]) { + extra_bits = 1; + break; + } + if(extra_bits) { + fprintf(stderr, "[WARNING, unknown bits:"); + for(i = 0; i < 8; i++) + fprintf(stderr, " %02x", cmd.bits [i] & ~cmd.examined [i]); + fprintf(stderr, "]\n"); + } + + return res; +} + +/* Evaluate a set of commands in the given register set (which is + * modified */ +int vmEval_CMD(vm_cmd_t commands[], int num_commands, + registers_t *registers, link_t *return_values) { + int i = 0; + int total = 0; + + state = registers; /* TODO FIXME */ + +#ifdef TRACE + /* DEBUG */ + if(1) { + int 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|", state->SPRM[i]); + fprintf(stderr, "\nGRPMS: "); + for(i = 0; i < 16; i++) + fprintf(stderr, "%04x|", state->GPRM[i]); + fprintf(stderr, "\n"); + } + if(1) { + int i; + for(i = 0; i < num_commands; i++) + vmPrint_CMD(i, &commands[i]); + fprintf(stderr, "--------------------------------------------\n"); + } /* end DEBUG */ +#endif + + while(i < num_commands && total < 100000) { + int line; + + if(0) vmPrint_CMD(i, &commands[i]); + line = eval_command(&commands[i].bytes[0], return_values); + + if (line < 0) { /* Link command */ + fprintf(stderr, "eval: Doing Link/Jump/Call\n"); + 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)); + 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; + } + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/decoder.h Tue Mar 12 19:45:53 2002 +0000 @@ -0,0 +1,97 @@ +/* + * 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$ + * + */ + +#ifndef DECODER_H_INCLUDED +#define DECODER_H_INCLUDED + +#include "config.h" +#include <inttypes.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]; + /* Need to have some thing to indicate normal/counter mode for every GPRM */ + /* int GPRM_mode[16]; */ +} registers_t; + +int vmEval_CMD(vm_cmd_t commands[], int num_commands, + registers_t *registers, link_t *return_values); + +void vmPrint_LINK(link_t value); + +#endif /* DECODER_H_INCLUDED */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dvd_types.h Tue Mar 12 19:45:53 2002 +0000 @@ -0,0 +1,283 @@ +/* + * 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$ + * + */ + +#ifndef DVD_H_INCLUDED +#define DVD_H_INCLUDED + +#include <inttypes.h> + +/** + * 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; + + +#endif /* DVD_H_INCLUDED */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dvdnav.c Tue Mar 12 19:45:53 2002 +0000 @@ -0,0 +1,971 @@ +/* + * 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$ + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#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> + +dvdnav_status_t dvdnav_open(dvdnav_t** dest, char *path) { + dvdnav_t *self; + + /* Create a new structure */ + (*dest) = NULL; + self = (dvdnav_t*)malloc(sizeof(dvdnav_t)); + if(!self) + return S_ERR; + memset(self, 0, (sizeof(dvdnav_t) ) ); /* Make sure self structure is clean */ + + pthread_mutex_init(&self->vm_lock, NULL); + /* Initialise the error string */ + printerr(""); + + /* Initialise the VM */ + self->vm = vm_new_vm(); + if(!self->vm) { + printerr("Error initialising the DVD VM"); + return S_ERR; + } + if(vm_reset(self->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(self->path, path, MAX_PATH_LEN); + + /* Set initial values of flags */ + self->expecting_nav_packet = 1; + self->started = 0; + + self->open_vtsN = -1; + self->open_domain = -1; + self->file = NULL; + self->cell = NULL; + self->at_soc = 1; + self->jumping = 0; + self->seeking = 0; + self->still_frame = -1; + self->cache_buffer = NULL; + self->cache_start_sector = -1; + self->cache_block_count = 0; + self->cache_valid = 0; + self->use_read_ahead = 1; + self->stop = 0; + self->highlight_changed = 0; + self->spu_clut_changed = 0; + + self->vobu_start = self->vobu_length = 0; + + /* Pre-open and close a file so that the CSS-keys are cached. */ + self->file = DVDOpenFile(vm_get_dvd_reader(self->vm), 0, DVD_READ_MENU_VOBS); + if (self->file) DVDCloseFile(self->file); + self->file = NULL; + + if(!self->started) { + /* Start the VM */ + vm_start(self->vm); + self->started = 1; + } + + (*dest) = self; + return S_OK; +} + +dvdnav_status_t dvdnav_close(dvdnav_t *self) { + if(!self) { + printerr("Passed a NULL pointer"); + return S_ERR; + } + printf("dvdnav:close:called\n"); + if (self->file) { + DVDCloseFile(self->file); + printf("dvdnav:close:file closing\n"); + self->file = NULL; + } + + /* Free the VM */ + if(self->vm) { + vm_free_vm(self->vm); + } + if (self->file) { + DVDCloseFile(self->file); + printf("dvdnav:close2:file closing\n"); + self->file = NULL; + } + pthread_mutex_destroy(&self->vm_lock); + /* Finally free the entire structure */ + free(self); + + return S_OK; +} + +dvdnav_status_t dvdnav_path(dvdnav_t *self, char** path) { + if(!self || !path || !(*path)) { + return S_ERR; + } + + /* FIXME: Is shallow copy 'right'? */ + (*path) = self->path; + + return S_OK; +} + +char* dvdnav_err_to_string(dvdnav_t *self) { + if(!self) { + /* Shold this be "passed a NULL pointer?" */ + return NULL; + } + + return self->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_check_packet(dvdnav_t *self, uint8_t *p) { + int bMpeg1=0; + uint32_t nHeaderLen; + uint32_t nPacketLen; + uint32_t nStreamID; +/* uint8_t *p_start=p; */ + + + if (p==NULL) { + printf("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)) { + printf("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) { +#ifdef HAVE_DVDREAD9 + navRead_PCI(&(self->pci), p+1); +#else + navRead_PCI(&(self->pci), p+1, nPacketLen - 1); +#endif + } + + p += nPacketLen; + + /* We should now have a DSI packet. */ + if(p[6] == 0x01) { + int num=0, current=0; + + nPacketLen = p[4] << 8 | p[5]; + p += 6; + /* dprint("NAV DSI packet\n"); */ +#ifdef HAVE_DVDREAD9 + navRead_DSI(&(self->dsi), p+1); +#else + navRead_DSI(&(self->dsi), p+1, sizeof(dsi_t)); +#endif + + self->vobu_start = self->dsi.dsi_gi.nv_pck_lbn; + self->vobu_length = self->dsi.dsi_gi.vobu_ea; + + /** + * 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. + * + * Otherwise, we set our next address past the end of this cell to + * force the code above to go to the next cell in the program. + */ + if( self->dsi.vobu_sri.next_vobu != SRI_END_OF_CELL ) { + self->next_vobu = self->dsi.dsi_gi.nv_pck_lbn + + ( self->dsi.vobu_sri.next_vobu & 0x7fffffff ); + } else { + self->next_vobu = self->vobu_start + self->vobu_length; + } + + dvdnav_get_angle_info(self, ¤t, &num); + if(num == 1) { + /* This is to switch back to angle one when we + * finish */ + dvdnav_angle_change(self, 1); + } + + if(num != 0) { + uint32_t next = self->pci.nsml_agli.nsml_agl_dsta[current-1]; + + if(next != 0) { + int dir = 0; + if(next & 0x80000000) { + dir = -1; + next = next & 0x3fffffff; + } else { + dir = 1; + } + + if(next != 0) { + self->next_vobu = self->vobu_start + dir * next; + } + } else if( self->dsi.sml_agli.data[current-1].address != 0 ) { + next = self->dsi.sml_agli.data[current-1].address; + self->vobu_length = self->dsi.sml_pbi.ilvu_ea; + + if((next & 0x80000000) && (next != 0x7fffffff)) { + self->next_vobu = self->dsi.dsi_gi.nv_pck_lbn - (next & 0x7fffffff); + } else { + self->next_vobu = self->dsi.dsi_gi.nv_pck_lbn + next; + } + } + } + } + return 1; + } + + return 0; +} + +dvdnav_status_t dvdnav_get_next_block(dvdnav_t *self, unsigned char *buf, + int *event, int *len) { + dvd_state_t *state; + int result; + if(!self || !event || !len || !buf) { + printerr("Passed a NULL pointer"); + return S_ERR; + } + pthread_mutex_lock(&self->vm_lock); + + if(!self->started) { + /* Start the VM */ + vm_start(self->vm); + self->started = 1; + } + + state = &(self->vm->state); + (*event) = DVDNAV_NOP; + (*len) = 0; + + /* Check the STOP flag */ + if(self->stop) { + (*event) = DVDNAV_STOP; + pthread_mutex_unlock(&self->vm_lock); + return S_OK; + } + + if(self->spu_clut_changed) { + (*event) = DVDNAV_SPU_CLUT_CHANGE; + printf("libdvdnav:SPU_CLUT_CHANGE\n"); + (*len) = sizeof(dvdnav_still_event_t); + memcpy(buf, &(state->pgc->palette), 16 * sizeof(uint32_t)); + self->spu_clut_changed = 0; + printf("libdvdnav:SPU_CLUT_CHANGE returning S_OK\n"); + pthread_mutex_unlock(&self->vm_lock); + return S_OK; + } + + if(self->spu_stream_changed) { + dvdnav_stream_change_event_t stream_change; + (*event) = DVDNAV_SPU_STREAM_CHANGE; + printf("libdvdnav:SPU_STREAM_CHANGE\n"); + (*len) = sizeof(dvdnav_stream_change_event_t); + stream_change.physical= vm_get_subp_active_stream( self->vm ); + memcpy(buf, &(stream_change), sizeof( dvdnav_stream_change_event_t)); + self->spu_stream_changed = 0; + printf("libdvdnav:SPU_STREAM_CHANGE stream_id=%d returning S_OK\n",stream_change.physical); + pthread_mutex_unlock(&self->vm_lock); + return S_OK; + } + + if(self->audio_stream_changed) { + dvdnav_stream_change_event_t stream_change; + (*event) = DVDNAV_AUDIO_STREAM_CHANGE; + printf("libdvdnav:AUDIO_STREAM_CHANGE\n"); + (*len) = sizeof(dvdnav_stream_change_event_t); + stream_change.physical= vm_get_audio_active_stream( self->vm ); + memcpy(buf, &(stream_change), sizeof( dvdnav_stream_change_event_t)); + self->audio_stream_changed = 0; + printf("libdvdnav:AUDIO_STREAM_CHANGE stream_id=%d returning S_OK\n",stream_change.physical); + pthread_mutex_unlock(&self->vm_lock); + return S_OK; + } + + /* Check the HIGHLIGHT flag */ + if(self->highlight_changed) { + dvdnav_highlight_event_t hevent; + + /* Fill in highlight struct with appropriate values */ + if(self->hli_state != 0) { + hevent.display = 1; + + /* Copy current button bounding box. */ + hevent.sx = self->hli_bbox[0]; + hevent.sy = self->hli_bbox[1]; + hevent.ex = self->hli_bbox[2]; + hevent.ey = self->hli_bbox[3]; + + hevent.palette = self->hli_clut; + hevent.pts = self->hli_pts; + hevent.buttonN = self->hli_buttonN; + + } else { + hevent.display = 0; + } + + (*event) = DVDNAV_HIGHLIGHT; + memcpy(buf, &(hevent), sizeof(hevent)); + (*len) = sizeof(hevent); + + self->highlight_changed = 0; + + pthread_mutex_unlock(&self->vm_lock); + return S_OK; + } + + /* Check to see if we need to change the curently opened VOB */ + if((self->open_vtsN != state->vtsN) || + (self->open_domain != state->domain)) { + dvd_read_domain_t domain; + int vtsN; + dvdnav_vts_change_event_t vts_event; + + if(self->file) { + dvdnav_read_cache_clear(self); + DVDCloseFile(self->file); + self->file = NULL; + } + + vts_event.old_vtsN = self->open_vtsN; + vts_event.old_domain = self->open_domain; + + /* Use the current DOMAIN to find whether to open menu or title VOBs */ + switch(state->domain) { + case FP_DOMAIN: + case VMGM_DOMAIN: + domain = DVD_READ_MENU_VOBS; + vtsN = 0; + break; + case VTSM_DOMAIN: + domain = DVD_READ_MENU_VOBS; + vtsN = state->vtsN; + break; + case VTS_DOMAIN: + domain = DVD_READ_TITLE_VOBS; + vtsN = state->vtsN; + break; + default: + printerr("Unknown domain when changing VTS."); + pthread_mutex_unlock(&self->vm_lock); + return S_ERR; + } + + self->open_domain = state->domain; + self->open_vtsN = state->vtsN; + dvdnav_read_cache_clear(self); + self->file = DVDOpenFile(vm_get_dvd_reader(self->vm), vtsN, domain); + vts_event.new_vtsN = self->open_vtsN; + vts_event.new_domain = self->open_domain; + + /* If couldn't open the file for some reason, moan */ + if(self->file == NULL) { + printerrf("Error opening vtsN=%i, domain=%i.", vtsN, domain); + pthread_mutex_unlock(&self->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?) */ + self->highlight_changed = 1; + self->spu_clut_changed = 1; + self->spu_stream_changed = 1; + self->audio_stream_changed = 1; + self->hli_state = 0; /* Hide */ + self->expecting_nav_packet = 1; + + pthread_mutex_unlock(&self->vm_lock); + return S_OK; + } + + /* Check the STILLFRAME flag */ + if(self->still_frame != -1) { + dvdnav_still_event_t still_event; + + still_event.length = self->still_frame; + + (*event) = DVDNAV_STILL_FRAME; + (*len) = sizeof(dvdnav_still_event_t); + memcpy(buf, &(still_event), sizeof(dvdnav_still_event_t)); + + pthread_mutex_unlock(&self->vm_lock); + return S_OK; + } + + if(self->at_soc) { + dvdnav_cell_change_event_t cell_event; + cell_playback_t *cell = &(state->pgc->cell_playback[state->cellN - 1]); + + cell_event.old_cell = self->cell; + self->vobu_start = cell->first_sector; + self->cell = cell; + cell_event.new_cell = self->cell; + + self->at_soc = 0; + + (*event) = DVDNAV_CELL_CHANGE; + (*len) = sizeof(dvdnav_cell_change_event_t); + memcpy(buf, &(cell_event), sizeof(dvdnav_cell_change_event_t)); + + pthread_mutex_unlock(&self->vm_lock); + return S_OK; + } + + if(self->expecting_nav_packet) { + dvdnav_nav_packet_event_t nav_event; + + /* Perform the jump if necessary (this is always a + * VOBU boundary). */ + + if(self->seeking) { + /* FIXME:Need to handle seeking outside current cell. */ + vobu_admap_t *admap = NULL; + + printf("Seeking to target %u ...\n", + self->seekto_block); + + /* Search through the VOBU_ADMAP for the nearest VOBU + * to the target block */ + switch(state->domain) { + case FP_DOMAIN: + case VMGM_DOMAIN: + //ifo = vm_get_vmgi(); + //ifoRead_VOBU_ADMAP(ifo); + admap = self->vm->vmgi->menu_vobu_admap; + break; + case VTSM_DOMAIN: + //ifo = vm_get_vtsi(); + //ifoRead_VOBU_ADMAP(ifo); + admap = self->vm->vtsi->menu_vobu_admap; + break; + case VTS_DOMAIN: + //ifo = vm_get_vtsi(); + //ifoRead_TITLE_VOBU_ADMAP(ifo); + admap = self->vm->vtsi->vts_vobu_admap; + break; + default: + printf("Error: Unknown domain for seeking seek.\n"); + } + + if(admap) { + uint32_t address = 0; + uint32_t vobu_start, next_vobu; + int found = 0; + + /* Search through ADMAP for best sector */ + vobu_start = 0x3fffffff; + + while((!found) && ((address<<2) < admap->last_byte)) { + next_vobu = admap->vobu_start_sectors[address]; + + /* printf("Found block %u\n", next_vobu); */ + + if(vobu_start <= self->seekto_block && + next_vobu > self->seekto_block) { + found = 1; + } else { + vobu_start = next_vobu; + } + + address ++; + } + if(found) { + self->vobu_start = vobu_start; + self->blockN = 0; + self->seeking = 0; + //self->at_soc = 1; + (*event) = DVDNAV_SEEK_DONE; + (*len) = 0; + pthread_mutex_unlock(&self->vm_lock); + return S_OK; + } else { + printf("Could not locate block\n"); + return -1; + } + } + } + if(self->jumping) { + printf("doing jumping\n"); + self->vobu_start = self->jmp_vobu_start; + self->blockN = self->jmp_blockN; + self->jumping = 0; + self->at_soc = 1; + } + + result = DVDReadBlocks(self->file, self->vobu_start + self->blockN, 1, buf); + + if(result <= 0) { + printerr("Error reading NAV packet."); + pthread_mutex_unlock(&self->vm_lock); + return S_ERR; + } + + if(dvdnav_check_packet(self, buf) == 0) { + printerr("Expected NAV packet but none found."); + pthread_mutex_unlock(&self->vm_lock); + return S_ERR; + } + + self->blockN++; + self->expecting_nav_packet = 0; + + dvdnav_pre_cache_blocks(self, self->vobu_start, self->vobu_length+1); + + /* Successfully got a NAV packet */ + nav_event.pci = &(self->pci); + nav_event.dsi = &(self->dsi); + + (*event) = DVDNAV_NAV_PACKET; + //memcpy(buf, &(nav_event), sizeof(dvdnav_nav_packet_event_t)); + //(*len) = sizeof(dvdnav_nav_packet_event_t); + (*len) = 2048; + pthread_mutex_unlock(&self->vm_lock); + return S_OK; + } + + /* If we've got here, it must just be a normal block. */ + if(!self->file) { + printerr("Attempting to read without opening file"); + pthread_mutex_unlock(&self->vm_lock); + return S_ERR; + } + + result = dvdnav_read_cache_block(self, self->vobu_start + self->blockN, 1, buf); + if(result <= 0) { + printerr("Error reading from DVD."); + pthread_mutex_unlock(&self->vm_lock); + return S_ERR; + } + self->blockN++; + (*len) = 2048; + (*event) = DVDNAV_BLOCK_OK; + + if(self->blockN > self->vobu_length) { + self->vobu_start = self->next_vobu; + self->blockN = 0; + self->expecting_nav_packet = 1; + + if(self->dsi.vobu_sri.next_vobu == SRI_END_OF_CELL) { + cell_playback_t *cell = &(state->pgc->cell_playback[state->cellN - 1]); + + if(cell->still_time != 0xff) { + vm_get_next_cell(self->vm); + } + + if(cell->still_time != 0) { + self->still_frame = cell->still_time; + } + + self->at_soc = 1; + } + } + + pthread_mutex_unlock(&self->vm_lock); + return S_OK; +} + +uint16_t dvdnav_audio_stream_to_lang(dvdnav_t *self, uint8_t stream) { + ifo_handle_t *vtsi; + dvd_state_t *state; + + if(!self) + return -1; + + pthread_mutex_lock(&self->vm_lock); + + vtsi = self->vm->vtsi; + state = &(self->vm->state); + + if((vtsi == NULL) || (state == NULL) || (state->domain != VTS_DOMAIN)) + goto __failed; + + if(stream >= vtsi->vtsi_mat->nr_of_vts_audio_streams) + goto __failed; + + if(vtsi->vtsi_mat->vts_audio_attr[stream].lang_type != 1) + goto __failed; + + pthread_mutex_unlock(&self->vm_lock); + return vtsi->vtsi_mat->vts_audio_attr[stream].lang_code; + + __failed: + pthread_mutex_unlock(&self->vm_lock); + return 0xffff; +} + +int8_t dvdnav_audio_logical_to_physical(dvdnav_t *self, uint8_t logical) { + audio_status_t *audio_status; + dvd_state_t *state; + int i = 0; + + if(!self) + return -1; + + pthread_mutex_lock(&self->vm_lock); + + state = &(self->vm->state); + + if((!state) || (!state->pgc) || (!state->pgc->audio_control)) + goto __failed; + + if(logical > 7) { + fprintf(stderr, "Invalid logical audio channel: %i\n", logical); + goto __failed; + } + + while (i < 8) { + audio_status = (audio_status_t*) &(state->pgc->audio_control[i]); + + if(!audio_status) + goto __failed; + + if(audio_status->available) + break; + + i++; + } + + if (i > 7) + goto __failed; + + if ((logical+i) > 7) + goto __failed; + + audio_status = (audio_status_t*) &(state->pgc->audio_control[logical+i]); + + if(!audio_status) + goto __failed; + + if(audio_status->available) { + /* Stream is available */ + pthread_mutex_unlock(&self->vm_lock); + return audio_status->stream_number; + } + + __failed: + pthread_mutex_unlock(&self->vm_lock); + return -1; +} + +int8_t dvdnav_audio_physical_to_logical(dvdnav_t *self, uint8_t physical) { + int8_t logical = -1; + int i = 0; + + if((!self) || (physical > 7)) + return -1; + + do { + if(dvdnav_audio_logical_to_physical(self, i) == physical) + logical = i; + + i++; + } while((i<8) && (logical == -1)); + + return logical; +} + +uint16_t dvdnav_spu_stream_to_lang(dvdnav_t *self, uint8_t stream) { + ifo_handle_t *vtsi; + dvd_state_t *state; + + if(!self) + return -1; + + pthread_mutex_lock(&self->vm_lock); + + vtsi = self->vm->vtsi; + state = &(self->vm->state); + + if((vtsi == NULL) || (state == NULL) || (state->domain != VTS_DOMAIN)) { + goto __failed; + } + + if(stream >= vtsi->vtsi_mat->nr_of_vts_subp_streams) { + goto __failed; + } + + if(vtsi->vtsi_mat->vts_subp_attr[stream].type != 1) { + goto __failed; + } + + pthread_mutex_unlock(&self->vm_lock); + return vtsi->vtsi_mat->vts_subp_attr[stream].lang_code; + + __failed: + pthread_mutex_unlock(&self->vm_lock); + return 0xffff; +} + +int8_t dvdnav_spu_logical_to_physical(dvdnav_t *self, uint8_t logical) { + spu_status_t *spu_status; + dvd_state_t *state; + ifo_handle_t *vtsi; + + if(!self) + return -1; + + pthread_mutex_lock(&self->vm_lock); + + vtsi = self->vm->vtsi; + state = &(self->vm->state); + + if((!state) || (!vtsi)) + goto __failed; + + if(logical > 31) { + fprintf(stderr, "Invalid logical spu channel: %i\n", logical); + goto __failed; + } + + spu_status = (spu_status_t*) &(state->pgc->subp_control[logical]); + + if(!spu_status) + goto __failed; + + if(spu_status->available) { + int8_t logical = -1; + video_attr_t *attr; + + attr = &(vtsi->vtsi_mat->vtsm_video_attr); + + if(!attr) + goto __failed; + + /* Stream is available */ + switch(attr->display_aspect_ratio) { + case 0: /* 4:3 */ + logical = spu_status->stream_number_4_3; + break; + case 3: /* 16:9 */ + logical = spu_status->stream_number_letterbox; + break; + } + pthread_mutex_unlock(&self->vm_lock); + return logical; + } + + __failed: + pthread_mutex_unlock(&self->vm_lock); + return -1; +} + +int8_t dvdnav_spu_physical_to_logical(dvdnav_t *self, uint8_t physical) { + int8_t logical = -1; + int i = 0; + + if(physical > 31) + return -1; + + do { + if(dvdnav_spu_logical_to_physical(self, i) == physical) + logical = i; + + i++; + } while((i<32) && (logical == -1)); + + return logical; +} + +/* Current domain (backend to dvdnav_is_domain_() funcs) */ +static int8_t _dvdnav_is_domain(dvdnav_t *self, domain_t domain) { + dvd_state_t *state; + + if((!self) || (!self->started) || (!self->vm)) + return -1; + + pthread_mutex_lock(&self->vm_lock); + state = &(self->vm->state); + pthread_mutex_unlock(&self->vm_lock); + + if(!state) + return -1; + + return (state->domain == domain) ? 1 : 0; +} + +/* First Play domain. (Menu) */ +int8_t dvdnav_is_domain_fp(dvdnav_t *self) { + return _dvdnav_is_domain(self, FP_DOMAIN); +} +/* Video management Menu domain. (Menu) */ +int8_t dvdnav_is_domain_vmgm(dvdnav_t *self) { + return _dvdnav_is_domain(self, VMGM_DOMAIN); +} +/* Video Title Menu domain (Menu) */ +int8_t dvdnav_is_domain_vtsm(dvdnav_t *self) { + return _dvdnav_is_domain(self, VTSM_DOMAIN); +} +/* Video Title domain (playing movie). */ +int8_t dvdnav_is_domain_vts(dvdnav_t *self) { + return _dvdnav_is_domain(self, VTS_DOMAIN); +} + +/* Generally delegate angle information handling to + * VM */ +dvdnav_status_t dvdnav_angle_change(dvdnav_t *self, int angle) { + int num, current; + + if(!self) { + return S_ERR; + } + + if(dvdnav_get_angle_info(self, ¤t, &num) != S_OK) { + printerr("Error getting angle info"); + return S_ERR; + } + + /* Set angle SPRM if valid */ + if((angle > 0) && (angle <= num)) { + self->vm->state.AGL_REG = angle; + } else { + printerr("Passed an invalid angle number"); + return S_ERR; + } + + return S_OK; +} + +dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *self, int* current_angle, + int *number_of_angles) { + if(!self || !self->vm) { + return S_ERR; + } + + if(!current_angle || !number_of_angles) { + printerr("Passed a NULL pointer"); + return S_ERR; + } + + vm_get_angle_info(self->vm, number_of_angles, current_angle); + + return S_OK; +} + + +/* + * $Log$ + * Revision 1.1 2002/03/12 19:45:57 richwareham + * Initial revision + * + * 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. + * + * + */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dvdnav.h Tue Mar 12 19:45:53 2002 +0000 @@ -0,0 +1,470 @@ +/* + * 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$ + * + */ + +#ifndef DVDNAV_H_INCLUDED +#define DVDNAV_H_INCLUDED + +/* 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 dvdnav data-type */ +typedef struct dvdnav_s dvdnav_t; + +/* Status */ +typedef int dvdnav_status_t; + +#define DVDNAV_STATUS_ERR 0 +#define DVDNAV_STATUS_OK 1 + +/** + * NOTE: 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(). + */ + +/*** Initialisation & housekeeping functions ***/ + +/** + * Attempts to open the DVD drive at the specifiec path and pre-cache + * any CSS-keys that your hacked libdvdread may use. + * + * Arguments: + * dest -- Pointer to a dvdnav_t pointer to fill in. + * 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. + * + * Arguments: + * self -- dvdnav_t to close. + */ +dvdnav_status_t dvdnav_close(dvdnav_t *self); + +/** + * Fills a pointer wiht a value pointing to a string describing + * the path associated with an open dvdnav_t. It assigns it NULL + * on error. + * + * Arguments: + * 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. + * + * Returns: A pointer to said string. + */ +char* dvdnav_err_to_string(dvdnav_t *self); + +/** DVD Player characteristics (FIXME: Need more entries) **/ + +/** + * Returns the region mask (bit 0 - region 1, bit 1 - R 2, etc) of the + * VM + */ +dvdnav_status_t dvdnav_get_region_mask(dvdnav_t *self, int *region); + +/** + * Sets the region mask of the VM. + * + * mask: 0x00..0xff + */ +dvdnav_status_t dvdnav_set_region_mask(dvdnav_t *self, int mask); + +/** + * Specify whether read-ahead caching should be used + * + * use_readahead: 0 - no, 1 - yes + */ +dvdnav_status_t dvdnav_set_readahead_flag(dvdnav_t *self, int use_readahead); + +/** + * Query state of readahead flag + * + * flag: pointer to int to recieve flag value + */ +dvdnav_status_t dvdnav_get_readahead_flag(dvdnav_t *self, int* flag); + +/** Getting data **/ + +/** + * Gets the next block off the DVD places it in 'buf'. If there is any + * special information, 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, + */ +dvdnav_status_t dvdnav_get_next_block(dvdnav_t *self, unsigned char *buf, + int *event, int *len); + +/** Navigation **/ + +/** + * Returns the number of titles on the disk in titles. + */ +dvdnav_status_t dvdnav_get_number_of_titles(dvdnav_t *self, int *titles); + +/** + * If we are currently in a still-frame, skip it. + */ +dvdnav_status_t dvdnav_still_skip(dvdnav_t *self); + +/** + * Plays a specified title of the DVD + * + * Arguments: + * title -- 1..99 + */ +dvdnav_status_t dvdnav_title_play(dvdnav_t *self, int title); + +/** + * Plays the specifiec title, starting from the specified + * part (chapter) + * + * Arguments: + * title -- 1..99 + * part -- 1..999 + */ +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. + * + * Arguments: + * title -- 1..99 + * part -- 1..999 + * parts_to_play -- 1..999 + */ +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 + * + * Arguments: + * title -- 1..99 + * time: (timecode) 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). + */ +dvdnav_status_t dvdnav_stop(dvdnav_t *self); + +/** + * Stop playing current title and play the "GoUp"-program chain + */ +dvdnav_status_t dvdnav_go_up(dvdnav_t *self); + +/** SEARCHING **/ + +/** + * Stop playing the current title and start playback of the title + * from the specified timecode. + * + * Arguments: + * time -- timecode. + */ +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. + * + * Arguments: + * offset -- sector offset. + * 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). + * + * Arguments: + * part: 1..999 + */ +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) + */ +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. + */ +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) + */ +dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *self); + +/** + * Stop playing the current title and jump to the specified menu. + * + * Arguments: + * menu -- Which menu to call + */ +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. + */ +dvdnav_status_t dvdnav_current_title_info(dvdnav_t *self, int *title, + int *part); + +/** + * Return 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. + */ +dvdnav_status_t dvdnav_get_position(dvdnav_t *self, unsigned int* pos, + unsigned int *len); + +/** Highlights **/ + +/** + * Set the value pointed to to the currently highlighted button + * number (1..36) or 0 if no button is highlighed. + * + * Arguments: + * button -- Pointer to the value to fill in. + */ +dvdnav_status_t dvdnav_get_current_highlight(dvdnav_t *self, int* button); + +/** + * Move button highlight around (e.g. with arrow keys) + */ +dvdnav_status_t dvdnav_upper_button_select(dvdnav_t *self); +dvdnav_status_t dvdnav_lower_button_select(dvdnav_t *self); +dvdnav_status_t dvdnav_right_button_select(dvdnav_t *self); +dvdnav_status_t dvdnav_left_button_select(dvdnav_t *self); + +/** + * Activate (press) highlighted button + */ +dvdnav_status_t dvdnav_button_activate(dvdnav_t *self); + +/** + * Highlight a specific button button + * + * button -- 1..39. + */ +dvdnav_status_t dvdnav_button_select(dvdnav_t *self, int button); + +/** + * Activate (press) specified button. + * + * Arguments: + * button: 1..36 + */ +dvdnav_status_t dvdnav_button_select_and_activate(dvdnav_t *self, int button); + +/** + * Select button at specified (image) co-ordinates. + * + * Arguments: + * x,y: Image co-ordinates + */ +dvdnav_status_t dvdnav_mouse_select(dvdnav_t *self, int x, int y); + +/** + * Activate (press) button at specified co-ordinates. + */ +dvdnav_status_t dvdnav_mouse_activate(dvdnav_t *self, int x, int y); + +/** i18n **/ + +/** + * Set specified menu language. + * + * Arguments: + * code: 1 char ISO 639 Language code + */ +dvdnav_status_t dvdnav_menu_languge_select(dvdnav_t *self, + char *code); + +/** SPU/Audio streams **/ + +/** + * Set a specific PHYSICAL MPEG stream. + * + * Arguments: + * audio: 0..7 + */ +dvdnav_status_t dvdnav_physical_audio_stream_change(dvdnav_t *self, + int audio); + +/** + * Set a specific logical audio stream. + * + * Arguments: + * audio: 0..7 + */ +dvdnav_status_t dvdnav_logical_audio_stream_change(dvdnav_t *self, + int audio); + +/** + * Set the int pointed to to the current PHYSICAL audio + * stream. + * + * Arguments: + * audio: Pointer to 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. + * + * Arguments: + * audio: Pointer to 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. + * + * Arguments: + * stream: 0..31 or 63 (dummy) + * display: 0..1 + */ +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. + * + * Arguments: + * stream: 0..31 or 63 (dummy) + * display: 0..1 + */ +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. + * + * Arguments: + * stream, display: Pointers to value + */ +dvdnav_status_t dvdnav_get_physical_spu_stream(dvdnav_t *self, + int* stream, int* disply); + +/** + * Set the ints pointed to to the current LOGICAL SPU + * stream & display flag. + * + * Arguments: + * stream, display: Pointers to value + */ +dvdnav_status_t dvdnav_get_logical_spu_stream(dvdnav_t *self, + int* stream, int* disply); + +/** ANGLES **/ + +/** + * Sets the current angle + * + * Arguments: + * angle: 1..9 + */ +dvdnav_status_t dvdnav_angle_change(dvdnav_t *self, int angle); + +/** + * Returns the current angle and number of angles present + */ +dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *self, int* current_angle, + int *number_of_angles); + +/** + * Converts a *logical* subpicture stream id into country code + * (returns 0xffff if no such stream). + */ +uint16_t dvdnav_audio_stream_to_lang(dvdnav_t *self, uint8_t stream); +uint16_t dvdnav_spu_stream_to_lang(dvdnav_t *self, uint8_t stream); + +/** + * Converts a _logical_ audio/spu stream number (as in the SPRMs) to the physical + * stream number encoded on the DVD. Returns -1 if this audio/spu stream is not + * present. + */ +int8_t dvdnav_audio_logical_to_physical(dvdnav_t *self, uint8_t logical); +int8_t dvdnav_spu_logical_to_physical(dvdnav_t *self, uint8_t logical); + +/** + * The exact opposite of the above two calls + */ +int8_t dvdnav_audio_physical_to_logical(dvdnav_t *self, uint8_t physical); +int8_t dvdnav_spu_physical_to_logical(dvdnav_t *self, uint8_t physical); + +/* Following functions returns: + * -1 on failure, + * 1 if condition is true, + * 0 if condition is false + */ +/*** Current VM domain state ***/ +/* First Play domain. (Menu) */ +int8_t dvdnav_is_domain_fp(dvdnav_t *self); +/* Video management Menu domain. (Menu) */ +int8_t dvdnav_is_domain_vmgm(dvdnav_t *self); +/* Video Title Menu domain (Menu) */ +int8_t dvdnav_is_domain_vtsm(dvdnav_t *self); +/* Video Title domain (playing movie). */ +int8_t dvdnav_is_domain_vts(dvdnav_t *self); + +#endif /* DVDNAV_H_INCLUDED */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dvdnav_events.h Tue Mar 12 19:45:53 2002 +0000 @@ -0,0 +1,90 @@ +/* + * 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$ + * + */ + +#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> + +/* 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 */ + +/*** EVENT TYPES ***/ + +typedef struct { + int length; /* The length (in seconds) the still frame + * should be displayed for, or 0xff if + * indefinate. */ +} dvdnav_still_event_t; + +typedef struct { + int physical; /* The physical (MPEG) stream number. */ + int logical; /* The logical (DVD) stream number. */ +} dvdnav_stream_change_event_t; + +typedef struct { + int old_vtsN; /* The old VTS number */ + dvd_read_domain_t old_domain; /* The old domain */ + int new_vtsN; /* The new one */ + dvd_read_domain_t new_domain; +} dvdnav_vts_change_event_t; + +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; + +typedef struct { + pci_t *pci; + dsi_t *dsi; +} dvdnav_nav_packet_event_t; + +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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dvdnav_internal.h Tue Mar 12 19:45:53 2002 +0000 @@ -0,0 +1,174 @@ +/* + * 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$ + * + */ + +#ifndef DVDNAV_INTERNAL_H_INCLUDED +#define DVDNAV_INTERNAL_H_INCLUDED + +#include "dvdnav.h" +#include "vm.h" + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <limits.h> +#include <string.h> +#include <pthread.h> + +#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 + +/* + * 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 + +/* 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 */ + uint32_t vobu_start; + uint32_t vobu_length; + uint32_t blockN; + uint32_t next_vobu; + cell_playback_t *cell; + uint32_t jmp_blockN; + uint32_t jmp_vobu_start; + uint32_t seekto_block; + + /* NAV data */ + pci_t pci; + dsi_t dsi; + + /* Flags */ + int expecting_nav_packet; + int at_soc; /* Are we at the start of a cell? */ + int still_frame; /* >=0 send still frame event with len still_frame + * -1 don't send event. */ + int jumping; /* non-zero if we are in the process of jumping */ + int seeking; /* non-zero if we are in the process of seeking */ + int stop; /* Are we stopped? (note not paused, actually stopped) */ + int highlight_changed; /* The highlight changed */ + int spu_clut_changed; /* The SPU CLUT changed */ + int spu_stream_changed; /* The SPU STREAM changed */ + int audio_stream_changed; /* The AUDIO STREAM 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 */ + uint16_t hli_bbox[4]; /* Highlight bounding box */ + uint32_t hli_clut; /* Highlight palette */ + uint32_t hli_pts; /* Highlight PTS for matching with SPU packet. */ + uint32_t hli_buttonN; /* Button number for SPU decoder. */ + /* Read-ahead cache. */ + 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; + + /* Errors */ + char err_str[MAX_ERR_LEN]; +}; + +/* Common things we do everytime we do a jump */ +void dvdnav_do_post_jump(dvdnav_t *self); + +/** USEFUL MACROS **/ + +#define printerrf(format, args...) snprintf(self->err_str, MAX_ERR_LEN, format, ## args); +#define printerr(str) strncpy(self->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 */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/highlight.c Tue Mar 12 19:45:53 2002 +0000 @@ -0,0 +1,277 @@ +/* + * 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$ + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* +#define BUTTON_TESTING +*/ + +#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 *self, int* button) { + if(!self) + return S_ERR; + + /* Simply return the appropriate value based on the SPRM */ + (*button) = (self->vm->state.HL_BTNN_REG) >> 10; + + return S_OK; +} + +btni_t *__get_current_button(dvdnav_t *self) { + int button = 0; + + if(dvdnav_get_current_highlight(self, &button) != S_OK) { + printerrf("Unable to get information on current highlight."); + return NULL; + } +#ifdef BUTTON_TESTING + navPrint_PCI(&(self->pci)); +#endif + + return &(self->pci.hli.btnit[button-1]); +} + +dvdnav_status_t dvdnav_upper_button_select(dvdnav_t *self) { + btni_t *button_ptr; + + if(!self) + return S_ERR; + + if((button_ptr = __get_current_button(self)) == NULL) { + return S_ERR; + } + + dvdnav_button_select(self, button_ptr->up); + + return S_OK; +} + +dvdnav_status_t dvdnav_lower_button_select(dvdnav_t *self) { + btni_t *button_ptr; + + if(!self) + return S_ERR; + + if((button_ptr = __get_current_button(self)) == NULL) { + return S_ERR; + } + + dvdnav_button_select(self, button_ptr->down); + + return S_OK; +} + +dvdnav_status_t dvdnav_right_button_select(dvdnav_t *self) { + btni_t *button_ptr; + + if(!self) + return S_ERR; + + if((button_ptr = __get_current_button(self)) == NULL) { + printerr("Error fetching information on current button."); + return S_ERR; + } + + dvdnav_button_select(self, button_ptr->right); + + return S_OK; +} + +dvdnav_status_t dvdnav_left_button_select(dvdnav_t *self) { + btni_t *button_ptr; + + if(!self) + return S_ERR; + + if((button_ptr = __get_current_button(self)) == NULL) { + return S_ERR; + } + + dvdnav_button_select(self, button_ptr->left); + + return S_OK; +} + +dvdnav_status_t dvdnav_button_activate(dvdnav_t *self) { + int button; + btni_t *button_ptr; + + if(!self) + return S_ERR; + pthread_mutex_lock(&self->vm_lock); + + /* Precisely the same as selecting a button except we want + * a different palette */ + if(dvdnav_get_current_highlight(self, &button) != S_OK) { + pthread_mutex_unlock(&self->vm_lock); + return S_ERR; + } + if(dvdnav_button_select(self, button) != S_OK) { + pthread_mutex_unlock(&self->vm_lock); + return S_ERR; + } + + /* Now get the current button's info */ + if((button_ptr = __get_current_button(self)) == NULL) { + printerr("Error fetching information on current button."); + pthread_mutex_unlock(&self->vm_lock); + return S_ERR; + } + + /* And set the palette */ + if(button_ptr->btn_coln != 0) { + self->hli_clut = + self->pci.hli.btn_colit.btn_coli[button_ptr->btn_coln-1][1]; + } else { + self->hli_clut = 0; + } + + /* Finally, make the VM execute the appropriate code and + * scedule a jump */ + + if(vm_eval_cmd(self->vm, &(button_ptr->cmd)) == 1) { + /* Cammand caused a jump */ + dvdnav_do_post_jump(self); + } + + pthread_mutex_unlock(&self->vm_lock); + return S_OK; +} + +dvdnav_status_t dvdnav_button_select(dvdnav_t *self, int button) { + btni_t *button_ptr; + + if(!self) { + printerrf("Unable to select button number %i as self state bad", + button); + return S_ERR; + } + + printf("Button select %i\n", button); + + /* Set the highlight SPRM if the passed button was valid*/ + if((button <= 0) || (button > self->pci.hli.hl_gi.btn_ns)) { + printerrf("Unable to select button number %i as it doesn't exist", + button); + return S_ERR; + } + self->vm->state.HL_BTNN_REG = (button << 10); + + /* Now get the current button's info */ + if((button_ptr = __get_current_button(self)) == NULL) { + printerr("Error fetching information on current button."); + return S_ERR; + } + + self->hli_bbox[0] = button_ptr->x_start; + self->hli_bbox[1] = button_ptr->y_start; + self->hli_bbox[2] = button_ptr->x_end; + self->hli_bbox[3] = button_ptr->y_end; + self->hli_state = 1; /* Selected */ + + if(button_ptr->btn_coln != 0) { + self->hli_clut = + self->pci.hli.btn_colit.btn_coli[button_ptr->btn_coln-1][0]; + } else { + self->hli_clut = 0; + } + self->hli_pts = self->pci.hli.hl_gi.hli_s_ptm; + self->hli_buttonN = button; + self->highlight_changed = 1; +#ifdef BUTTON_TESTING + printf("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_select_and_activate(dvdnav_t *self, + int button) { + /* A trivial function */ + if(dvdnav_button_select(self, button) != S_ERR) { + return dvdnav_button_activate(self); + } + + /* Should never get here without an error */ + return S_ERR; +} + +dvdnav_status_t dvdnav_mouse_select(dvdnav_t *self, int x, int y) { + int button, cur_button; + + /* FIXME: At the moment, the case of no button matchin (x,y) is + * silently ignored, is this OK? */ + if(!self) + return S_ERR; + + if(dvdnav_get_current_highlight(self, &cur_button) != S_OK) { + return S_ERR; + } + + /* Loop through each button */ + for(button=1; button <= self->pci.hli.hl_gi.btn_ns; button++) { + btni_t *button_ptr = NULL; + + button_ptr = &(self->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)) { + /* As an efficiency measure, only re-select the button + * if it is different to the previously selected one. */ + if(button != cur_button) { + dvdnav_button_select(self, button); + } + } + } + + return S_OK; +} + +dvdnav_status_t dvdnav_mouse_activate(dvdnav_t *self, int x, int y) { + /* A trivial function */ + if(dvdnav_mouse_select(self, x,y) != S_ERR) { + return dvdnav_button_activate(self); + } + + /* Should never get here without an error */ + return S_ERR; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/navigation.c Tue Mar 12 19:45:53 2002 +0000 @@ -0,0 +1,170 @@ +/* + * 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$ + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <dvdnav.h> +#include "dvdnav_internal.h" + +#include "vm.h" + +/* Navigation API calls */ + +/* Common things we do everytime we do a jump */ +void dvdnav_do_post_jump(dvdnav_t *self) { + dvd_state_t *state = &(self->vm->state); + cell_playback_t *cell = &(state->pgc->cell_playback[state->cellN - 1]); + + self->jmp_blockN = 0; /* FIXME: Should this be different? */ + self->jmp_vobu_start = cell->first_sector; + self->jumping = 1; + self->still_frame = -1; +} + +dvdnav_status_t dvdnav_still_skip(dvdnav_t *self) { + if(!self) + return S_ERR; + + self->still_frame = -1; + + return S_OK; +} + +dvdnav_status_t dvdnav_get_number_of_titles(dvdnav_t *self, int *titles) { + if(!self) + return S_ERR; + + if(!titles) { + printerr("Passed a NULL pointer"); + return S_ERR; + } + + (*titles) = vm_get_vmgi(self->vm)->tt_srpt->nr_of_srpts; + + return S_OK; +} + +dvdnav_status_t dvdnav_title_play(dvdnav_t *self, int title) { + int num_titles; + + if(!self) { + return S_ERR; + } + + /* Check number of titles */ + dvdnav_get_number_of_titles(self, &num_titles); + if((title > num_titles) || (title <= 0)) { + printerrf("Invalid title passed (%i, maximum %i)", title, + num_titles); + return S_ERR; + } + + vm_start_title(self->vm, title); + + /* self->expecting_nav_packet = 1; */ + + dvdnav_do_post_jump(self); + + return S_OK; +} + +dvdnav_status_t dvdnav_part_play(dvdnav_t *self, int title, int part) { + int num_titles, num_progs; + + if(!self) { + return S_ERR; + } + + /* Check number of titles */ + dvdnav_get_number_of_titles(self, &num_titles); + if((title > num_titles) || (title <= 0)) { + printerrf("Invalid title passed (%i, maximum %i)", title, + num_titles); + return S_ERR; + } + + vm_start_title(self->vm, title); + + + /* Check number of parts */ + num_progs = self->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(self->vm, part); + + /* self->expecting_nav_packet = 1; */ + + dvdnav_do_post_jump(self); + + return S_OK; +} + +dvdnav_status_t dvdnav_part_play_auto_stop(dvdnav_t *self, int title, + int part, int parts_to_play) { + /* Perform jump as per usual */ + + return dvdnav_part_play(self, title, part); + + /* FIXME: Impement auto-stop */ + + /* return S_OK;*/ +} + +dvdnav_status_t dvdnav_time_play(dvdnav_t *self, int title, + unsigned long int time) { + /* FIXME: Implement */ + + return S_OK; +} + +dvdnav_status_t dvdnav_stop(dvdnav_t *self) { + if(!self) + return S_ERR; + + /* Set the STOP flag */ + + self->stop = 1; + + return S_OK; +} + +dvdnav_status_t dvdnav_go_up(dvdnav_t *self) { + if(!self) + return S_ERR; + + /* A nice easy function... delegate to the VM */ + vm_go_up(self->vm); + + dvdnav_do_post_jump(self); + + return S_OK; +} + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/read_cache.c Tue Mar 12 19:45:53 2002 +0000 @@ -0,0 +1,91 @@ +/* + * 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$ + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "read_cache.h" + +/* This function MUST be called whenever self->file changes. */ +void dvdnav_read_cache_clear(dvdnav_t *self) { + if(!self) + return; + + self->cache_start_sector = -1; + self->cache_valid = 0; +} +/* This function is called just after reading the NAV packet. */ +void dvdnav_pre_cache_blocks(dvdnav_t *self, int sector, size_t block_count) { + int result; + + if(!self) + return; + + if(!self->use_read_ahead) { + self->cache_valid = 0; + self->cache_start_sector = -1; + return; + } + + if (self->cache_buffer) { + if( block_count > self->cache_malloc_size) { + self->cache_buffer = realloc(self->cache_buffer, block_count * DVD_VIDEO_LB_LEN); + self->cache_malloc_size = block_count; + } + } else { + self->cache_buffer = malloc(block_count * DVD_VIDEO_LB_LEN); + self->cache_malloc_size = block_count; + } + self->cache_start_sector = sector; + self->cache_block_count = block_count; + result = DVDReadBlocks( self->file, sector, block_count, self->cache_buffer); + self->cache_valid = 1; +} + +/* This function will do the cache read once implemented */ +int dvdnav_read_cache_block( dvdnav_t *self, int sector, size_t block_count, uint8_t *buf) { + int result; + + if(!self) + return 0; + + if(self->cache_valid && self->use_read_ahead) { + if (self->cache_start_sector != -1 ) { + if ((sector >= self->cache_start_sector) && + (sector < self->cache_start_sector + self->cache_block_count)) { + memcpy(buf, self->cache_buffer + ((off_t)((off_t)sector - (off_t)self->cache_start_sector) * DVD_VIDEO_LB_LEN), DVD_VIDEO_LB_LEN); + return DVD_VIDEO_LB_LEN; + } + } + } else { + result = DVDReadBlocks( self->file, sector, block_count, buf); + return result; + } + + printf("DVD read cache miss! sector=%d, start=%d\n", sector, self->cache_start_sector); + result = DVDReadBlocks( self->file, sector, block_count, buf); + return result; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/read_cache.h Tue Mar 12 19:45:53 2002 +0000 @@ -0,0 +1,36 @@ +/* + * 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$ + * + */ + +#ifndef __DVDNAV_READ_CACHE_H +#define __DVDNAV_READ_CACHE_H + +#include "dvdnav_internal.h" + +/* This function MUST be called whenever self->file changes. */ +void dvdnav_read_cache_clear(dvdnav_t *self); +/* This function is called just after reading the NAV packet. */ +void dvdnav_pre_cache_blocks(dvdnav_t *self, int sector, size_t block_count); +/* This function will do the cache read */ +int dvdnav_read_cache_block(dvdnav_t *self, int sector, size_t block_count, uint8_t *buf); + +#endif /* __DVDNAV_READ_CACHE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/searching.c Tue Mar 12 19:45:53 2002 +0000 @@ -0,0 +1,320 @@ +/* + * 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$ + * + */ + +#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 *self, + unsigned long int time) { +/* Time search the current PGC based on the xxx table */ + return S_OK; +} + +dvdnav_status_t dvdnav_sector_search(dvdnav_t *self, + 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((!self) || (!self->vm) ) + return -1; + + state = &(self->vm->state); + if((!state) || (!state->pgc) ) + return -1; + + if(offset == 0) + return -1; + + if(self->still_frame != -1) + /* Cannot do seeking in a still frame. */ + return -1; + + pthread_mutex_lock(&self->vm_lock); + result = dvdnav_get_position(self, &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(&self->vm_lock); + return -1; + } + + switch(origin) { + case SEEK_SET: + if(offset > length) { + pthread_mutex_unlock(&self->vm_lock); + return -1; + } + target = offset; + break; + case SEEK_CUR: + if(target + offset > length) { + pthread_mutex_unlock(&self->vm_lock); + return -1; + } + target += offset; + break; + case SEEK_END: + if(length - offset < 0) { + pthread_mutex_unlock(&self->vm_lock); + return -1; + } + target = length - offset; + default: + /* Error occured */ + pthread_mutex_unlock(&self->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) { + printf("Seeking to cell %i from choice of %i to %i\n", + fnd_cell_nr, first_cell_nr, last_cell_nr); + self->seekto_block = target; + self->seeking = 1; + /* + * Clut does not actually change, + * but as the decoders have been closed then opened, + * A new clut has to be sent. + */ + self->spu_clut_changed = 1; + //ogle_do_post_jump(ogle); + fprintf(stderr,"FIXME: After cellN=%u blockN=%u\n" , + state->cellN, + state->blockN); + + pthread_mutex_unlock(&self->vm_lock); + return target; + } else { + fprintf(stderr, "Error when seeking, asked to seek outside program\n"); + } + + + + printf("FIXME: Implement seeking to location %u\n", target); + +// self->seekto_block=target; +// self->seeking = 1; + + pthread_mutex_unlock(&self->vm_lock); + return -1; +} + +dvdnav_status_t dvdnav_part_search(dvdnav_t *self, int part) { + return S_OK; +} + +dvdnav_status_t dvdnav_prev_pg_search(dvdnav_t *self) { + dvd_state_t *state; + state = &(self->vm->state); + /* Make sure this is not the first chapter */ + + if(state->pgN <= 1 ) { + printf("dvdnav: at first chapter. prev chapter failed.\n"); + return S_ERR; + } + printf("dvdnav: previous chapter\n"); + vm_jump_prog(self->vm, state->pgN - 1); + dvdnav_do_post_jump(self); + printf("dvdnav: previous chapter done\n"); + + return S_OK; +} + +dvdnav_status_t dvdnav_top_pg_search(dvdnav_t *self) { + + printf("dvdnav: top chapter. NOP.\n"); + + return S_OK; +} + +dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *self) { + dvd_state_t *state; + state = &(self->vm->state); + /* Make sure this is not the last chapter */ + if(state->pgN >= state->pgc->nr_of_programs) { + printf("dvdnav: at last chapter. next chapter failed.\n"); + return S_ERR; + } + printf("dvdnav: next chapter\n"); + vm_jump_prog(self->vm, state->pgN + 1); + dvdnav_do_post_jump(self); + printf("dvdnav: next chapter done\n"); + + return S_OK; +} + +dvdnav_status_t dvdnav_menu_call(dvdnav_t *self, DVDMenuID_t menu) { + dvd_state_t *state; + + pthread_mutex_lock(&self->vm_lock); + state = &(self->vm->state); + vm_menu_call(self->vm, menu, 0); + dvdnav_do_post_jump(self); + pthread_mutex_unlock(&self->vm_lock); + return S_OK; +} + +dvdnav_status_t dvdnav_current_title_info(dvdnav_t *self, int *tt, int *pr) { +int vts_ttn = 0; + int vts, i; + domain_t domain; + tt_srpt_t* srpt; + + if(!self) + return S_ERR; + + if(!tt || !pr) { + printerr("Passed a NULL pointer"); + } + + if(tt) + *tt = -1; + if(*pr) + *pr = -1; + + domain = self->vm->state.domain; + if((domain == FP_DOMAIN) || (domain == VMGM_DOMAIN)) { + /* Not in a title */ + return S_OK; + } + + vts_ttn = self->vm->state.VTS_TTN_REG; + vts = self->vm->state.vtsN; + + if(pr) { + *pr = self->vm->state.pgN; + } + + /* Search TT_SRPT for title */ + if(!(vm_get_vmgi(self->vm))) { + printerr("Oh poo, no SRPT"); + return S_ERR; + } + + srpt = vm_get_vmgi(self->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 *self, char **title_str) { + if(!self) + 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 *self, 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((!self) || (!self->vm) ) + return 0; + + state = &(self->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 = self->vobu_start + self->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; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/settings.c Tue Mar 12 19:45:53 2002 +0000 @@ -0,0 +1,89 @@ +/* + * 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$ + * + */ + +#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 *self, int *region) { + if(!self) + return S_ERR; + + if(!region) { + printerr("Passed a NULL pointer"); + return S_ERR; + } + + if(!self->vm) { + printerr("VM not yet initialised"); + return S_ERR; + } + + (*region) = self->vm->state.registers.SPRM[20]; + + return S_OK; +} + +dvdnav_status_t dvdnav_set_region_mask(dvdnav_t *self, int mask) { + if(!self) + return S_ERR; + + if(!self->vm) { + printerr("VM not yet initialised"); + return S_ERR; + } + + self->vm->state.registers.SPRM[20] = (mask & 0xff); + + return S_OK; +} + +dvdnav_status_t dvdnav_set_readahead_flag(dvdnav_t *self, int use_readahead) { + if(!self) + return S_ERR; + + self->use_read_ahead = use_readahead; + + return S_OK; +} + +dvdnav_status_t dvdnav_get_readahead_flag(dvdnav_t *self, int* flag) { + if(!self) + return S_ERR; + + if(!flag) { + printerr("Passed a NULL pointer"); + return S_ERR; + } + + (*flag) = self->use_read_ahead; + return S_OK; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm.c Tue Mar 12 19:45:53 2002 +0000 @@ -0,0 +1,1547 @@ +/* + * 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$ + * + */ + +#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" + +/* Local prototypes */ + +static void saveRSMinfo(vm_t *self,int cellN, int blockN); +static int set_PGN(vm_t *self); +static link_t play_PGC(vm_t *self); +static link_t play_PG(vm_t *self); +static link_t play_Cell(vm_t *self); +static link_t play_Cell_post(vm_t *self); +static link_t play_PGC_post(vm_t *self); +static link_t process_command(vm_t *self,link_t link_values); + +static void ifoOpenNewVTSI(vm_t *self,dvd_reader_t *dvd, int vtsN); +static pgcit_t* get_PGCIT(vm_t *self); +static int get_video_aspect(vm_t *self); + +/* Can only be called when in VTS_DOMAIN */ +static int get_TT(vm_t *self,int tt); +static int get_VTS_TT(vm_t *self,int vtsN, int vts_ttn); +static int get_VTS_PTT(vm_t *self,int vtsN, int vts_ttn, int part); + +static int get_MENU(vm_t *self,int menu); /* VTSM & VMGM */ +static int get_FP_PGC(vm_t *self); /* FP */ + +/* Called in any domain */ +static int get_ID(vm_t *self,int id); +static int get_PGC(vm_t *self,int pgcN); +static int get_PGCN(vm_t *self); + +/* 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 *self) { + switch((self->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", + (self->state).vtsN, + (self->state).pgN, + (self->state).cellN, + (self->state).blockN, + (self->state).VTS_TTN_REG, + (self->state).TTN_REG, + (self->state).TT_PGCN_REG); +} + +void vm_stop(vm_t *self) { + if(!self) + return; + + if(self->vmgi) { + ifoClose(self->vmgi); + } + + if(self->vtsi) { + ifoClose(self->vtsi); + } + + if(self->dvd) { + DVDClose(self->dvd); + } +} + +void vm_free_vm(vm_t *self) { + if(self) { + vm_stop(self); + free(self); + } +} + +/* IFO Access */ + +ifo_handle_t *vm_get_vmgi(vm_t *self) { + if(!self) + return NULL; + + return self->vmgi; +} + +ifo_handle_t *vm_get_vtsi(vm_t *self) { + if(!self) + return NULL; + + return self->vtsi; +} + +/* Reader Access */ + +dvd_reader_t *vm_get_dvd_reader(vm_t *self) { + if(!self) + return NULL; + + return self->dvd; +} + +int vm_reset(vm_t *self, char *dvdroot) /* , register_t regs) */ { + /* Setup State */ + memset((self->state).registers.SPRM, 0, sizeof(uint16_t)*24); + memset((self->state).registers.GPRM, 0, sizeof((self->state).registers.GPRM)); + (self->state).registers.SPRM[0] = ('e'<<8)|'n'; /* Player Menu Languange code */ + (self->state).AST_REG = 15; /* 15 why? */ + (self->state).SPST_REG = 62; /* 62 why? */ + (self->state).AGL_REG = 1; + (self->state).TTN_REG = 1; + (self->state).VTS_TTN_REG = 1; + /* (self->state).TT_PGCN_REG = 0 */ + (self->state).PTTN_REG = 1; + (self->state).HL_BTNN_REG = 1 << 10; + + (self->state).PTL_REG = 15; /* Parental Level */ + (self->state).registers.SPRM[12] = ('U'<<8)|'S'; /* Parental Management Country Code */ + (self->state).registers.SPRM[16] = ('e'<<8)|'n'; /* Initial Language Code for Audio */ + (self->state).registers.SPRM[18] = ('e'<<8)|'n'; /* Initial Language Code for Spu */ + /* Player Regional Code Mask. + * bit0 = Region 1 + * bit1 = Region 2 + */ + (self->state).registers.SPRM[20] = 0x1; /* Player Regional Code Mask. Region free! */ + (self->state).registers.SPRM[14] = 0x100; /* Try Pan&Scan */ + + (self->state).pgN = 0; + (self->state).cellN = 0; + + (self->state).domain = FP_DOMAIN; + (self->state).rsm_vtsN = 0; + (self->state).rsm_cellN = 0; + (self->state).rsm_blockN = 0; + + (self->state).vtsN = -1; + + self->dvd = DVDOpen(dvdroot); + if(!self->dvd) { + fprintf(stderr, "vm: faild to open/read the DVD\n"); + return -1; + } + + self->vmgi = ifoOpenVMGI(self->dvd); + if(!self->vmgi) { + fprintf(stderr, "vm: faild to read VIDEO_TS.IFO\n"); + return -1; + } + if(!ifoRead_FP_PGC(self->vmgi)) { + fprintf(stderr, "vm: ifoRead_FP_PGC failed\n"); + return -1; + } + if(!ifoRead_TT_SRPT(self->vmgi)) { + fprintf(stderr, "vm: ifoRead_TT_SRPT failed\n"); + return -1; + } + if(!ifoRead_PGCI_UT(self->vmgi)) { + fprintf(stderr, "vm: ifoRead_PGCI_UT failed\n"); + return -1; + } + if(!ifoRead_PTL_MAIT(self->vmgi)) { + fprintf(stderr, "vm: ifoRead_PTL_MAIT failed\n"); + ; /* return -1; Not really used for now.. */ + } + if(!ifoRead_VTS_ATRT(self->vmgi)) { + fprintf(stderr, "vm: ifoRead_VTS_ATRT failed\n"); + ; /* return -1; Not really used for now.. */ + } + if(!ifoRead_VOBU_ADMAP(self->vmgi)) { + fprintf(stderr, "vm: ifoRead_VOBU_ADMAP vgmi failed\n"); + ; /* return -1; Not really used for now.. */ + } + /* ifoRead_TXTDT_MGI(vmgi); Not implemented yet */ + + return 0; +} + +/* FIXME TODO XXX $$$ Handle error condition too... */ +int vm_start(vm_t *self) +{ + link_t link_values; + + /* Set pgc to FP pgc */ + get_FP_PGC(self); + link_values = play_PGC(self); + link_values = process_command(self,link_values); + assert(link_values.command == PlayThis); + (self->state).blockN = link_values.data1; + + return 0; /* ?? */ +} + +int vm_start_title(vm_t *self, int tt) { + link_t link_values; + + get_TT(self, tt); + link_values = play_PGC(self); + link_values = process_command(self, link_values); + assert(link_values.command == PlayThis); + (self->state).blockN = link_values.data1; + + return 0; /* ?? */ +} + +int vm_jump_prog(vm_t *self, int pr) { + link_t link_values; + + (self->state).pgN = pr; /* ?? */ + + get_PGC(self, get_PGCN(self)); + link_values = play_PG(self); + link_values = process_command(self, link_values); + assert(link_values.command == PlayThis); + (self->state).blockN = link_values.data1; + + return 0; /* ?? */ +} + +int vm_eval_cmd(vm_t *self, vm_cmd_t *cmd) +{ + link_t link_values; + + if(vmEval_CMD(cmd, 1, &(self->state).registers, &link_values)) { + link_values = process_command(self, link_values); + assert(link_values.command == PlayThis); + (self->state).blockN = link_values.data1; + return 1; /* Something changed, Jump */ + } else { + return 0; /* It updated some state thats all... */ + } +} + +int vm_get_next_cell(vm_t *self) +{ + link_t link_values; + link_values = play_Cell_post(self); + link_values = process_command(self,link_values); + assert(link_values.command == PlayThis); + (self->state).blockN = link_values.data1; + + return 0; /* ?? */ +} + +int vm_top_pg(vm_t *self) +{ + link_t link_values; + link_values = play_PG(self); + link_values = process_command(self,link_values); + assert(link_values.command == PlayThis); + (self->state).blockN = link_values.data1; + + return 1; /* Jump */ +} + +int vm_go_up(vm_t *self) +{ + link_t link_values; + + if(get_PGC(self, (self->state).pgc->goup_pgc_nr)) + assert(0); + + link_values = play_PGC(self); + link_values = process_command(self,link_values); + assert(link_values.command == PlayThis); + (self->state).blockN = link_values.data1; + + return 1; /* Jump */ +} + +int vm_next_pg(vm_t *self) +{ + /* Do we need to get a updated pgN value first? */ + (self->state).pgN += 1; + return vm_top_pg(self); +} + +int vm_prev_pg(vm_t *self) +{ + /* Do we need to get a updated pgN value first? */ + (self->state).pgN -= 1; + if((self->state).pgN == 0) { + /* Check for previous PGCN ? */ + (self->state).pgN = 1; + /* return 0; */ + } + return vm_top_pg(self); +} + + +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 *self, 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 = (self->state).domain; + + switch((self->state).domain) { + case VTS_DOMAIN: + saveRSMinfo(self, 0, block); + /* FALL THROUGH */ + case VTSM_DOMAIN: + case VMGM_DOMAIN: + (self->state).domain = menuid2domain(menuid); + if(get_PGCIT(self) != NULL && get_MENU(self, menuid) != -1) { + link_values = play_PGC(self); + link_values = process_command(self, link_values); + assert(link_values.command == PlayThis); + (self->state).blockN = link_values.data1; + return 1; /* Jump */ + } else { + (self->state).domain = old_domain; + } + break; + case FP_DOMAIN: /* FIXME XXX $$$ What should we do here? */ + break; + } + + return 0; +} + + +int vm_resume(vm_t *self) +{ + int i; + link_t link_values; + + /* Check and see if there is any rsm info!! */ + if((self->state).rsm_vtsN == 0) { + return 0; + } + + (self->state).domain = VTS_DOMAIN; + ifoOpenNewVTSI(self, self->dvd, (self->state).rsm_vtsN); + get_PGC(self, (self->state).rsm_pgcN); + + /* These should never be set in SystemSpace and/or MenuSpace */ + /* (self->state).TTN_REG = (self->state).rsm_tt; */ + /* (self->state).TT_PGCN_REG = (self->state).rsm_pgcN; */ + /* (self->state).HL_BTNN_REG = (self->state).rsm_btnn; */ + for(i = 0; i < 5; i++) { + (self->state).registers.SPRM[4 + i] = (self->state).rsm_regs[i]; + } + + if((self->state).rsm_cellN == 0) { + assert((self->state).cellN); /* Checking if this ever happens */ + (self->state).pgN = 1; + link_values = play_PG(self); + link_values = process_command(self, link_values); + assert(link_values.command == PlayThis); + (self->state).blockN = link_values.data1; + } else { + (self->state).cellN = (self->state).rsm_cellN; + (self->state).blockN = (self->state).rsm_blockN; + /* (self->state).pgN = ?? does this gets the righ value in play_Cell, no! */ + if(set_PGN(self)) { + /* Were at or past the end of the PGC, should not happen for a RSM */ + assert(0); + play_PGC_post(self); + } + } + + return 1; /* Jump */ +} + +/** + * Return the substream id for 'logical' audio stream audioN. + * 0 <= audioN < 8 + */ +int vm_get_audio_stream(vm_t *self, int audioN) +{ + int streamN = -1; + printf("dvdnav:vm.c:get_audio_stream audioN=%d\n",audioN); + if((self->state).domain == VTSM_DOMAIN + || (self->state).domain == VMGM_DOMAIN + || (self->state).domain == FP_DOMAIN) { + audioN = 0; + } + + if(audioN < 8) { + /* Is there any contol info for this logical stream */ + if((self->state).pgc->audio_control[audioN] & (1<<15)) { + streamN = ((self->state).pgc->audio_control[audioN] >> 8) & 0x07; + } + } + + if((self->state).domain == VTSM_DOMAIN + || (self->state).domain == VMGM_DOMAIN + || (self->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. + * 0 <= subpN < 32 + */ +int vm_get_subp_stream(vm_t *self, int subpN) +{ + int streamN = -1; + int source_aspect = get_video_aspect(self); + + if((self->state).domain == VTSM_DOMAIN + || (self->state).domain == VMGM_DOMAIN + || (self->state).domain == FP_DOMAIN) { + subpN = 0; + } + + if(subpN < 32) { /* a valid logical stream */ + /* Is this logical stream present */ + if((self->state).pgc->subp_control[subpN] & (1<<31)) { + if(source_aspect == 0) /* 4:3 */ + streamN = ((self->state).pgc->subp_control[subpN] >> 24) & 0x1f; + if(source_aspect == 3) /* 16:9 */ + streamN = ((self->state).pgc->subp_control[subpN] >> 16) & 0x1f; + } + } + + /* Paranoia.. if no stream select 0 anyway */ +/* I am not paranoid */ +/* if((self->state).domain == VTSM_DOMAIN + || (self->state).domain == VMGM_DOMAIN + || (self->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 *self) +{ + int subpN; + int streamN; + subpN = (self->state).SPST_REG & ~0x40; + streamN = vm_get_subp_stream(self, subpN); + + /* If no such stream, then select the first one that exists. */ + if(streamN == -1) { + for(subpN = 0; subpN < 32; subpN++) { + if((self->state).pgc->subp_control[subpN] & (1<<31)) { + + streamN = vm_get_subp_stream(self, subpN); + 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((self->state).domain == VTS_DOMAIN && !((self->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 *self) +{ + int audioN; + int streamN; + audioN = (self->state).AST_REG ; + streamN = vm_get_audio_stream(self, audioN); + + /* If no such stream, then select the first one that exists. */ + if(streamN == -1) { + for(audioN = 0; audioN < 8; audioN++) { + if((self->state).pgc->audio_control[audioN] & (1<<15)) { + streamN = vm_get_audio_stream(self, audioN); + break; + } + } + } + + return streamN; +} + + +void vm_get_angle_info(vm_t *self, int *num_avail, int *current) +{ + *num_avail = 1; + *current = 1; + + if((self->state).domain == VTS_DOMAIN) { + /* TTN_REG does not allways point to the correct title.. */ + title_info_t *title; + if((self->state).TTN_REG > self->vmgi->tt_srpt->nr_of_srpts) + return; + title = &self->vmgi->tt_srpt->title[(self->state).TTN_REG - 1]; + if(title->title_set_nr != (self->state).vtsN || + title->vts_ttn != (self->state).VTS_TTN_REG) + return; + *num_avail = title->nr_of_angles; + *current = (self->state).AGL_REG; + if(*current > *num_avail) /* Is this really a good idea? */ + *current = *num_avail; + } +} + + +void vm_get_audio_info(vm_t *self, int *num_avail, int *current) +{ + if((self->state).domain == VTS_DOMAIN) { + *num_avail = self->vtsi->vtsi_mat->nr_of_vts_audio_streams; + *current = (self->state).AST_REG; + } else if((self->state).domain == VTSM_DOMAIN) { + *num_avail = self->vtsi->vtsi_mat->nr_of_vtsm_audio_streams; /* 1 */ + *current = 1; + } else if((self->state).domain == VMGM_DOMAIN || (self->state).domain == FP_DOMAIN) { + *num_avail = self->vmgi->vmgi_mat->nr_of_vmgm_audio_streams; /* 1 */ + *current = 1; + } +} + +void vm_get_subp_info(vm_t *self, int *num_avail, int *current) +{ + if((self->state).domain == VTS_DOMAIN) { + *num_avail = self->vtsi->vtsi_mat->nr_of_vts_subp_streams; + *current = (self->state).SPST_REG; + } else if((self->state).domain == VTSM_DOMAIN) { + *num_avail = self->vtsi->vtsi_mat->nr_of_vtsm_subp_streams; /* 1 */ + *current = 0x41; + } else if((self->state).domain == VMGM_DOMAIN || (self->state).domain == FP_DOMAIN) { + *num_avail = self->vmgi->vmgi_mat->nr_of_vmgm_subp_streams; /* 1 */ + *current = 0x41; + } +} + +subp_attr_t vm_get_subp_attr(vm_t *self, int streamN) +{ + subp_attr_t attr; + + if((self->state).domain == VTS_DOMAIN) { + attr = self->vtsi->vtsi_mat->vts_subp_attr[streamN]; + } else if((self->state).domain == VTSM_DOMAIN) { + attr = self->vtsi->vtsi_mat->vtsm_subp_attr; + } else if((self->state).domain == VMGM_DOMAIN || (self->state).domain == FP_DOMAIN) { + attr = self->vmgi->vmgi_mat->vmgm_subp_attr; + } + return attr; +} + +audio_attr_t vm_get_audio_attr(vm_t *self, int streamN) +{ + audio_attr_t attr; + + if((self->state).domain == VTS_DOMAIN) { + attr = self->vtsi->vtsi_mat->vts_audio_attr[streamN]; + } else if((self->state).domain == VTSM_DOMAIN) { + attr = self->vtsi->vtsi_mat->vtsm_audio_attr; + } else if((self->state).domain == VMGM_DOMAIN || (self->state).domain == FP_DOMAIN) { + attr = self->vmgi->vmgi_mat->vmgm_audio_attr; + } + return attr; +} + +video_attr_t vm_get_video_attr(vm_t *self) +{ + video_attr_t attr; + + if((self->state).domain == VTS_DOMAIN) { + attr = self->vtsi->vtsi_mat->vts_video_attr; + } else if((self->state).domain == VTSM_DOMAIN) { + attr = self->vtsi->vtsi_mat->vtsm_video_attr; + } else if((self->state).domain == VMGM_DOMAIN || (self->state).domain == FP_DOMAIN) { + attr = self->vmgi->vmgi_mat->vmgm_video_attr; + } + return attr; +} + +void vm_get_video_res(vm_t *self, int *width, int *height) +{ + video_attr_t attr; + + attr = vm_get_video_attr(self); + + 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 *self, int cellN, int blockN) +{ + int i; + + if(cellN != 0) { + (self->state).rsm_cellN = cellN; + (self->state).rsm_blockN = 0; + } else { + (self->state).rsm_cellN = (self->state).cellN; + (self->state).rsm_blockN = blockN; + } + (self->state).rsm_vtsN = (self->state).vtsN; + (self->state).rsm_pgcN = get_PGCN(self); + + /* assert((self->state).rsm_pgcN == (self->state).TT_PGCN_REG); // for VTS_DOMAIN */ + + for(i = 0; i < 5; i++) { + (self->state).rsm_regs[i] = (self->state).registers.SPRM[4 + i]; + } +} + + + +/* Figure out the correct pgN from the cell and update (self->state). */ +static int set_PGN(vm_t *self) { + int new_pgN = 0; + + while(new_pgN < (self->state).pgc->nr_of_programs + && (self->state).cellN >= (self->state).pgc->program_map[new_pgN]) + new_pgN++; + + if(new_pgN == (self->state).pgc->nr_of_programs) /* We are at the last program */ + if((self->state).cellN > (self->state).pgc->nr_of_cells) + return 1; /* We are past the last cell */ + + (self->state).pgN = new_pgN; + + if((self->state).domain == VTS_DOMAIN) { + playback_type_t *pb_ty; + if((self->state).TTN_REG > self->vmgi->tt_srpt->nr_of_srpts) + return 0; /* ?? */ + pb_ty = &self->vmgi->tt_srpt->title[(self->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((self->state).VTS_TTN_REG <= ptt_srpt->nr_of_srpts); + assert(get_PGCN() == ptt_srpt->title[(self->state).VTS_TTN_REG - 1].ptt[0].pgcn); + assert(1 == ptt_srpt->title[(self->state).VTS_TTN_REG - 1].ptt[0].pgn); +#endif + (self->state).PTTN_REG = (self->state).pgN; + } + } + + return 0; +} + + + + + +static link_t play_PGC(vm_t *self) +{ + link_t link_values; + + fprintf(stderr, "vm: play_PGC:"); + if((self->state).domain != FP_DOMAIN) + fprintf(stderr, " (self->state).pgcN (%i)\n", get_PGCN(self)); + else + fprintf(stderr, " first_play_pgc\n"); + + /* This must be set before the pre-commands are executed because they */ + /* might contain a CallSS that will save resume state */ + (self->state).pgN = 1; + (self->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((self->state).pgc->command_tbl && (self->state).pgc->command_tbl->nr_of_pre) { + if(vmEval_CMD((self->state).pgc->command_tbl->pre_cmds, + (self->state).pgc->command_tbl->nr_of_pre, + &(self->state).registers, &link_values)) { + /* link_values contains the 'jump' return value */ + return link_values; + } else { + fprintf(stderr, "PGC pre commands didn't do a Jump, Link or Call\n"); + } + } + return play_PG(self); +} + + +static link_t play_PG(vm_t *self) +{ + fprintf(stderr, "play_PG: (self->state).pgN (%i)\n", (self->state).pgN); + + assert((self->state).pgN > 0); + if((self->state).pgN > (self->state).pgc->nr_of_programs) { + fprintf(stderr, "(self->state).pgN (%i) == pgc->nr_of_programs + 1 (%i)\n", + (self->state).pgN, (self->state).pgc->nr_of_programs + 1); + assert((self->state).pgN == (self->state).pgc->nr_of_programs + 1); + return play_PGC_post(self); + } + + (self->state).cellN = (self->state).pgc->program_map[(self->state).pgN - 1]; + + return play_Cell(self); +} + + +static link_t play_Cell(vm_t *self) +{ + fprintf(stderr, "play_Cell: (self->state).cellN (%i)\n", (self->state).cellN); + + assert((self->state).cellN > 0); + if((self->state).cellN > (self->state).pgc->nr_of_cells) { + fprintf(stderr, "(self->state).cellN (%i) == pgc->nr_of_cells + 1 (%i)\n", + (self->state).cellN, (self->state).pgc->nr_of_cells + 1); + assert((self->state).cellN == (self->state).pgc->nr_of_cells + 1); + return play_PGC_post(self); + } + + + /* Multi angle/Interleaved */ + switch((self->state).pgc->cell_playback[(self->state).cellN - 1].block_mode) { + case 0: /* Normal */ + assert((self->state).pgc->cell_playback[(self->state).cellN - 1].block_type == 0); + break; + case 1: /* The first cell in the block */ + switch((self->state).pgc->cell_playback[(self->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. */ + (self->state).cellN += (self->state).AGL_REG - 1; + assert((self->state).cellN <= (self->state).pgc->nr_of_cells); + assert((self->state).pgc->cell_playback[(self->state).cellN - 1].block_mode != 0); + assert((self->state).pgc->cell_playback[(self->state).cellN - 1].block_type == 1); + break; + case 2: /* ?? */ + case 3: /* ?? */ + default: + fprintf(stderr, "Invalid? Cell block_mode (%d), block_type (%d)\n", + (self->state).pgc->cell_playback[(self->state).cellN - 1].block_mode, + (self->state).pgc->cell_playback[(self->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 (self->state).pgN and PTTN_REG */ + if(set_PGN(self)) { + /* Should not happen */ + link_t tmp = {LinkTailPGC, /* No Button */ 0, 0, 0}; + assert(0); + return tmp; + } + + { + link_t tmp = {PlayThis, /* Block in Cell */ 0, 0, 0}; + return tmp; + } + +} + +static link_t play_Cell_post(vm_t *self) +{ + cell_playback_t *cell; + + fprintf(stderr, "play_Cell_post: (self->state).cellN (%i)\n", (self->state).cellN); + + cell = &(self->state).pgc->cell_playback[(self->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((self->state).pgc->command_tbl != NULL); + assert((self->state).pgc->command_tbl->nr_of_cell >= cell->cell_cmd_nr); + fprintf(stderr, "Cell command pressent, executing\n"); + if(vmEval_CMD(&(self->state).pgc->command_tbl->cell_cmds[cell->cell_cmd_nr - 1], 1, + &(self->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((self->state).pgc->cell_playback[(self->state).cellN - 1].block_mode) { + case 0: /* Normal */ + assert((self->state).pgc->cell_playback[(self->state).cellN - 1].block_type == 0); + (self->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((self->state).pgc->cell_playback[(self->state).cellN - 1].block_type) { + case 0: /* Not part of a block */ + assert(0); + case 1: /* Angle block */ + /* Skip the 'other' angles */ + (self->state).cellN++; + while((self->state).cellN <= (self->state).pgc->nr_of_cells + && (self->state).pgc->cell_playback[(self->state).cellN - 1].block_mode >= 2) { + (self->state).cellN++; + } + break; + case 2: /* ?? */ + case 3: /* ?? */ + default: + fprintf(stderr, "Invalid? Cell block_mode (%d), block_type (%d)\n", + (self->state).pgc->cell_playback[(self->state).cellN - 1].block_mode, + (self->state).pgc->cell_playback[(self->state).cellN - 1].block_type); + } + break; + } + + + /* Figure out the correct pgN for the new cell */ + if(set_PGN(self)) { + fprintf(stderr, "last cell in this PGC\n"); + return play_PGC_post(self); + } + + return play_Cell(self); +} + + +static link_t play_PGC_post(vm_t *self) +{ + link_t link_values; + + fprintf(stderr, "play_PGC_post:\n"); + + assert((self->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((self->state).pgc->command_tbl && + vmEval_CMD((self->state).pgc->command_tbl->post_cmds, + (self->state).pgc->command_tbl->nr_of_post, + &(self->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((self->state).pgc->next_pgc_nr != 0); + return link_next_pgc; + } +} + + +static link_t process_command(vm_t *self, link_t link_values) +{ + /* FIXME $$$ Move this to a separate function? */ + self->badness_counter++; + if (self->badness_counter > 1) fprintf(stderr, "**** process_command re-entered %d*****\n",self->badness_counter); + while(link_values.command != PlayThis) { + + 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(self); + + switch(link_values.command) { + case LinkNoLink: + /* No Link */ + if(link_values.data1 != 0) + (self->state).HL_BTNN_REG = link_values.data1 << 10; + exit(1); + + case LinkTopC: + /* Link to Top?? Cell */ + if(link_values.data1 != 0) + (self->state).HL_BTNN_REG = link_values.data1 << 10; + link_values = play_Cell(self); + break; + case LinkNextC: + /* Link to Next Cell */ + if(link_values.data1 != 0) + (self->state).HL_BTNN_REG = link_values.data1 << 10; + (self->state).cellN += 1; /* FIXME: What if cellN becomes > nr_of_cells? */ + link_values = play_Cell(self); + break; + case LinkPrevC: + /* Link to Previous Cell */ + if(link_values.data1 != 0) + (self->state).HL_BTNN_REG = link_values.data1 << 10; + (self->state).cellN -= 1; /* FIXME: What if cellN becomes < 1? */ + link_values = play_Cell(self); + break; + + case LinkTopPG: + /* Link to Top Program */ + if(link_values.data1 != 0) + (self->state).HL_BTNN_REG = link_values.data1 << 10; + /* Does pgN always contain the current value? */ + link_values = play_PG(self); + break; + case LinkNextPG: + /* Link to Next Program */ + if(link_values.data1 != 0) + (self->state).HL_BTNN_REG = link_values.data1 << 10; + /* Does pgN always contain the current value? */ + (self->state).pgN += 1; /* FIXME: What if pgN becomes > pgc.nr_of_programs? */ + link_values = play_PG(self); + break; + case LinkPrevPG: + /* Link to Previous Program */ + if(link_values.data1 != 0) + (self->state).HL_BTNN_REG = link_values.data1 << 10; + /* Does pgN always contain the current value? */ + (self->state).pgN -= 1; /* FIXME: What if pgN becomes < 1? */ + link_values = play_PG(self); + break; + + case LinkTopPGC: + /* Link to Top Program Chain */ + if(link_values.data1 != 0) + (self->state).HL_BTNN_REG = link_values.data1 << 10; + link_values = play_PGC(self); + break; + case LinkNextPGC: + /* Link to Next Program Chain */ + if(link_values.data1 != 0) + (self->state).HL_BTNN_REG = link_values.data1 << 10; + assert((self->state).pgc->next_pgc_nr != 0); + if(get_PGC(self, (self->state).pgc->next_pgc_nr)) + assert(0); + link_values = play_PGC(self); + break; + case LinkPrevPGC: + /* Link to Previous Program Chain */ + if(link_values.data1 != 0) + (self->state).HL_BTNN_REG = link_values.data1 << 10; + assert((self->state).pgc->prev_pgc_nr != 0); + if(get_PGC(self, (self->state).pgc->prev_pgc_nr)) + assert(0); + link_values = play_PGC(self); + break; + case LinkGoUpPGC: + /* Link to GoUp??? Program Chain */ + if(link_values.data1 != 0) + (self->state).HL_BTNN_REG = link_values.data1 << 10; + assert((self->state).pgc->goup_pgc_nr != 0); + if(get_PGC(self, (self->state).pgc->goup_pgc_nr)) + assert(0); + link_values = play_PGC(self); + break; + case LinkTailPGC: + /* Link to Tail??? Program Chain */ + if(link_values.data1 != 0) + (self->state).HL_BTNN_REG = link_values.data1 << 10; + link_values = play_PGC_post(self); + break; + + case LinkRSM: + { + /* Link to Resume */ + int i; + /* Check and see if there is any rsm info!! */ + (self->state).domain = VTS_DOMAIN; + ifoOpenNewVTSI(self, self->dvd, (self->state).rsm_vtsN); + get_PGC(self, (self->state).rsm_pgcN); + + /* These should never be set in SystemSpace and/or MenuSpace */ + /* (self->state).TTN_REG = rsm_tt; ?? */ + /* (self->state).TT_PGCN_REG = (self->state).rsm_pgcN; ?? */ + for(i = 0; i < 5; i++) { + (self->state).registers.SPRM[4 + i] = (self->state).rsm_regs[i]; + } + + if(link_values.data1 != 0) + (self->state).HL_BTNN_REG = link_values.data1 << 10; + + if((self->state).rsm_cellN == 0) { + assert((self->state).cellN); /* Checking if this ever happens */ + /* assert( time/block/vobu is 0 ); */ + (self->state).pgN = 1; + link_values = play_PG(self); + } else { + /* assert( time/block/vobu is _not_ 0 ); */ + /* play_Cell_at_time */ + /* (self->state).pgN = ?? this gets the righ value in play_Cell */ + (self->state).cellN = (self->state).rsm_cellN; + link_values.command = PlayThis; + link_values.data1 = (self->state).rsm_blockN; + if(set_PGN(self)) { + /* 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(get_PGC(self, link_values.data1)) + assert(0); + link_values = play_PGC(self); + break; + case LinkPTTN: + /* Link to Part of this Title Number:data1 */ + assert((self->state).domain == VTS_DOMAIN); + if(link_values.data2 != 0) + (self->state).HL_BTNN_REG = link_values.data2 << 10; + if(get_VTS_PTT(self, (self->state).vtsN, (self->state).VTS_TTN_REG, link_values.data1) == -1) + assert(0); + link_values = play_PG(self); + break; + case LinkPGN: + /* Link to Program Number:data1 */ + if(link_values.data2 != 0) + (self->state).HL_BTNN_REG = link_values.data2 << 10; + /* Update any other state, PTTN perhaps? */ + (self->state).pgN = link_values.data1; + link_values = play_PG(self); + break; + case LinkCN: + /* Link to Cell Number:data1 */ + if(link_values.data2 != 0) + (self->state).HL_BTNN_REG = link_values.data2 << 10; + /* Update any other state, pgN, PTTN perhaps? */ + (self->state).cellN = link_values.data1; + link_values = play_Cell(self); + break; + + case Exit: + exit(1); /* 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) */ + //fprintf(stderr,"****** JumpTT is Broken, please fix me!!! ****\n"); + assert((self->state).domain == VMGM_DOMAIN || (self->state).domain == FP_DOMAIN); /* ?? */ + if(get_TT(self,link_values.data1) == -1) + assert(0); + link_values = play_PGC(self); + 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((self->state).domain == VTSM_DOMAIN || (self->state).domain == VTS_DOMAIN); /* ?? */ + /* FIXME: Should be able to use get_VTS_PTT here */ + if(get_VTS_TT(self,(self->state).vtsN, link_values.data1) == -1) + assert(0); + link_values = play_PGC(self); + 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((self->state).domain == VTSM_DOMAIN || (self->state).domain == VTS_DOMAIN); /* ?? */ + if(get_VTS_PTT(self,(self->state).vtsN, link_values.data1, link_values.data2) == -1) + assert(0); + link_values = play_PG(self); + break; + + case JumpSS_FP: + /* Jump to First Play Domain */ + /* Only allowed from the VTS Menu Domain(VTSM) */ + /* or the Video Manager domain (VMG) */ + assert((self->state).domain == VMGM_DOMAIN || (self->state).domain == VTSM_DOMAIN); /* ?? */ + get_FP_PGC(self); + link_values = play_PGC(self); + 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((self->state).domain == VMGM_DOMAIN || + (self->state).domain == VTSM_DOMAIN || + (self->state).domain == FP_DOMAIN); /* ?? */ + (self->state).domain = VMGM_DOMAIN; + if(get_MENU(self,link_values.data1) == -1) + assert(0); + link_values = play_PGC(self); + 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 */ + fprintf(stderr, "dvdnav: BUG TRACKING *******************************************************************\n"); + fprintf(stderr, "dvdnav: If you see this message, please report these values to the dvd-devel mailing list.\n"); + fprintf(stderr, " data1=%u data2=%u data3=%u\n", + link_values.data1, + link_values.data2, + link_values.data3); + fprintf(stderr, "dvdnav: *******************************************************************\n"); + + if(link_values.data1 !=0) { + assert((self->state).domain == VMGM_DOMAIN || (self->state).domain == FP_DOMAIN); /* ?? */ + (self->state).domain = VTSM_DOMAIN; + ifoOpenNewVTSI(self, self->dvd, link_values.data1); /* Also sets (self->state).vtsN */ + } else { + /* This happens on 'The Fifth Element' region 2. */ + assert((self->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); */ + (self->state).VTS_TTN_REG = link_values.data2; + if(get_MENU(self, link_values.data3) == -1) + assert(0); + link_values = play_PGC(self); + break; + case JumpSS_VMGM_PGC: + assert((self->state).domain == VMGM_DOMAIN || + (self->state).domain == VTSM_DOMAIN || + (self->state).domain == FP_DOMAIN); /* ?? */ + (self->state).domain = VMGM_DOMAIN; + if(get_PGC(self,link_values.data1) == -1) + assert(0); + link_values = play_PGC(self); + break; + + case CallSS_FP: + assert((self->state).domain == VTS_DOMAIN); /* ?? */ + /* Must be called before domain is changed */ + saveRSMinfo(self, link_values.data1, /* We dont have block info */ 0); + get_FP_PGC(self); + link_values = play_PGC(self); + break; + case CallSS_VMGM_MENU: + assert((self->state).domain == VTS_DOMAIN); /* ?? */ + /* Must be called before domain is changed */ + saveRSMinfo(self,link_values.data2, /* We dont have block info */ 0); + (self->state).domain = VMGM_DOMAIN; + if(get_MENU(self,link_values.data1) == -1) + assert(0); + link_values = play_PGC(self); + break; + case CallSS_VTSM: + assert((self->state).domain == VTS_DOMAIN); /* ?? */ + /* Must be called before domain is changed */ + saveRSMinfo(self,link_values.data2, /* We dont have block info */ 0); + (self->state).domain = VTSM_DOMAIN; + if(get_MENU(self,link_values.data1) == -1) + assert(0); + link_values = play_PGC(self); + break; + case CallSS_VMGM_PGC: + assert((self->state).domain == VTS_DOMAIN); /* ?? */ + /* Must be called before domain is changed */ + saveRSMinfo(self,link_values.data2, /* We dont have block info */ 0); + (self->state).domain = VMGM_DOMAIN; + if(get_PGC(self,link_values.data1) == -1) + assert(0); + link_values = play_PGC(self); + break; + case PlayThis: + /* Should never happen. */ + break; + } + fprintf(stderr, "After:"); + vm_print_current_domain_state(self); + + } + self->badness_counter--; + return link_values; +} + +static int get_TT(vm_t *self, int tt) +{ + //fprintf(stderr,"****** get_TT is Broken, please fix me!!! ****\n"); + assert(tt <= self->vmgi->tt_srpt->nr_of_srpts); + + (self->state).TTN_REG = tt; + + return get_VTS_TT(self, self->vmgi->tt_srpt->title[tt - 1].title_set_nr, + self->vmgi->tt_srpt->title[tt - 1].vts_ttn); +} + + +static int get_VTS_TT(vm_t *self, int vtsN, int vts_ttn) +{ + int pgcN; + //fprintf(stderr,"****** get_VTS_TT is Broken, please fix me!!! ****\n"); + fprintf(stderr,"title_set_nr=%d\n", vtsN); + fprintf(stderr,"vts_ttn=%d\n", vts_ttn); + + (self->state).domain = VTS_DOMAIN; + if(vtsN != (self->state).vtsN) { + fprintf(stderr,"****** opening new VTSI ****\n"); + ifoOpenNewVTSI(self, self->dvd, vtsN); /* Also sets (self->state).vtsN */ + fprintf(stderr,"****** opened VTSI ****\n"); + } + + pgcN = get_ID(self, vts_ttn); /* This might return -1 */ + assert(pgcN != -1); + + /* (self->state).TTN_REG = ?? Must search tt_srpt for a matching entry... */ + (self->state).VTS_TTN_REG = vts_ttn; + /* Any other registers? */ + + return get_PGC(self, pgcN); +} + + +static int get_VTS_PTT(vm_t *self, int vtsN, int /* is this really */ vts_ttn, int part) +{ + int pgcN, pgN; + + (self->state).domain = VTS_DOMAIN; + if(vtsN != (self->state).vtsN) + ifoOpenNewVTSI(self, self->dvd, vtsN); /* Also sets (self->state).vtsN */ + + assert(vts_ttn <= self->vtsi->vts_ptt_srpt->nr_of_srpts); + assert(part <= self->vtsi->vts_ptt_srpt->title[vts_ttn - 1].nr_of_ptts); + + pgcN = self->vtsi->vts_ptt_srpt->title[vts_ttn - 1].ptt[part - 1].pgcn; + pgN = self->vtsi->vts_ptt_srpt->title[vts_ttn - 1].ptt[part - 1].pgn; + + /* (self->state).TTN_REG = ?? Must search tt_srpt for a matchhing entry... */ + (self->state).VTS_TTN_REG = vts_ttn; + /* Any other registers? */ + + (self->state).pgN = pgN; /* ?? */ + + return get_PGC(self, pgcN); +} + + + +static int get_FP_PGC(vm_t *self) +{ + (self->state).domain = FP_DOMAIN; + + (self->state).pgc = self->vmgi->first_play_pgc; + + return 0; +} + + +static int get_MENU(vm_t *self, int menu) +{ + assert((self->state).domain == VMGM_DOMAIN || (self->state).domain == VTSM_DOMAIN); + return get_PGC(self, get_ID(self, menu)); +} + +static int get_ID(vm_t *self, int id) +{ + int pgcN, i; + pgcit_t *pgcit; + + /* Relies on state to get the correct pgcit. */ + pgcit = get_PGCIT(self); + assert(pgcit != NULL); + + /* 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); + return -1; /* error */ +} + + + +static int get_PGC(vm_t *self, int pgcN) +{ + /* FIXME: Keep this up to date with the ogle people */ + pgcit_t *pgcit; + + pgcit = get_PGCIT(self); + + assert(pgcit != NULL); /* ?? Make this return -1 instead */ + if(pgcN < 1 || pgcN > pgcit->nr_of_pgci_srp) { +/* if(pgcit->nr_of_pgci_srp != 1) */ + return -1; /* error */ +/* pgcN = 1; */ + } + + /* (self->state).pgcN = pgcN; */ + (self->state).pgc = pgcit->pgci_srp[pgcN - 1].pgc; + + if((self->state).domain == VTS_DOMAIN) + (self->state).TT_PGCN_REG = pgcN; + + return 0; +} + +static int get_PGCN(vm_t *self) +{ + pgcit_t *pgcit; + int pgcN = 1; + + pgcit = get_PGCIT(self); + + assert(pgcit != NULL); + + while(pgcN <= pgcit->nr_of_pgci_srp) { + if(pgcit->pgci_srp[pgcN - 1].pgc == (self->state).pgc) + return pgcN; + pgcN++; + } + + return -1; /* error */ +} + + +static int get_video_aspect(vm_t *self) +{ + int aspect = 0; + + if((self->state).domain == VTS_DOMAIN) { + aspect = self->vtsi->vtsi_mat->vts_video_attr.display_aspect_ratio; + } else if((self->state).domain == VTSM_DOMAIN) { + aspect = self->vtsi->vtsi_mat->vtsm_video_attr.display_aspect_ratio; + } else if((self->state).domain == VMGM_DOMAIN) { + aspect = self->vmgi->vmgi_mat->vmgm_video_attr.display_aspect_ratio; + } + fprintf(stderr, "dvdnav:get_video_aspect:aspect=%d\n",aspect); + assert(aspect == 0 || aspect == 3); + (self->state).registers.SPRM[14] &= ~(0x3 << 10); + (self->state).registers.SPRM[14] |= aspect << 10; + + return aspect; +} + + + + + + +static void ifoOpenNewVTSI(vm_t *self, dvd_reader_t *dvd, int vtsN) +{ + if((self->state).vtsN == vtsN) { + return; /* We alread have it */ + } + + if(self->vtsi != NULL) + ifoClose(self->vtsi); + + self->vtsi = ifoOpenVTSI(dvd, vtsN); + if(self->vtsi == NULL) { + fprintf(stderr, "ifoOpenVTSI failed\n"); + exit(1); + } + if(!ifoRead_VTS_PTT_SRPT(self->vtsi)) { + fprintf(stderr, "ifoRead_VTS_PTT_SRPT failed\n"); + exit(1); + } + if(!ifoRead_PGCIT(self->vtsi)) { + fprintf(stderr, "ifoRead_PGCIT failed\n"); + exit(1); + } + if(!ifoRead_PGCI_UT(self->vtsi)) { + fprintf(stderr, "ifoRead_PGCI_UT failed\n"); + exit(1); + } + if(!ifoRead_VOBU_ADMAP(self->vtsi)) { + fprintf(stderr, "ifoRead_VOBU_ADMAP vtsi failed\n"); + exit(1); + } + if(!ifoRead_TITLE_VOBU_ADMAP(self->vtsi)) { + fprintf(stderr, "ifoRead_TITLE_VOBU_ADMAP vtsi failed\n"); + exit(1); + } + (self->state).vtsN = vtsN; +} + + + + +static pgcit_t* get_MENU_PGCIT(vm_t *self, 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)); + i = 0; /* error? */ + } + + return h->pgci_ut->lu[i].pgcit; +} + +/* Uses state to decide what to return */ +static pgcit_t* get_PGCIT(vm_t *self) { + pgcit_t *pgcit; + + if((self->state).domain == VTS_DOMAIN) { + pgcit = self->vtsi->vts_pgcit; + } else if((self->state).domain == VTSM_DOMAIN) { + pgcit = get_MENU_PGCIT(self, self->vtsi, (self->state).registers.SPRM[0]); + } else if((self->state).domain == VMGM_DOMAIN) { + pgcit = get_MENU_PGCIT(self, self->vmgi, (self->state).registers.SPRM[0]); + } else { + pgcit = NULL; /* Should never hapen */ + } + + return pgcit; +} + +/* + * $Log$ + * Revision 1.1 2002/03/12 19:45:55 richwareham + * Initial revision + * + * 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 itself. + * 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. + * + * + */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm.h Tue Mar 12 19:45:53 2002 +0000 @@ -0,0 +1,130 @@ +/* + * 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$ + * + */ + +#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; + 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 { + dvd_reader_t *dvd; + ifo_handle_t *vmgi; + ifo_handle_t *vtsi; + dvd_state_t state; + int badness_counter; +} 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 *self); + +/* IFO access */ +ifo_handle_t *vm_get_vmgi(vm_t *self); +ifo_handle_t *vm_get_vtsi(vm_t *self); + +/* Reader Access */ +dvd_reader_t *vm_get_dvd_reader(vm_t *self); + +/* Jumping */ +int vm_start_title(vm_t *self, int tt); +int vm_jump_prog(vm_t *self, int pr); + +/* Other calls */ +int vm_reset(vm_t *self, char *dvdroot); /* , register_t regs); */ +int vm_start(vm_t *self); +int vm_eval_cmd(vm_t *self, vm_cmd_t *cmd); +int vm_get_next_cell(vm_t *self); +int vm_menu_call(vm_t *self, DVDMenuID_t menuid, int block); +int vm_resume(vm_t *self); +int vm_go_up(vm_t *self); +int vm_top_pg(vm_t *self); +int vm_next_pg(vm_t *self); +int vm_prev_pg(vm_t *self); +int vm_get_audio_stream(vm_t *self, int audioN); +int vm_get_audio_active_stream(vm_t *self); +int vm_get_subp_stream(vm_t *self, int subpN); +int vm_get_subp_active_stream(vm_t *self); +void vm_get_angle_info(vm_t *self, int *num_avail, int *current); +void vm_get_audio_info(vm_t *self, int *num_avail, int *current); +void vm_get_subp_info(vm_t *self, int *num_avail, int *current); +subp_attr_t vm_get_subp_attr(vm_t *self, int streamN); +audio_attr_t vm_get_audio_attr(vm_t *self, int streamN); +void vm_get_video_res(vm_t *self, int *width, int *height); + +#endif /* VM_HV_INCLUDED */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmcmd.c Tue Mar 12 19:45:53 2002 +0000 @@ -0,0 +1,520 @@ +/* + * 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$ + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <ctype.h> +#include <inttypes.h> + +#include "vmcmd.h" + + +/* freebsd compatibility */ +#ifndef PRIu8 +#define PRIu8 "d" +#endif + +/* freebsd compatibility */ +#ifndef PRIu16 +#define PRIu16 "d" +#endif + + +typedef struct +{ + uint8_t bits[8]; + uint8_t examined[8]; +} cmd_t; + +/* Fix this.. pass as parameters instead. */ +static cmd_t cmd; + +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 uint32_t bits(int byte, int bit, int count) { + uint32_t val = 0; + int bit_mask; + + while(count--) { + if(bit > 7) { + bit = 0; + byte++; + } + bit_mask = 0x01 << (7-bit); + val <<= 1; + if((cmd.bits[byte]) & bit_mask) + val |= 1; + cmd.examined[byte] |= bit_mask; + bit++; + } + return val; +} + + +static void print_system_reg(uint16_t reg) { + if(reg < sizeof(system_reg_abbr_table) / sizeof(char *)) + fprintf(stderr, system_reg_table[reg]); + else + fprintf(stderr, " WARNING: Unknown system register "); +} + +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(int immediate, int byte) { + if(immediate) { + int i = bits(byte,0,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(bits(byte + 1,0,8)); + } +} + +static void print_reg_or_data_2(int immediate, int byte) { + if(immediate) + fprintf(stderr, "0x%x", bits(byte,1,7)); + else + fprintf(stderr, "g[%" PRIu8 "]", bits(byte,4,4)); +} + +static void print_if_version_1(void) { + uint8_t op = bits(1,1,3); + + if(op) { + fprintf(stderr, "if ("); + print_reg(bits(3,0,8)); + print_cmp_op(op); + print_reg_or_data(bits(1,0,1), 4); + fprintf(stderr, ") "); + } +} + +static void print_if_version_2(void) { + uint8_t op = bits(1,1,3); + + if(op) { + fprintf(stderr, "if ("); + print_reg(bits(6,0,8)); + print_cmp_op(op); + print_reg(bits(7,0,8)); + fprintf(stderr, ") "); + } +} + +static void print_if_version_3(void) { + uint8_t op = bits(1,1,3); + + if(op) { + fprintf(stderr, "if ("); + print_reg(bits(2,4,4)); + print_cmp_op(op); + print_reg_or_data(bits(1,0,1), 6); + fprintf(stderr, ") "); + } +} + +static void print_if_version_4(void) { + uint8_t op = bits(1,1,3); + + if(op) { + fprintf(stderr, "if ("); + print_reg(bits(1,4,4)); + print_cmp_op(op); + print_reg_or_data(bits(1,0,1), 4); + fprintf(stderr, ") "); + } +} + +static void print_special_instruction(void) { + uint8_t op = bits(1,4,4); + + switch(op) { + case 0: /* NOP */ + fprintf(stderr, "Nop"); + break; + case 1: /* Goto line */ + fprintf(stderr, "Goto %" PRIu8, bits(7,0,8)); + break; + case 2: /* Break */ + fprintf(stderr, "Break"); + break; + case 3: /* Parental level */ + fprintf(stderr, "SetTmpPML %" PRIu8 ", Goto %" PRIu8, + bits(6,4,4), bits(7,0,8)); + break; + default: + fprintf(stderr, "WARNING: Unknown special instruction (%i)", + bits(1,4,4)); + } +} + +static void print_linksub_instruction(void) { + int linkop = bits(7,3,5); + int button = bits(6,0,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(int optional) { + uint8_t op = bits(1,4,4); + + if(optional && op) + fprintf(stderr, ", "); + + switch(op) { + case 0: + if(!optional) + fprintf(stderr, "WARNING: NOP (link)!"); + break; + case 1: + print_linksub_instruction(); + break; + case 4: + fprintf(stderr, "LinkPGCN %" PRIu16, bits(6,1,15)); + break; + case 5: + fprintf(stderr, "LinkPTT %" PRIu16 " (button %" PRIu8 ")", + bits(6,6,10), bits(6,0,6)); + break; + case 6: + fprintf(stderr, "LinkPGN %" PRIu8 " (button %" PRIu8 ")", + bits(7,1,7), bits(6,0,6)); + break; + case 7: + fprintf(stderr, "LinkCN %" PRIu8 " (button %" PRIu8 ")", + bits(7,0,8), bits(6,0,6)); + break; + default: + fprintf(stderr, "WARNING: Unknown link instruction"); + } +} + +static void print_jump_instruction(void) { + switch(bits(1,4,4)) { + case 1: + fprintf(stderr, "Exit"); + break; + case 2: + fprintf(stderr, "JumpTT %" PRIu8, bits(5,1,7)); + break; + case 3: + fprintf(stderr, "JumpVTS_TT %" PRIu8, bits(5,1,7)); + break; + case 5: + fprintf(stderr, "JumpVTS_PTT %" PRIu8 ":%" PRIu16, + bits(5,1,7), bits(2,6,10)); + break; + case 6: + switch(bits(5,0,2)) { + case 0: + fprintf(stderr, "JumpSS FP"); + break; + case 1: + fprintf(stderr, "JumpSS VMGM (menu %" PRIu8 ")", bits(5,4,4)); + break; + case 2: + fprintf(stderr, "JumpSS VTSM (vts %" PRIu8 ", title %" PRIu8 + ", menu %" PRIu8 ")", bits(4,0,8), bits(3,0,8), bits(5,4,4)); + break; + case 3: + fprintf(stderr, "JumpSS VMGM (pgc %" PRIu8 ")", bits(2,1,15)); + break; + } + break; + case 8: + switch(bits(5,0,2)) { + case 0: + fprintf(stderr, "CallSS FP (rsm_cell %" PRIu8 ")", + bits(4,0,8)); + break; + case 1: + fprintf(stderr, "CallSS VMGM (menu %" PRIu8 + ", rsm_cell %" PRIu8 ")", bits(5,4,4), bits(4,0,8)); + break; + case 2: + fprintf(stderr, "CallSS VTSM (menu %" PRIu8 + ", rsm_cell %" PRIu8 ")", bits(5,4,4), bits(4,0,8)); + break; + case 3: + fprintf(stderr, "CallSS VMGM (pgc %" PRIu8 ", rsm_cell %" PRIu8 ")", + bits(2,1,15), bits(4,0,8)); + break; + } + break; + default: + fprintf(stderr, "WARNING: Unknown Jump/Call instruction"); + } +} + +static void print_system_set(void) { + int i; + + switch(bits(0,4,4)) { + case 1: /* Set system reg 1 &| 2 &| 3 (Audio, Subp. Angle) */ + for(i = 1; i <= 3; i++) { + if(bits(2+i,0,1)) { + print_system_reg(i); + fprintf(stderr, " = "); + print_reg_or_data_2(bits(0,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(bits(0,3,1), 2); + fprintf(stderr, " "); + print_system_reg(10); + fprintf(stderr, " = %" PRIu8, bits(5,0,8)); /* ?? */ + break; + case 3: /* Mode: Counter / Register + Set */ + fprintf(stderr, "SetMode "); + if(bits(5,0,1)) + fprintf(stderr, "Counter "); + else + fprintf(stderr, "Register "); + print_reg(bits(5,4,4)); + print_set_op(0x1); /* '=' */ + print_reg_or_data(bits(0,3,1), 2); + break; + case 6: /* Set system reg 8 (Highlighted button) */ + print_system_reg(8); + if(bits(0,3,1)) /* immediate */ + fprintf(stderr, " = 0x%x (button no %d)", bits(4,0,16), bits(4,0,6)); + else + fprintf(stderr, " = g[%" PRIu8 "]", bits(5,4,4)); + break; + default: + fprintf(stderr, "WARNING: Unknown system set instruction (%i)", + bits(0,4,4)); + } +} + +static void print_set_version_1(void) { + uint8_t set_op = bits(0,4,4); + + if(set_op) { + print_reg(bits(3,0,8)); + print_set_op(set_op); + print_reg_or_data(bits(0,3,1), 4); + } else { + fprintf(stderr, "NOP"); + } +} + +static void print_set_version_2(void) { + uint8_t set_op = bits(0,4,4); + + if(set_op) { + print_reg(bits(1,4,4)); + print_set_op(set_op); + print_reg_or_data(bits(0,3,1), 2); + } else { + fprintf(stderr, "NOP"); + } +} + +void vmPrint_mnemonic(vm_cmd_t *command) { + int i, extra_bits; + + for(i = 0; i < 8; i++) { + cmd.bits[i] = command->bytes[i]; + cmd.examined[i] = 0; + } + + switch(bits(0,0,3)) { /* three first bits */ + case 0: /* Special instructions */ + print_if_version_1(); + print_special_instruction(); + break; + case 1: /* Jump/Call or Link instructions */ + if(bits(0,3,1)) { + print_if_version_2(); + print_jump_instruction(); + } else { + print_if_version_1(); + print_link_instruction(0); /* must be pressent */ + } + break; + case 2: /* Set System Parameters instructions */ + print_if_version_2(); + print_system_set(); + print_link_instruction(1); /* either 'if' or 'link' */ + break; + case 3: /* Set General Parameters instructions */ + print_if_version_3(); + print_set_version_1(); + print_link_instruction(1); /* either 'if' or 'link' */ + break; + case 4: /* Set, Compare -> LinkSub instructions */ + print_set_version_2(); + fprintf(stderr, ", "); + print_if_version_4(); + print_linksub_instruction(); + break; + case 5: /* Compare -> (Set and LinkSub) instructions */ + print_if_version_4(); + fprintf(stderr, "{ "); + print_set_version_2(); + fprintf(stderr, ", "); + print_linksub_instruction(); + fprintf(stderr, " }"); + break; + case 6: /* Compare -> Set, always LinkSub instructions */ + print_if_version_4(); + fprintf(stderr, "{ "); + print_set_version_2(); + fprintf(stderr, " } "); + print_linksub_instruction(); + break; + default: + fprintf(stderr, "WARNING: Unknown instruction type (%i)", bits(0,0,3)); + } + /* Check if there still are bits set that were not examined */ + + extra_bits = 0; + for(i = 0; i < 8; i++) + if(cmd.bits[i] & ~ cmd.examined[i]) { + extra_bits = 1; + break; + } + if(extra_bits) { + fprintf(stderr, " [WARNING, unknown bits:"); + for(i = 0; i < 8; i++) + fprintf(stderr, " %02x", cmd.bits[i] & ~ cmd.examined[i]); + fprintf(stderr, "]"); + } +} + +void vmPrint_CMD(int row, vm_cmd_t *command) { + int i; + + fprintf(stderr, "(%03d) ", row + 1); + for(i = 0; i < 8; i++) + fprintf(stderr, "%02x ", command->bytes[i]); + fprintf(stderr, "| "); + + vmPrint_mnemonic(command); + fprintf(stderr, "\n"); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmcmd.h Tue Mar 12 19:45:53 2002 +0000 @@ -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$ + * + */ + +#ifndef VMCMD_H_INCLUDED +#define VMCMD_H_INCLUDED + +#include "config.h" +#include <inttypes.h> +#include <dvdread/ifo_types.h> /* Only for vm_cmd_t */ + +void vmPrint_mnemonic(vm_cmd_t *command); +void vmPrint_CMD(int row, vm_cmd_t *command); + +#endif /* VMCMD_H_INCLUDED */