/***************************************************************************** * vmg.c: DVD Video Manager library: manager functions. ***************************************************************************** * Copyright (C) 2002 VideoLAN * $Id: vmg.c,v 1.12 2003/03/09 18:11:16 gbazin Exp $ * * Authors: Stéphane Borel * * Adapted from Ogle - A video player * Copyright (C) 2000, 2001 Håkan Hjort * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ #include "config.h" #include #include #include #ifdef HAVE_UNISTD_H # include #endif #include #include "common.h" #include #include #include #include "dvdplay/dvdplay.h" #include "tools.h" #include "command.h" #include "vmg.h" #include "msg.h" /***************************************************************************** * _OpenVMGI: wrapper to dvdread functions to open video manager *****************************************************************************/ int _OpenVMGI( dvdplay_ptr dvdplay, char * psz_dvdroot ) { _dvdplay_dbg( dvdplay, "opening libdvdread" ); /* Initialize DVDRead */ dvdplay->p_dvdread = DVDOpen( psz_dvdroot ); if( dvdplay->p_dvdread == NULL ) { _dvdplay_err( dvdplay, "failed to open/read the DVD" ); return -1; } /* Initialize video manager ifo */ dvdplay->p_vmgi = ifoOpenVMGI( dvdplay->p_dvdread ); if( dvdplay->p_vmgi == NULL ) { _dvdplay_err( dvdplay, "failed to read VIDEO_TS.IFO" ); return -1; } if( !ifoRead_FP_PGC( dvdplay->p_vmgi ) ) { _dvdplay_err( dvdplay, "ifoRead_FP_PGC failed" ); return -1; } if( !ifoRead_TT_SRPT( dvdplay->p_vmgi ) ) { _dvdplay_err( dvdplay, "ifoRead_TT_SRPT failed" ); return -1; } if( !ifoRead_PGCI_UT( dvdplay->p_vmgi ) ) { _dvdplay_err( dvdplay, "ifoRead_PGCI_UT failed" ); return -1; } if( !ifoRead_VOBU_ADMAP( dvdplay->p_vmgi ) ) { _dvdplay_err( dvdplay, "ifoRead_VOBU_ADMAP failed" ); return -1; } if( !ifoRead_PTL_MAIT( dvdplay->p_vmgi ) ) { _dvdplay_err( dvdplay, "ifoRead_PTL_MAIT failed"); ; // return -1; Not really used for now.. } if( !ifoRead_VTS_ATRT( dvdplay->p_vmgi ) ) { _dvdplay_err( dvdplay, "ifoRead_VTS_ATRT failed"); ; // return -1; Not really used for now.. } //ifoRead_TXTDT_MGI(vmgi); Not implemented yet return 0; } /***************************************************************************** * _OpenVTSI: wrapper for dvdread functions to open a new vts ifo *****************************************************************************/ int _OpenVTSI( dvdplay_ptr dvdplay, int i_vtsN ) { if( dvdplay->state.i_vtsN == i_vtsN ) { /* We alread have it */ return 0; } _dvdplay_dbg( dvdplay, "opening new VTSI" ); if( dvdplay->p_vtsi != NULL ) { ifoClose( dvdplay->p_vtsi ); } dvdplay->p_vtsi = ifoOpenVTSI( dvdplay->p_dvdread, i_vtsN ); if( dvdplay->p_vtsi == NULL ) { _dvdplay_err( dvdplay, "ifoOpenVTSI failed" ); return -1; } if( !ifoRead_VTS_PTT_SRPT( dvdplay->p_vtsi ) ) { _dvdplay_err( dvdplay, "ifoRead_VTS_PTT_SRPT failed" ); return -1; } if( !ifoRead_PGCIT( dvdplay->p_vtsi ) ) { _dvdplay_err( dvdplay, "ifoRead_PGCIT failed" ); return -1; } if( !ifoRead_PGCI_UT( dvdplay->p_vtsi ) ) { _dvdplay_err( dvdplay, "ifoRead_PGCI_UT failed" ); return -1; } if( !ifoRead_VOBU_ADMAP( dvdplay->p_vtsi ) ) { _dvdplay_err( dvdplay, "ifoRead_VOBU_ADMAP failed" ); return -1; } if( !ifoRead_TITLE_VOBU_ADMAP( dvdplay->p_vtsi ) ) { _dvdplay_err( dvdplay, "ifoRead_TITLE_VOBU_ADMAP failed" ); return -1; } dvdplay->state.i_vtsN = i_vtsN; dvdplay->pf_callback( dvdplay->p_args, NEW_VTS ); return 0; } /***************************************************************************** * _OpenFile: *****************************************************************************/ int _OpenFile( dvdplay_ptr dvdplay ) { int i_type; int i_vts; _dvdplay_dbg( dvdplay, "changing vob file" ); if( dvdplay->p_file ) { DVDCloseFile( dvdplay->p_file ); } switch( dvdplay->state.domain ) { case VTSM_DOMAIN: i_type = DVD_READ_MENU_VOBS; i_vts = dvdplay->state.i_vtsN; break; case VTS_DOMAIN: i_type = DVD_READ_TITLE_VOBS; i_vts = dvdplay->state.i_vtsN; break; case FP_DOMAIN: case VMGM_DOMAIN: default: i_type = DVD_READ_MENU_VOBS; i_vts = 0; break; } dvdplay->p_file = DVDOpenFile( dvdplay->p_dvdread, i_vts, i_type ); dvdplay->pf_callback( dvdplay->p_args, NEW_FILE ); return 0; } /***************************************************************************** * _MenuId2Domain: convert menu id as in dvdplay_menu_t in domain_t *****************************************************************************/ domain_t _MenuId2Domain( dvdplay_menu_t menuid ) { domain_t domain = VTSM_DOMAIN; // Really shouldn't have to.. switch( menuid ) { case TITLE_MENU: domain = VMGM_DOMAIN; break; case ROOT_MENU: case SUBPICTURE_MENU: case AUDIO_MENU: case ANGLE_MENU: case PART_MENU: domain = VTSM_DOMAIN; break; } return domain; } /***************************************************************************** * _SaveRSMinfo: store current state to be able to restore it later. ***************************************************************************** * Must be called before domain is changed (get_PGCN()). *****************************************************************************/ void _SaveRSMinfo( dvdplay_ptr dvdplay, int i_cellN, int i_blockN ) { int i; _dvdplay_dbg( dvdplay, "saving state for resume" ); if( i_cellN != 0 ) { dvdplay->resume.i_cellN = i_cellN; dvdplay->resume.i_blockN = 0; } else { dvdplay->resume.i_cellN = dvdplay->state.i_cellN; dvdplay->resume.i_blockN = i_blockN; } dvdplay->resume.i_vtsN = dvdplay->state.i_vtsN; dvdplay->resume.i_pgcN = _GetCurrentPGCN( dvdplay ); if( dvdplay->resume.i_pgcN != dvdplay->TT_PGCN_REG) { // for VTS_DOMAIN _dvdplay_warn( dvdplay, "mismatch between resume & register" ); } for( i = 0 ; i < 5 ; i++ ) { dvdplay->pi_rsm_regs[i] = dvdplay->registers.SPRM[4 + i]; } } /* * play functions: they change state according to commands, and link to * play function for sub-division. */ /***************************************************************************** * _PlayPGC: play ProgGram Chain, called after a PGC change to update state. *****************************************************************************/ int _PlayPGC( dvdplay_ptr dvdplay ) { if( dvdplay->state.domain != FP_DOMAIN ) { _dvdplay_dbg( dvdplay, "play_PGC: state.pgcN (%d)", _GetCurrentPGCN( dvdplay ) ); } else { _dvdplay_dbg( dvdplay, "play_PGC: first_play_pgc" ); } /* This must be set before the pre-commands are executed because they * might contain a CallSS that will save resume state. */ dvdplay->state.i_pgN = 1; dvdplay->state.i_cellN = 1; dvdplay->state.i_blockN = 0; /* * eval: updates the state and returns either * - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN) * - just play video i.e first PG * (This is what happens if you fall of the end of the pre_cmds) * - or a error (are there more cases?) * Note: the callback has to be called after the pre-commands. */ if( dvdplay->b_pgc_pre && dvdplay->state.p_pgc->command_tbl && dvdplay->state.p_pgc->command_tbl->nr_of_pre ) { if( _dvdplay_CommandTable( dvdplay, dvdplay->state.p_pgc->command_tbl->pre_cmds, dvdplay->state.p_pgc->command_tbl->nr_of_pre ) ) { /* warn callback only if link does not request any _playPGC */ if( dvdplay->link.command != LinkPGCN && dvdplay->link.command != LinkTopPGC && dvdplay->link.command != LinkGoUpPGC && dvdplay->link.command != LinkNextPGC && dvdplay->link.command != LinkPrevPGC && dvdplay->link.command != JumpTT && dvdplay->link.command != JumpVTS_TT && dvdplay->link.command != JumpSS_FP && dvdplay->link.command != JumpSS_VTSM && dvdplay->link.command != JumpSS_VMGM_MENU && dvdplay->link.command != JumpSS_VMGM_PGC && dvdplay->link.command != CallSS_FP && dvdplay->link.command != CallSS_VTSM && dvdplay->link.command != CallSS_VMGM_MENU && dvdplay->link.command != CallSS_VMGM_PGC ) { dvdplay->pf_callback( dvdplay->p_args, NEW_PGC ); } /* link_values contains the 'jump' return value */ return 0; } else { _dvdplay_warn( dvdplay, "PGC pre commands didn't do a Jump, " "Link or Call"); } } else { dvdplay->b_pgc_pre = 1; } dvdplay->pf_callback( dvdplay->p_args, NEW_PGC ); return _PlayPG( dvdplay ); } /***************************************************************************** * _PlayPGCpost: called after end of PGC to execute commands. *****************************************************************************/ int _PlayPGCpost( dvdplay_ptr dvdplay ) { _dvdplay_dbg( dvdplay, "play post PGC commands" ); if( dvdplay->state.p_pgc->still_time ) { /* FIXME XXX $$$ */ _dvdplay_warn( dvdplay, "positive still time during post pgc" ); } /* * 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( dvdplay->state.p_pgc->command_tbl && _dvdplay_CommandTable( dvdplay, dvdplay->state.p_pgc->command_tbl->post_cmds, dvdplay->state.p_pgc->command_tbl->nr_of_post ) ) { return 0; } else { /* Or perhaps handle it here? */ if( dvdplay->state.p_pgc->next_pgc_nr != 0 ) { link_t tmp = { LinkNextPGC, 0, 0, 0 }; _dvdplay_warn( dvdplay, "fell off the end of the pgc, " "continuing in Next PGC" ); /* Should end up in the STOP_DOMAIN if next_pgc is 0. */ memcpy( &dvdplay->link, &tmp, sizeof(link_t) ); } return 0; } } /***************************************************************************** * _PlayPG: play ProGram, called after a program chain change, or when * a new program has been requested with dvdplay_pg. *****************************************************************************/ int _PlayPG( dvdplay_ptr dvdplay ) { _dvdplay_dbg( dvdplay, "play_PG: state.pgN (%d)", dvdplay->state.i_pgN ); if( dvdplay->state.i_pgN <= 0 ) { _dvdplay_warn( dvdplay, "state pgN not positive; setting to 1" ); dvdplay->state.i_pgN = 1; } if( dvdplay->state.i_pgN > dvdplay->state.p_pgc->nr_of_programs ) { _dvdplay_warn( dvdplay, "state.pgN (%d) == pgc->nr_of_programs + 1 (%d)", dvdplay->state.i_pgN, dvdplay->state.p_pgc->nr_of_programs + 1 ); return _PlayPGCpost( dvdplay ); } dvdplay->state.i_cellN = dvdplay->state.p_pgc->program_map[dvdplay->state.i_pgN-1]; dvdplay->state.i_blockN = 0; dvdplay->pf_callback( dvdplay->p_args, NEW_PG ); return _PlayCell( dvdplay ); } #define CPB \ dvdplay->state.p_pgc->cell_playback[ dvdplay->state.i_cellN - 1 ] /***************************************************************************** * _PlayCell: enter a cell and execute pre-commands. *****************************************************************************/ int _PlayCell( dvdplay_ptr dvdplay ) { _dvdplay_dbg( dvdplay, "play_Cell: state.cellN (%d)", dvdplay->state.i_cellN ); if( dvdplay->state.i_cellN <= 0 ) { _dvdplay_warn( dvdplay, "state cellN not positive; setting to 1" ); dvdplay->state.i_cellN = 1; } if( dvdplay->state.i_cellN > dvdplay->state.p_pgc->nr_of_cells ) { _dvdplay_warn( dvdplay, "state.cellN (%d) == pgc->nr_of_cells + 1 (%d)", dvdplay->state.i_cellN, dvdplay->state.p_pgc->nr_of_cells + 1 ); return _PlayPGCpost( dvdplay ); } /* Multi angle/Interleaved */ switch( CPB.block_mode ) { case 0: // Normal assert( CPB.block_type == 0 ); break; case 1: // The first cell in the block switch( CPB.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 outside the block. */ dvdplay->state.i_cellN += dvdplay->AGL_REG - 1; assert( dvdplay->state.i_cellN <= dvdplay->state.p_pgc->nr_of_cells ); assert( CPB.block_mode != 0 ); assert( CPB.block_type == 1 ); break; case 2: // ?? case 3: // ?? default: _dvdplay_warn( dvdplay, "invalid? cell block_mode (%d), block_type (%d)", CPB.block_mode, CPB.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: _dvdplay_warn( dvdplay, "cell is in block but did not enter at first cell" ); } dvdplay->pf_callback( dvdplay->p_args, NEW_CELL ); /* Updates state.pgN and PTTN_REG */ if( _UpdatePGN( dvdplay ) ) { /* Should not happen */ link_t link_tail = { LinkTailPGC, /* No Button */ 0, 0, 0 }; memcpy( &dvdplay->link, &link_tail, sizeof(link_t) ); _dvdplay_warn( dvdplay, "last cell in PGC; linking to tail PGC" ); return 0; } else { link_t link_play = { PlayThis, /* Block in Cell */ 0, 0, 0 }; memcpy( &dvdplay->link, &link_play, sizeof(link_t) ); return 0; } } /***************************************************************************** * _PlayCellPost: update state and process command after cell end. ***************************************************************************** * Still time is already taken care of before we get called. *****************************************************************************/ int _PlayCellPost( dvdplay_ptr dvdplay ) { _dvdplay_dbg( dvdplay, "play_Cell_post: state.cellN (%d)", dvdplay->state.i_cellN ); /* Deal with a Cell command, if any */ if( CPB.cell_cmd_nr != 0 && dvdplay->state.p_pgc->command_tbl != NULL && dvdplay->state.p_pgc->command_tbl->nr_of_cell >= CPB.cell_cmd_nr ) { _dvdplay_dbg( dvdplay, "found command for cell" ); /* evaluate command for current cell */ if( _dvdplay_CommandTable( dvdplay, &dvdplay->state.p_pgc->command_tbl->cell_cmds[CPB.cell_cmd_nr - 1], 1 ) ) { return 0; } else { _dvdplay_dbg( dvdplay, "cell command didn't do a Jump, Link or Call" ); // Error ?? goto tail? goto next PG? or what? just continue? } } /* Where to continue after playing the cell... * Multi angle/Interleaved */ switch( CPB.block_mode ) { case 0: // Normal if( CPB.block_type ) { _dvdplay_warn( dvdplay, "angle block type for normal block (%d)", CPB.block_type ); } dvdplay->state.i_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( CPB.block_type ) { case 0: // Not part of a block _dvdplay_warn( dvdplay, "normal block type for angle block" ); case 1: // Angle block /* Skip the 'other' angles */ dvdplay->state.i_cellN++; while( dvdplay->state.i_cellN <= dvdplay->state.p_pgc->nr_of_cells && CPB.block_mode >= 2 ) { dvdplay->state.i_cellN++; } break; case 2: // ?? case 3: // ?? default: _dvdplay_warn( dvdplay, "invalid? Cell block_mode (%d), block_type (%d)", CPB.block_mode, CPB.block_type ); } break; } /* Figure out the correct pgN for the new cell */ if( _UpdatePGN( dvdplay ) ) { _dvdplay_dbg( dvdplay, "last cell in this PGC" ); return _PlayPGCpost( dvdplay ); } return _PlayCell( dvdplay ); } #undef CPB /* * set functions: change state to go to given PGC */ /***************************************************************************** * _SetDomain: update state domain. *****************************************************************************/ int _SetDomain( dvdplay_ptr dvdplay, domain_t domain ) { if( dvdplay->state.domain != domain ) { _dvdplay_dbg( dvdplay, "new domain" ); dvdplay->state.domain = domain; dvdplay->pf_callback( dvdplay->p_args, NEW_DOMAIN ); } return 0; } /***************************************************************************** * _SetPGC: go to ProGram Chain pgcN *****************************************************************************/ int _SetPGC( dvdplay_ptr dvdplay, int i_pgcN ) { pgcit_t * p_pgcit; _dvdplay_dbg( dvdplay, "setting PGC %d", i_pgcN ); if( ( p_pgcit = _GetPGCIT( dvdplay ) ) == NULL ) { _dvdplay_err( dvdplay, "unable to find PGC IT" ); return -1; } if( i_pgcN < 1 || i_pgcN > p_pgcit->nr_of_pgci_srp ) { _dvdplay_err( dvdplay, "pgcN out of bound" ); return -1; } dvdplay->state.i_pgcN = i_pgcN; dvdplay->state.p_pgc = p_pgcit->pgci_srp[i_pgcN - 1].pgc; if( dvdplay->state.domain == VTS_DOMAIN ) { dvdplay->TT_PGCN_REG = i_pgcN; } return 0; } /***************************************************************************** * _SetFP_PGC: go to First Play ProGram Chain *****************************************************************************/ int _SetFP_PGC( dvdplay_ptr dvdplay ) { _dvdplay_dbg( dvdplay, "setting FP PGC" ); _SetDomain( dvdplay, FP_DOMAIN ); _OpenFile ( dvdplay ); dvdplay->state.i_pgcN = 0; dvdplay->state.p_pgc = dvdplay->p_vmgi->first_play_pgc; return 0; } /***************************************************************************** * _SetTT: go to PGC for title i_tt. *****************************************************************************/ int _SetTT( dvdplay_ptr dvdplay, int i_tt ) { _dvdplay_dbg( dvdplay, "setting title %d", i_tt ); if( i_tt > 0 && i_tt <= dvdplay->p_vmgi->tt_srpt->nr_of_srpts ) { dvdplay->TTN_REG = i_tt; return _SetVTS_TT( dvdplay, dvdplay->p_vmgi->tt_srpt->title[i_tt - 1].title_set_nr, dvdplay->p_vmgi->tt_srpt->title[i_tt - 1].vts_ttn ); } _dvdplay_err( dvdplay, "invalid title %d", i_tt ); return -1; } /***************************************************************************** * _SetPTT: go to PGC for title i_tt, chapter i_ptt. *****************************************************************************/ int _SetPTT( dvdplay_ptr dvdplay, int i_tt, int i_ptt ) { _dvdplay_dbg( dvdplay, "setting title %d, part %d", i_tt, i_ptt ); if( i_tt > 0 && i_tt <= dvdplay->p_vmgi->tt_srpt->nr_of_srpts ) { dvdplay->TTN_REG = i_tt; return _SetVTS_PTT( dvdplay, dvdplay->p_vmgi->tt_srpt->title[i_tt - 1].title_set_nr, dvdplay->p_vmgi->tt_srpt->title[i_tt - 1].vts_ttn, i_ptt ); } _dvdplay_err( dvdplay, "invalid title %d", i_tt ); return -1; } /***************************************************************************** * _SetVTS_TT: go to PGC for vts_title i_vts_ttn in VTS i_vtsN ; open new * file if necessary *****************************************************************************/ int _SetVTS_TT( dvdplay_ptr dvdplay, int i_vtsN, int i_vts_ttn ) { int i_pgcN; int i_tt; _dvdplay_dbg( dvdplay, "setting VTS title %d for VTS %d", i_vts_ttn, i_vtsN ); _SetDomain( dvdplay, VTS_DOMAIN ); if( i_vtsN != dvdplay->state.i_vtsN ) { _OpenVTSI( dvdplay, i_vtsN ); // Also sets state.vtsN } _OpenFile ( dvdplay ); if( ( i_pgcN = _GetPGCNbyID( dvdplay, i_vts_ttn ) ) <= 0 ) { _dvdplay_err( dvdplay, "cannot find PGC" ); return -1; } i_tt = dvdplay->TTN_REG; if( i_vtsN != dvdplay->p_vmgi->tt_srpt->title[i_tt - 1].title_set_nr || i_vts_ttn != dvdplay->p_vmgi->tt_srpt->title[i_tt - 1].vts_ttn ) { i_tt = 1; while( i_tt <= dvdplay->p_vmgi->tt_srpt->nr_of_srpts && ( i_vtsN != dvdplay->p_vmgi->tt_srpt->title[i_tt - 1].title_set_nr || i_vts_ttn != dvdplay->p_vmgi->tt_srpt->title[i_tt - 1].vts_ttn ) ) { ++i_tt; } if( i_tt <= dvdplay->p_vmgi->tt_srpt->nr_of_srpts ) { dvdplay->TTN_REG = i_tt; } else { _dvdplay_err( dvdplay, "invalid title %d", i_tt ); } } dvdplay->VTS_TTN_REG = i_vts_ttn; /* Any other registers? */ return _SetPGC( dvdplay, i_pgcN ); } /***************************************************************************** * _SetVTS_PTT: like SetVTS_TT but set ProGram too. *****************************************************************************/ int _SetVTS_PTT( dvdplay_ptr dvdplay, int i_vtsN, int /* is this really */ i_vts_ttn, int i_part ) { int i_pgcN, i_pgN, i_tt; #define TT \ dvdplay->p_vtsi->vts_ptt_srpt->title[i_vts_ttn - 1] if( i_vts_ttn <= dvdplay->p_vtsi->vts_ptt_srpt->nr_of_srpts && i_part <= TT.nr_of_ptts ) { _SetDomain( dvdplay, VTS_DOMAIN ); _OpenVTSI ( dvdplay, i_vtsN ); // Also sets state.vtsN _OpenFile ( dvdplay ); i_pgcN = TT.ptt[i_part - 1].pgcn; i_pgN = TT.ptt[i_part - 1].pgn; i_tt = dvdplay->TTN_REG; if( i_vtsN != dvdplay->p_vmgi->tt_srpt->title[i_tt - 1].title_set_nr || i_vts_ttn != dvdplay->p_vmgi->tt_srpt->title[i_tt - 1].vts_ttn ) { i_tt = 1; while( i_tt <= dvdplay->p_vmgi->tt_srpt->nr_of_srpts && ( i_vtsN != dvdplay->p_vmgi->tt_srpt->title[i_tt - 1].title_set_nr || i_vts_ttn != dvdplay->p_vmgi->tt_srpt->title[i_tt - 1].vts_ttn ) ) { ++i_tt; } if( i_tt <= dvdplay->p_vmgi->tt_srpt->nr_of_srpts ) { dvdplay->TTN_REG = i_tt; } else { _dvdplay_err( dvdplay, "invalid title %d", i_tt ); } } dvdplay->VTS_TTN_REG = i_vts_ttn; /* Any other registers? */ dvdplay->state.i_pgcN = i_pgcN; dvdplay->state.i_pgN = i_pgN; // ?? return _SetPGC( dvdplay, i_pgcN ); } #undef TT _dvdplay_err( dvdplay, "invalid VTS_TT (%d) or PTT (%d)", i_vts_ttn, i_part ); return -1; } /***************************************************************************** * _SetMenu: go to PGC for menu id i_menu_id ***************************************************************************** * Can only be called when in VMGM_DOMAIN or VTSM_DOMAIN *****************************************************************************/ int _SetMenu( dvdplay_ptr dvdplay, int i_menu_id ) { if( dvdplay->state.domain == VMGM_DOMAIN || dvdplay->state.domain == VTSM_DOMAIN ) { return _SetPGC( dvdplay, _GetPGCNbyID( dvdplay, i_menu_id ) ); } return -1; } /***************************************************************************** * ProcessCommand: pop a link_value and execute related command. *****************************************************************************/ #define BUTTON( str ) \ _dvdplay_trace( dvdplay, str " (button %d)\n", dvdplay->link.data1 ); \ if( dvdplay->link.data1 != 0 ) \ { \ dvdplay->HL_BTNN_REG = dvdplay->link.data1 << 10; \ } #define BUTTON2( str ) \ _dvdplay_trace( dvdplay, str " %d (button %d)\n", \ dvdplay->link.data1, dvdplay->link.data2 ); \ if( dvdplay->link.data2 != 0 ) \ { \ dvdplay->HL_BTNN_REG = dvdplay->link.data2 << 10; \ } #define JUMP( str ) \ _dvdplay_trace( dvdplay, str " %d\n", dvdplay->link.data1 ); static int ProcessCommand( dvdplay_ptr dvdplay ) { int i; switch( dvdplay->link.command ) { case LinkNoLink: BUTTON( "LinkNoLink" ) _dvdplay_warn( dvdplay, "no link command" ); { link_t link_play = { PlayThis, /* Block in Cell */ 0, 0, 0 }; memcpy( &dvdplay->link, &link_play, sizeof(link_t) ); } //exit(1); /* FIXME */ break; case LinkTopC: BUTTON( "LinkTopC" ) _PlayCell( dvdplay ); break; case LinkNextC: BUTTON( "LinkNextC" ) dvdplay->state.i_cellN += 1; if( dvdplay->state.i_cellN > dvdplay->state.p_pgc->nr_of_cells ) { _dvdplay_warn( dvdplay, "link to next cell out of range" ); } _PlayCell( dvdplay ); break; case LinkPrevC: BUTTON( "LinkPrevC" ) dvdplay->state.i_cellN -= 1; if( dvdplay->state.i_cellN <= 0 ) { _dvdplay_warn( dvdplay, "link to prev cell out of range" ); } _PlayCell( dvdplay ); break; case LinkTopPG: BUTTON( "LinkTopPG" ) /* Does pgN always contain the current value? */ _PlayPG( dvdplay ); break; case LinkNextPG: BUTTON( "LinkNextPG" ) /* Does pgN always contain the current value? */ dvdplay->state.i_pgN += 1; if( dvdplay->state.i_pgN > dvdplay->state.p_pgc->nr_of_programs ) { _dvdplay_warn( dvdplay, "link to next pg out of range" ); } _PlayPG( dvdplay ); break; case LinkPrevPG: BUTTON( "LinkPrevPG" ) /* Does pgN always contain the current value? */ dvdplay->state.i_pgN -= 1; if( dvdplay->state.i_pgN <= 0 ) { _dvdplay_warn( dvdplay, "link to prev pg out of range" ); } _PlayPG( dvdplay ); break; #define LINK_PGC( type ) \ if( !dvdplay->state.p_pgc->type ) \ { \ _dvdplay_warn( dvdplay, "null pgc type in link" ); \ } \ if( _SetPGC( dvdplay, dvdplay->state.p_pgc->type ) ) \ { \ /* FIXME: what to do here ? */ \ _dvdplay_err( dvdplay, "cannot set pgc in link" ); \ } case LinkTopPGC: BUTTON( "LinkTopPGC" ) _PlayPGC( dvdplay ); break; case LinkNextPGC: BUTTON( "LinkNextPGC" ) LINK_PGC( next_pgc_nr ) _PlayPGC( dvdplay ); break; case LinkPrevPGC: BUTTON( "LinkPrevPGC" ) LINK_PGC( prev_pgc_nr ) _PlayPGC( dvdplay ); break; case LinkGoUpPGC: BUTTON( "LinkGoUpPGC" ) LINK_PGC( goup_pgc_nr ) _PlayPGC( dvdplay ); break; case LinkTailPGC: BUTTON( "LinkTailPGC" ) _PlayPGCpost( dvdplay ); break; #undef LINK_PGC case LinkRSM: /* FIXME */ /* Check and see if there is any rsm info! */ _SetDomain( dvdplay, VTS_DOMAIN ); _OpenVTSI( dvdplay, dvdplay->resume.i_vtsN ); _OpenFile ( dvdplay ); _SetPGC( dvdplay, dvdplay->resume.i_pgcN ); /* These should never be set in SystemSpace and/or MenuSpace */ /* state.TTN_REG = rsm_tt; ?? */ /* state.TT_PGCN_REG = state.rsm_pgcN; ?? */ for( i = 0 ; i < 5 ; i++ ) { dvdplay->registers.SPRM[4 + i] = dvdplay->pi_rsm_regs[i]; } BUTTON( "LinkRSM" ) if( dvdplay->resume.i_cellN == 0 ) { assert( dvdplay->state.i_cellN); // Checking if this ever happens /* assert( time/block/vobu is 0 ); */ dvdplay->state.i_pgN = 1; _PlayPG( dvdplay ); } else { /* assert( time/block/vobu is _not_ 0 ); */ /* play_Cell_at_time */ //state.pgN = ?? this gets the right value in play_Cell dvdplay->state.i_cellN = dvdplay->resume.i_cellN; dvdplay->link.command = PlayThis; dvdplay->link.data1 = dvdplay->resume.i_blockN; if( _UpdatePGN( dvdplay ) ) { /* Were at the end of the PGC, should not happen for a RSM */ //assert(0); dvdplay->link.command = LinkTailPGC; dvdplay->link.data1 = 0; /* No button */ } } break; case LinkPGCN: JUMP( "LinkPGCN" ); if( _SetPGC( dvdplay, dvdplay->link.data1 ) ) { _dvdplay_err( dvdplay, "cannot set link PGC" ); } _PlayPGC( dvdplay ); break; case LinkPTTN: if( dvdplay->state.domain != VTS_DOMAIN ) { _dvdplay_warn( dvdplay, "not in VTS_DOMAIN for link to PTT" ); } BUTTON2( "LinkPTTN" ) if( _SetVTS_PTT( dvdplay, dvdplay->state.i_vtsN, dvdplay->VTS_TTN_REG, dvdplay->link.data1 ) == -1 ) { _dvdplay_err( dvdplay, "cannot set link PTT" ); } _PlayPG( dvdplay ); break; case LinkPGN: BUTTON2( "LinkPGN" ) /* Update any other state, PTTN perhaps? */ dvdplay->state.i_pgN = dvdplay->link.data1; _PlayPG( dvdplay ); break; case LinkCN: BUTTON2( "LinkCN" ) /* Update any other state, pgN, PTTN perhaps? */ dvdplay->state.i_cellN = dvdplay->link.data1; _PlayCell( dvdplay ); break; case Exit: _dvdplay_trace( dvdplay, "Exit\n" ); /* FIXME: What should we do here?? */ break; case JumpTT: JUMP( "JumpTT" ) if( dvdplay->state.domain != VMGM_DOMAIN && dvdplay->state.domain != FP_DOMAIN ) { _dvdplay_warn( dvdplay, "jump to title from VTS*_DOMAIN" ); } if( _SetTT( dvdplay, dvdplay->link.data1 ) == -1 ) { _dvdplay_err( dvdplay, "cannot set jump TT" ); } _PlayPGC( dvdplay ); break; case JumpVTS_TT: JUMP( "JumpVTS_TT" ) if( dvdplay->state.domain != VTSM_DOMAIN && dvdplay->state.domain != VTS_DOMAIN ) { _dvdplay_warn( dvdplay, "jump to VTS title out of VTS*_DOMAIN" ); } if( _SetVTS_TT( dvdplay, dvdplay->state.i_vtsN, dvdplay->link.data1 ) == -1 ) { _dvdplay_err( dvdplay, "cannot set jump VTS_TT" ); } _PlayPGC( dvdplay ); break; case JumpVTS_PTT: _dvdplay_trace( dvdplay, "JumpVTS_PTT %d:%d\n", dvdplay->link.data1, dvdplay->link.data2 ); if( dvdplay->state.domain != VTSM_DOMAIN && dvdplay->state.domain != VTS_DOMAIN ) { _dvdplay_warn( dvdplay, "jump to VTS PTT out of VTS*_DOMAIN" ); } if( _SetVTS_PTT( dvdplay, dvdplay->state.i_vtsN, dvdplay->link.data1, dvdplay->link.data2 ) == -1 ) { _dvdplay_err( dvdplay, "cannot set jump VTS_PTT" ); } /* _SetVTS_PTT changes the PGC */ dvdplay->pf_callback( dvdplay->p_args, NEW_PGC ); _PlayPG( dvdplay ); break; case JumpSS_FP: _dvdplay_trace( dvdplay, "JumpSS_FP\n" ); if( dvdplay->state.domain != VMGM_DOMAIN && dvdplay->state.domain != VTSM_DOMAIN ) { _dvdplay_warn( dvdplay, "jump to FP PGC out of menu domain" ); } _SetFP_PGC( dvdplay ); _PlayPGC( dvdplay ); break; case JumpSS_VMGM_MENU: JUMP( "JumpSS_VMGM_MENU" ); if( dvdplay->state.domain == VTS_DOMAIN ) { _dvdplay_warn( dvdplay, "jump to manager menu from VTS domain" ); } _SetDomain( dvdplay, VMGM_DOMAIN ); _OpenFile ( dvdplay ); if( _SetMenu( dvdplay, dvdplay->link.data1 ) == -1 ) { _dvdplay_err( dvdplay, "cannot set jump menu" ); } _PlayPGC( dvdplay ); break; case JumpSS_VTSM: _dvdplay_trace( dvdplay, "JumpSS_VTSM vts %d title %d menu %d\n", dvdplay->link.data1, dvdplay->link.data2, dvdplay->link.data3 ); if( dvdplay->link.data1 == 0 ) { // 'The Fifth Element' region 2 has data1 == 0. assert( dvdplay->state.domain == VTSM_DOMAIN ); } else if( dvdplay->link.data1 == dvdplay->state.i_vtsN ) { // "Captain Scarlet & the Mysterons" has data1 == state.vtsN i VTSM assert( dvdplay->state.domain == VTSM_DOMAIN || dvdplay->state.domain == VMGM_DOMAIN || dvdplay->state.domain == FP_DOMAIN ); //?? _SetDomain( dvdplay, VTSM_DOMAIN ); _OpenFile ( dvdplay ); } else { // Normal case. assert( dvdplay->state.domain == VMGM_DOMAIN || dvdplay->state.domain == FP_DOMAIN ); //?? _SetDomain( dvdplay, VTSM_DOMAIN ); _OpenVTSI( dvdplay, dvdplay->link.data1 ); // Also sets state.vtsN _OpenFile ( dvdplay ); } // I don't really know what title is supposed to be used for. // Alien or Aliens has this != 1, I think. //assert(link_values.data2 == 1); assert( dvdplay->link.data2 != 0 ); dvdplay->VTS_TTN_REG = dvdplay->link.data2; if( _SetMenu( dvdplay, dvdplay->link.data3 ) == -1 ) { assert(0); } _PlayPGC( dvdplay ); break; case JumpSS_VMGM_PGC: JUMP( "JumpSS_VMGM_PGC" ); assert( dvdplay->state.domain == VMGM_DOMAIN || dvdplay->state.domain == VTSM_DOMAIN || dvdplay->state.domain == FP_DOMAIN ); //?? _SetDomain( dvdplay, VMGM_DOMAIN ); _OpenFile ( dvdplay ); if( _SetPGC( dvdplay, dvdplay->link.data1 ) == -1 ) { assert(0); } _PlayPGC( dvdplay ); break; case CallSS_FP: _dvdplay_trace( dvdplay, "CallSS_FP resume cell %d\n", dvdplay->link.data1 ); assert( dvdplay->state.domain == VTS_DOMAIN ); //?? /* Must be called before domain is changed */ _SaveRSMinfo( dvdplay, dvdplay->link.data1, /* dont have block info*/0 ); _SetFP_PGC( dvdplay ); _PlayPGC( dvdplay ); break; case CallSS_VMGM_MENU: _dvdplay_trace( dvdplay, "CallSS_VMGM_MENU %d resume cell %d\n", dvdplay->link.data1, dvdplay->link.data2 ); assert( dvdplay->state.domain == VTS_DOMAIN ); //?? /* Must be called before domain is changed */ _SaveRSMinfo( dvdplay, dvdplay->link.data2, /*dont have block info*/0 ); _SetDomain( dvdplay, VMGM_DOMAIN ); _OpenFile ( dvdplay ); if( _SetMenu( dvdplay, dvdplay->link.data1 ) == -1 ) { assert(0); } _PlayPGC( dvdplay ); break; case CallSS_VTSM: _dvdplay_trace( dvdplay, "CallSS_VTSM %d resume cell %d\n", dvdplay->link.data1, dvdplay->link.data2 ); assert( dvdplay->state.domain == VTS_DOMAIN ); //?? /* Must be called before domain is changed */ _SaveRSMinfo( dvdplay, dvdplay->link.data2, /* dont have block info*/0 ); _SetDomain( dvdplay, VTSM_DOMAIN ); _OpenFile ( dvdplay ); if( _SetMenu( dvdplay, dvdplay->link.data1 ) == -1 ) { assert(0); } _PlayPGC( dvdplay ); break; case CallSS_VMGM_PGC: _dvdplay_trace( dvdplay, "CallSS_VMGM_PGC %d resume cell %d\n", dvdplay->link.data1, dvdplay->link.data2 ); assert( dvdplay->state.domain == VTS_DOMAIN ); //?? /* Must be called before domain is changed */ _SaveRSMinfo( dvdplay, dvdplay->link.data2, /* dont have block info*/0 ); _SetDomain( dvdplay, VMGM_DOMAIN ); _OpenFile ( dvdplay ); if( _SetPGC( dvdplay, dvdplay->link.data1 ) == -1 ) { assert(0); } _PlayPGC( dvdplay ); break; case PlayThis: _dvdplay_trace( dvdplay, "PlayThis\n" ); /* Should never happen. */ break; } return 0; } /***************************************************************************** * _ProcessLink: update state according to link commands. *****************************************************************************/ int _ProcessLink( dvdplay_ptr dvdplay ) { _dvdplay_dbg( dvdplay, "processing link commands" ); while( dvdplay->link.command != PlayThis ) { /* fprintf(stderr, "%i %i %i %i\n", link_values.command, link_values.data1, link_values.data2, link_values.data3); */ ProcessCommand( dvdplay ); } return 0; }