MythTV  0.26-pre
vm.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2000, 2001 HÃ¥kan Hjort
00003  * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
00004  *               2002-2004 the dvdnav project
00005  *
00006  * This file is part of libdvdnav, a DVD navigation library. It is modified
00007  * from a file originally part of the Ogle DVD player.
00008  *
00009  * libdvdnav is free software; you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation; either version 2 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * libdvdnav is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License along
00020  * with libdvdnav; if not, write to the Free Software Foundation, Inc.,
00021  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00022  */
00023 
00024 #ifdef HAVE_CONFIG_H
00025 #include "config.h"
00026 #endif
00027 
00028 #include <stdio.h>
00029 #include <string.h>
00030 #include <stdlib.h>
00031 #include <unistd.h>
00032 #include <inttypes.h>
00033 #include <limits.h>
00034 #include <assert.h>
00035 #include <sys/types.h>
00036 #include <sys/stat.h>
00037 #include <sys/time.h>
00038 #include <fcntl.h>
00039 
00040 #include <dvdread/nav_types.h>
00041 #include <dvdread/ifo_types.h>
00042 #include <dvdread/ifo_read.h>
00043 #include "dvdnav/dvdnav.h"
00044 
00045 #include "decoder.h"
00046 #include "remap.h"
00047 #include "vm.h"
00048 #include "dvdnav_internal.h"
00049 
00050 #ifdef _MSC_VER
00051 #include <io.h>   /* read() */
00052 #endif /* _MSC_VER */
00053 
00054 #ifdef __OS2__
00055 #define INCL_DOS
00056 #include <os2.h>
00057 #include <io.h>     /* setmode() */
00058 #include <fcntl.h>  /* O_BINARY  */
00059 #endif
00060 
00061 #include "mythiowrapper.h"
00062 
00063 /*
00064 #define STRICT
00065 */
00066 
00067 /* Local prototypes */
00068 
00069 /* get_XYZ returns a value.
00070  * set_XYZ sets state using passed parameters.
00071  *         returns success/failure.
00072  */
00073 
00074 /* Play */
00075 static link_t play_PGC(vm_t *vm);
00076 static link_t play_PGC_PG(vm_t *vm, int pgN);
00077 static link_t play_PGC_post(vm_t *vm);
00078 static link_t play_PG(vm_t *vm);
00079 static link_t play_Cell(vm_t *vm);
00080 static link_t play_Cell_post(vm_t *vm);
00081 
00082 /* Process link - returns 1 if a hop has been performed */
00083 static int process_command(vm_t *vm,link_t link_values);
00084 
00085 /* Set */
00086 static int  set_TT(vm_t *vm, int tt);
00087 static int  set_PTT(vm_t *vm, int tt, int ptt);
00088 static int  set_VTS_TT(vm_t *vm, int vtsN, int vts_ttn);
00089 static int  set_VTS_PTT(vm_t *vm, int vtsN, int vts_ttn, int part);
00090 static int  set_PROG(vm_t *vm, int tt, int pgcn, int pgn);
00091 static int  set_VTS_PROG(vm_t *vm, int vtsN, int vts_ttn, int pgcn, int pgn);
00092 static int  set_FP_PGC(vm_t *vm);
00093 static int  set_MENU(vm_t *vm, int menu);
00094 static int  set_PGCN(vm_t *vm, int pgcN);
00095 static int  set_PGN(vm_t *vm); /* Set PGN based on (vm->state).CellN */
00096 static void set_RSMinfo(vm_t *vm, int cellN, int blockN);
00097 
00098 /* Get */
00099 static int get_TT(vm_t *vm, int vtsN, int vts_ttn);
00100 static int get_ID(vm_t *vm, int id);
00101 static int get_PGCN(vm_t *vm);
00102 
00103 static pgcit_t* get_MENU_PGCIT(vm_t *vm, ifo_handle_t *h, uint16_t lang);
00104 static pgcit_t* get_PGCIT(vm_t *vm);
00105 
00106 
00107 /* Helper functions */
00108 
00109 #ifdef TRACE
00110 static void vm_print_current_domain_state(vm_t *vm) {
00111   switch((vm->state).domain) {
00112     case VTS_DOMAIN:
00113       fprintf(MSG_OUT, "libdvdnav: Video Title Domain: -\n");
00114       break;
00115 
00116     case VTSM_DOMAIN:
00117       fprintf(MSG_OUT, "libdvdnav: Video Title Menu Domain: -\n");
00118       break;
00119 
00120     case VMGM_DOMAIN:
00121       fprintf(MSG_OUT, "libdvdnav: Video Manager Menu Domain: -\n");
00122       break;
00123 
00124     case FP_DOMAIN:
00125       fprintf(MSG_OUT, "libdvdnav: First Play Domain: -\n");
00126       break;
00127 
00128     default:
00129       fprintf(MSG_OUT, "libdvdnav: Unknown Domain: -\n");
00130       break;
00131   }
00132   fprintf(MSG_OUT, "libdvdnav: VTS:%d PGC:%d PG:%u CELL:%u BLOCK:%u VTS_TTN:%u TTN:%u TT_PGCN:%u\n",
00133                    (vm->state).vtsN,
00134                    get_PGCN(vm),
00135                    (vm->state).pgN,
00136                    (vm->state).cellN,
00137                    (vm->state).blockN,
00138                    (vm->state).VTS_TTN_REG,
00139                    (vm->state).TTN_REG,
00140                    (vm->state).TT_PGCN_REG);
00141 }
00142 #endif
00143 
00144 #ifdef __OS2__
00145 #define open os2_open
00146 
00147 static int os2_open(const char *name, int oflag)
00148 {
00149   HFILE hfile;
00150   ULONG ulAction;
00151   ULONG rc;
00152 
00153   rc = DosOpenL(name, &hfile, &ulAction, 0, FILE_NORMAL,
00154                 OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
00155                 OPEN_ACCESS_READONLY | OPEN_SHARE_DENYNONE | OPEN_FLAGS_DASD,
00156                 NULL);
00157 
00158   if(rc)
00159     return -1;
00160 
00161   setmode(hfile, O_BINARY);
00162 
00163   return (int)hfile;
00164 }
00165 #endif
00166 
00167 static void dvd_read_name(char *name, char *serial, const char *device) {
00168     /* Because we are compiling with _FILE_OFFSET_BITS=64
00169      * all off_t are 64bit.
00170      */
00171     off_t off;
00172     int fd, i;
00173     uint8_t data[DVD_VIDEO_LB_LEN];
00174 
00175     /* Read DVD name */
00176     fd = mythfile_open(device, O_RDONLY);
00177     if (fd > 0) {
00178       off = mythfile_seek( fd, 32 * (off_t) DVD_VIDEO_LB_LEN, SEEK_SET );
00179       if( off == ( 32 * (off_t) DVD_VIDEO_LB_LEN ) ) {
00180         off = mythfile_read( fd, data, DVD_VIDEO_LB_LEN );
00181         if (off == ( (off_t) DVD_VIDEO_LB_LEN )) {
00182           fprintf(MSG_OUT, "libdvdnav: DVD Title: ");
00183           for(i=25; i < 73; i++ ) {
00184             if((data[i] == 0)) break;
00185             if((data[i] > 32) && (data[i] < 127)) {
00186               fprintf(MSG_OUT, "%c", data[i]);
00187             } else {
00188               fprintf(MSG_OUT, " ");
00189             }
00190           }
00191           strncpy(name, (char*) &data[25], 48);
00192           name[48] = 0;
00193           fprintf(MSG_OUT, "\nlibdvdnav: DVD Serial Number: ");
00194           for(i=73; i < 89; i++ ) {
00195             if((data[i] == 0)) break;
00196             if((data[i] > 32) && (data[i] < 127)) {
00197               fprintf(MSG_OUT, "%c", data[i]);
00198             } else {
00199               fprintf(MSG_OUT, " ");
00200             }
00201           }
00202           strncpy(serial, (char*) &data[73], (i-73));
00203           serial[14] = 0;
00204           fprintf(MSG_OUT, "\nlibdvdnav: DVD Title (Alternative): ");
00205           for(i=89; i < 128; i++ ) {
00206             if((data[i] == 0)) break;
00207             if((data[i] > 32) && (data[i] < 127)) {
00208               fprintf(MSG_OUT, "%c", data[i]);
00209             } else {
00210               fprintf(MSG_OUT, " ");
00211             }
00212           }
00213           fprintf(MSG_OUT, "\n");
00214         } else {
00215           fprintf(MSG_OUT, "libdvdnav: Can't read name block. Probably not a DVD-ROM device.\n");
00216         }
00217       } else {
00218         fprintf(MSG_OUT, "libdvdnav: Can't seek to block %u\n", 32 );
00219       }
00220       mythfile_close(fd);
00221     } else {
00222     fprintf(MSG_OUT, "NAME OPEN FAILED\n");
00223   }
00224 }
00225 
00226 static int ifoOpenNewVTSI(vm_t *vm, dvd_reader_t *dvd, int vtsN) {
00227   if((vm->state).vtsN == vtsN) {
00228     return 1; /*  We alread have it */
00229   }
00230 
00231   if(vm->vtsi != NULL)
00232     ifoClose(vm->vtsi);
00233 
00234   vm->vtsi = ifoOpenVTSI(dvd, vtsN);
00235   if(vm->vtsi == NULL) {
00236     fprintf(MSG_OUT, "libdvdnav: ifoOpenVTSI failed\n");
00237     return 0;
00238   }
00239   if(!ifoRead_VTS_PTT_SRPT(vm->vtsi)) {
00240     fprintf(MSG_OUT, "libdvdnav: ifoRead_VTS_PTT_SRPT failed\n");
00241     return 0;
00242   }
00243   if(!ifoRead_PGCIT(vm->vtsi)) {
00244     fprintf(MSG_OUT, "libdvdnav: ifoRead_PGCIT failed\n");
00245     return 0;
00246   }
00247   if(!ifoRead_PGCI_UT(vm->vtsi)) {
00248     fprintf(MSG_OUT, "libdvdnav: ifoRead_PGCI_UT failed\n");
00249     return 0;
00250   }
00251   if(!ifoRead_VOBU_ADMAP(vm->vtsi)) {
00252     fprintf(MSG_OUT, "libdvdnav: ifoRead_VOBU_ADMAP vtsi failed\n");
00253     return 0;
00254   }
00255   if(!ifoRead_TITLE_VOBU_ADMAP(vm->vtsi)) {
00256     fprintf(MSG_OUT, "libdvdnav: ifoRead_TITLE_VOBU_ADMAP vtsi failed\n");
00257     return 0;
00258   }
00259   (vm->state).vtsN = vtsN;
00260 
00261   return 1;
00262 }
00263 
00264 
00265 /* Initialisation & Destruction */
00266 
00267 vm_t* vm_new_vm() {
00268   return (vm_t*)calloc(sizeof(vm_t), sizeof(char));
00269 }
00270 
00271 void vm_free_vm(vm_t *vm) {
00272   vm_stop(vm);
00273   free(vm);
00274 }
00275 
00276 
00277 /* IFO Access */
00278 
00279 ifo_handle_t *vm_get_vmgi(vm_t *vm) {
00280   return vm->vmgi;
00281 }
00282 
00283 ifo_handle_t *vm_get_vtsi(vm_t *vm) {
00284   return vm->vtsi;
00285 }
00286 
00287 
00288 /* Reader Access */
00289 
00290 dvd_reader_t *vm_get_dvd_reader(vm_t *vm) {
00291   return vm->dvd;
00292 }
00293 
00294 
00295 /* Basic Handling */
00296 
00297 int vm_start(vm_t *vm) {
00298   /* Set pgc to FP (First Play) pgc */
00299   set_FP_PGC(vm);
00300   process_command(vm, play_PGC(vm));
00301   return !vm->stopped;
00302 }
00303 
00304 void vm_stop(vm_t *vm) {
00305   if(vm->vmgi) {
00306     ifoClose(vm->vmgi);
00307     vm->vmgi=NULL;
00308   }
00309   if(vm->vtsi) {
00310     ifoClose(vm->vtsi);
00311     vm->vtsi=NULL;
00312   }
00313   if(vm->dvd) {
00314     DVDClose(vm->dvd);
00315     vm->dvd=NULL;
00316   }
00317   vm->stopped = 1;
00318 }
00319 
00320 int vm_reset(vm_t *vm, const char *dvdroot) {
00321   /*  Setup State */
00322   memset((vm->state).registers.SPRM, 0, sizeof((vm->state).registers.SPRM));
00323   memset((vm->state).registers.GPRM, 0, sizeof((vm->state).registers.GPRM));
00324   memset((vm->state).registers.GPRM_mode, 0, sizeof((vm->state).registers.GPRM_mode));
00325   memset((vm->state).registers.GPRM_mode, 0, sizeof((vm->state).registers.GPRM_mode));
00326   memset((vm->state).registers.GPRM_time, 0, sizeof((vm->state).registers.GPRM_time));
00327   (vm->state).registers.SPRM[0]  = ('e'<<8)|'n'; /* Player Menu Languange code */
00328   (vm->state).AST_REG            = 15;           /* 15 why? */
00329   (vm->state).SPST_REG           = 62;           /* 62 why? */
00330   (vm->state).AGL_REG            = 1;
00331   (vm->state).TTN_REG            = 1;
00332   (vm->state).VTS_TTN_REG        = 1;
00333   /* (vm->state).TT_PGCN_REG        = 0 */
00334   (vm->state).PTTN_REG           = 1;
00335   (vm->state).HL_BTNN_REG        = 1 << 10;
00336   (vm->state).PTL_REG            = 15;           /* Parental Level */
00337   (vm->state).registers.SPRM[12] = ('U'<<8)|'S'; /* Parental Management Country Code */
00338   (vm->state).registers.SPRM[16] = ('e'<<8)|'n'; /* Initial Language Code for Audio */
00339   (vm->state).registers.SPRM[18] = ('e'<<8)|'n'; /* Initial Language Code for Spu */
00340   (vm->state).registers.SPRM[20] = 0x1;          /* Player Regional Code Mask. Region free! */
00341   (vm->state).registers.SPRM[14] = 0x100;        /* Try Pan&Scan */
00342 
00343   (vm->state).pgN                = 0;
00344   (vm->state).cellN              = 0;
00345   (vm->state).cell_restart       = 0;
00346 
00347   (vm->state).domain             = FP_DOMAIN;
00348   (vm->state).rsm_vtsN           = 0;
00349   (vm->state).rsm_cellN          = 0;
00350   (vm->state).rsm_blockN         = 0;
00351 
00352   (vm->state).vtsN               = -1;
00353 
00354   if (vm->dvd && dvdroot) {
00355     /* a new dvd device has been requested */
00356     vm_stop(vm);
00357   }
00358   if (!vm->dvd) {
00359     vm->dvd = DVDOpen(dvdroot);
00360     if(!vm->dvd) {
00361       fprintf(MSG_OUT, "libdvdnav: vm: failed to open/read the DVD\n");
00362       return 0;
00363     }
00364     vm->vmgi = ifoOpenVMGI(vm->dvd);
00365     if(!vm->vmgi) {
00366       fprintf(MSG_OUT, "libdvdnav: vm: failed to read VIDEO_TS.IFO\n");
00367       return 0;
00368     }
00369     if(!ifoRead_FP_PGC(vm->vmgi)) {
00370       fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_FP_PGC failed\n");
00371       return 0;
00372     }
00373     if(!ifoRead_TT_SRPT(vm->vmgi)) {
00374       fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_TT_SRPT failed\n");
00375       return 0;
00376     }
00377     if(!ifoRead_PGCI_UT(vm->vmgi)) {
00378       fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_PGCI_UT failed\n");
00379       return 0;
00380     }
00381     if(!ifoRead_PTL_MAIT(vm->vmgi)) {
00382       fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_PTL_MAIT failed\n");
00383       /* return 0; Not really used for now.. */
00384     }
00385     if(!ifoRead_VTS_ATRT(vm->vmgi)) {
00386       fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_VTS_ATRT failed\n");
00387       /* return 0; Not really used for now.. */
00388     }
00389     if(!ifoRead_VOBU_ADMAP(vm->vmgi)) {
00390       fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_VOBU_ADMAP vgmi failed\n");
00391       /* return 0; Not really used for now.. */
00392     }
00393     /* ifoRead_TXTDT_MGI(vmgi); Not implemented yet */
00394     dvd_read_name(vm->dvd_name, vm->dvd_serial, dvdroot);
00395     vm->map  = remap_loadmap(vm->dvd_name);
00396   }
00397   if (vm->vmgi) {
00398     int i, mask;
00399     fprintf(MSG_OUT, "libdvdnav: DVD disk reports itself with Region mask 0x%08x. Regions:",
00400       vm->vmgi->vmgi_mat->vmg_category);
00401     for (i = 1, mask = 1; i <= 8; i++, mask <<= 1)
00402       if (((vm->vmgi->vmgi_mat->vmg_category >> 16) & mask) == 0)
00403         fprintf(MSG_OUT, " %d", i);
00404     fprintf(MSG_OUT, "\n");
00405   }
00406   return 1;
00407 }
00408 
00409 
00410 /* copying and merging */
00411 
00412 vm_t *vm_new_copy(vm_t *source) {
00413   vm_t *target = vm_new_vm();
00414   int vtsN;
00415   int pgcN = get_PGCN(source);
00416   int pgN  = (source->state).pgN;
00417 
00418   assert(pgcN);
00419 
00420   memcpy(target, source, sizeof(vm_t));
00421 
00422   /* open a new vtsi handle, because the copy might switch to another VTS */
00423   target->vtsi = NULL;
00424   vtsN = (target->state).vtsN;
00425   if (vtsN > 0) {
00426     (target->state).vtsN = 0;
00427     if (!ifoOpenNewVTSI(target, target->dvd, vtsN))
00428       assert(0);
00429 
00430     /* restore pgc pointer into the new vtsi */
00431     if (!set_PGCN(target, pgcN))
00432       assert(0);
00433     (target->state).pgN = pgN;
00434   }
00435 
00436   return target;
00437 }
00438 
00439 void vm_merge(vm_t *target, vm_t *source) {
00440   if(target->vtsi)
00441     ifoClose(target->vtsi);
00442   memcpy(target, source, sizeof(vm_t));
00443   memset(source, 0, sizeof(vm_t));
00444 }
00445 
00446 void vm_free_copy(vm_t *vm) {
00447   if(vm->vtsi)
00448     ifoClose(vm->vtsi);
00449   free(vm);
00450 }
00451 
00452 
00453 /* regular playback */
00454 
00455 void vm_position_get(vm_t *vm, vm_position_t *position) {
00456   position->button = (vm->state).HL_BTNN_REG >> 10;
00457   position->vts = (vm->state).vtsN;
00458   position->domain = (vm->state).domain;
00459   position->spu_channel = (vm->state).SPST_REG;
00460   position->audio_channel = (vm->state).AST_REG;
00461   position->angle_channel = (vm->state).AGL_REG;
00462   position->hop_channel = vm->hop_channel; /* Increases by one on each hop */
00463   position->cell = (vm->state).cellN;
00464   position->cell_restart = (vm->state).cell_restart;
00465   position->cell_start = (vm->state).pgc->cell_playback[(vm->state).cellN - 1].first_sector;
00466   position->still = (vm->state).pgc->cell_playback[(vm->state).cellN - 1].still_time;
00467   position->block = (vm->state).blockN;
00468 
00469   /* handle PGC stills at PGC end */
00470   if ((vm->state).cellN == (vm->state).pgc->nr_of_cells)
00471     position->still += (vm->state).pgc->still_time;
00472   /* still already determined */
00473   if (position->still)
00474     return;
00475   /* This is a rough fix for some strange still situations on some strange DVDs.
00476    * There are discs (like the German "Back to the Future" RC2) where the only
00477    * indication of a still is a cell playback time higher than the time the frames
00478    * in this cell actually take to play (like 1 frame with 1 minute playback time).
00479    * On the said BTTF disc, for these cells last_sector and last_vobu_start_sector
00480    * are equal and the cells are very short, so we abuse these conditions to
00481    * detect such discs. I consider these discs broken, so the fix is somewhat
00482    * broken, too. */
00483   if (((vm->state).pgc->cell_playback[(vm->state).cellN - 1].last_sector ==
00484        (vm->state).pgc->cell_playback[(vm->state).cellN - 1].last_vobu_start_sector) &&
00485       ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].last_sector -
00486        (vm->state).pgc->cell_playback[(vm->state).cellN - 1].first_sector < 1024)) {
00487     int time;
00488     int size = (vm->state).pgc->cell_playback[(vm->state).cellN - 1].last_sector -
00489                (vm->state).pgc->cell_playback[(vm->state).cellN - 1].first_sector;
00490     time  = ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.hour   >> 4  ) * 36000;
00491     time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.hour   & 0x0f) * 3600;
00492     time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.minute >> 4  ) * 600;
00493     time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.minute & 0x0f) * 60;
00494     time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.second >> 4  ) * 10;
00495     time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.second & 0x0f) * 1;
00496     if (!time || size / time > 30)
00497       /* datarate is too high, it might be a very short, but regular cell */
00498       return;
00499     if (time > 0xff) time = 0xff;
00500     position->still = time;
00501   }
00502 }
00503 
00504 void vm_get_next_cell(vm_t *vm) {
00505   process_command(vm, play_Cell_post(vm));
00506 }
00507 
00508 
00509 /* Jumping */
00510 
00511 int vm_jump_pg(vm_t *vm, int pg) {
00512   (vm->state).pgN = pg;
00513   process_command(vm, play_PG(vm));
00514   return 1;
00515 }
00516 
00517 int vm_jump_cell_block(vm_t *vm, int cell, int block) {
00518   (vm->state).cellN = cell;
00519   process_command(vm, play_Cell(vm));
00520   /* play_Cell can jump to a different cell in case of angles */
00521   if ((vm->state).cellN == cell)
00522     (vm->state).blockN = block;
00523   return 1;
00524 }
00525 
00526 int vm_jump_title_program(vm_t *vm, int title, int pgcn, int pgn) {
00527   link_t link;
00528 
00529   if(!set_PROG(vm, title, pgcn, pgn))
00530     return 0;
00531   /* Some DVDs do not want us to jump directly into a title and have
00532    * PGC pre commands taking us back to some menu. Since we do not like that,
00533    * we do not execute PGC pre commands that would do a jump. */
00534   /* process_command(vm, play_PGC_PG(vm, (vm->state).pgN)); */
00535   link = play_PGC_PG(vm, (vm->state).pgN);
00536   if (link.command != PlayThis)
00537     /* jump occured -> ignore it and play the PG anyway */
00538     process_command(vm, play_PG(vm));
00539   else
00540     process_command(vm, link);
00541   return 1;
00542 }
00543 
00544 int vm_jump_title_part(vm_t *vm, int title, int part) {
00545   link_t link;
00546 
00547   if(!set_PTT(vm, title, part))
00548     return 0;
00549   /* Some DVDs do not want us to jump directly into a title and have
00550    * PGC pre commands taking us back to some menu. Since we do not like that,
00551    * we do not execute PGC pre commands that would do a jump. */
00552   /* process_command(vm, play_PGC_PG(vm, (vm->state).pgN)); */
00553   link = play_PGC_PG(vm, (vm->state).pgN);
00554   if (link.command != PlayThis)
00555     /* jump occured -> ignore it and play the PG anyway */
00556     process_command(vm, play_PG(vm));
00557   else
00558     process_command(vm, link);
00559   return 1;
00560 }
00561 
00562 int vm_jump_top_pg(vm_t *vm) {
00563   process_command(vm, play_PG(vm));
00564   return 1;
00565 }
00566 
00567 int vm_jump_next_pg(vm_t *vm) {
00568   if((vm->state).pgN >= (vm->state).pgc->nr_of_programs) {
00569     /* last program -> move to TailPGC */
00570     process_command(vm, play_PGC_post(vm));
00571     return 1;
00572   } else {
00573     vm_jump_pg(vm, (vm->state).pgN + 1);
00574     return 1;
00575   }
00576 }
00577 
00578 int vm_jump_prev_pg(vm_t *vm) {
00579   if ((vm->state).pgN <= 1) {
00580     /* first program -> move to last program of previous PGC */
00581     if ((vm->state).pgc->prev_pgc_nr && set_PGCN(vm, (vm->state).pgc->prev_pgc_nr)) {
00582       process_command(vm, play_PGC(vm));
00583       vm_jump_pg(vm, (vm->state).pgc->nr_of_programs);
00584       return 1;
00585     }
00586     return 0;
00587   } else {
00588     vm_jump_pg(vm, (vm->state).pgN - 1);
00589     return 1;
00590   }
00591 }
00592 
00593 int vm_jump_up(vm_t *vm) {
00594   if((vm->state).pgc->goup_pgc_nr && set_PGCN(vm, (vm->state).pgc->goup_pgc_nr)) {
00595     process_command(vm, play_PGC(vm));
00596     return 1;
00597   }
00598   return 0;
00599 }
00600 
00601 int vm_jump_menu(vm_t *vm, DVDMenuID_t menuid) {
00602   domain_t old_domain = (vm->state).domain;
00603 
00604   switch ((vm->state).domain) {
00605   case VTS_DOMAIN:
00606     set_RSMinfo(vm, 0, (vm->state).blockN);
00607     /* FALL THROUGH */
00608   case VTSM_DOMAIN:
00609   case VMGM_DOMAIN:
00610     switch(menuid) {
00611     case DVD_MENU_Title:
00612     case DVD_MENU_Escape:
00613       if(vm->vmgi == NULL || vm->vmgi->pgci_ut == NULL) {
00614         return 0;
00615       }
00616       (vm->state).domain = VMGM_DOMAIN;
00617       break;
00618     case DVD_MENU_Root:
00619     case DVD_MENU_Subpicture:
00620     case DVD_MENU_Audio:
00621     case DVD_MENU_Angle:
00622     case DVD_MENU_Part:
00623       if(vm->vtsi == NULL || vm->vtsi->pgci_ut == NULL) {
00624         return 0;
00625       }
00626       (vm->state).domain = VTSM_DOMAIN;
00627       break;
00628     }
00629     if(get_PGCIT(vm) && set_MENU(vm, menuid)) {
00630       process_command(vm, play_PGC(vm));
00631       return 1;  /* Jump */
00632     } else {
00633       (vm->state).domain = old_domain;
00634     }
00635     break;
00636   case FP_DOMAIN: /* FIXME XXX $$$ What should we do here? */
00637     break;
00638   }
00639 
00640   return 0;
00641 }
00642 
00643 int vm_jump_resume(vm_t *vm) {
00644   link_t link_values = { LinkRSM, 0, 0, 0 };
00645 
00646   if (!(vm->state).rsm_vtsN) /* Do we have resume info? */
00647     return 0;
00648   if (!process_command(vm, link_values))
00649     return 0;
00650   return 1;
00651 }
00652 
00653 int vm_exec_cmd(vm_t *vm, vm_cmd_t *cmd) {
00654   link_t link_values;
00655 
00656   if(vmEval_CMD(cmd, 1, &(vm->state).registers, &link_values))
00657     return process_command(vm, link_values);
00658   else
00659     return 0; /*  It updated some state thats all... */
00660 }
00661 
00662 
00663 /* getting information */
00664 
00665 int vm_get_current_menu(vm_t *vm, int *menuid) {
00666   pgcit_t* pgcit;
00667   int pgcn;
00668   pgcn = (vm->state).pgcN;
00669   pgcit = get_PGCIT(vm);
00670   if(pgcit==NULL) return 0;
00671   *menuid = pgcit->pgci_srp[pgcn - 1].entry_id & 0xf ;
00672   return 1;
00673 }
00674 
00675 int vm_get_current_title_part(vm_t *vm, int *title_result, int *part_result) {
00676   vts_ptt_srpt_t *vts_ptt_srpt;
00677   int title, part = 0, vts_ttn;
00678   int found;
00679   int16_t pgcN, pgN;
00680 
00681   vts_ptt_srpt = vm->vtsi->vts_ptt_srpt;
00682   pgcN = get_PGCN(vm);
00683   pgN = vm->state.pgN;
00684 
00685   found = 0;
00686   for (vts_ttn = 0; (vts_ttn < vts_ptt_srpt->nr_of_srpts) && !found; vts_ttn++) {
00687     for (part = 0; (part < vts_ptt_srpt->title[vts_ttn].nr_of_ptts) && !found; part++) {
00688       if (vts_ptt_srpt->title[vts_ttn].ptt[part].pgcn == pgcN) {
00689         if (vts_ptt_srpt->title[vts_ttn].ptt[part].pgn  == pgN) {
00690           found = 1;
00691           break;
00692         }
00693         if (part > 0 && vts_ptt_srpt->title[vts_ttn].ptt[part].pgn > pgN &&
00694             vts_ptt_srpt->title[vts_ttn].ptt[part - 1].pgn < pgN) {
00695           part--;
00696           found = 1;
00697           break;
00698         }
00699       }
00700     }
00701     if (found) break;
00702   }
00703   vts_ttn++;
00704   part++;
00705 
00706   if (!found) {
00707     fprintf(MSG_OUT, "libdvdnav: chapter NOT FOUND!\n");
00708     return 0;
00709   }
00710 
00711   title = get_TT(vm, vm->state.vtsN, vts_ttn);
00712 
00713 #ifdef TRACE
00714   if (title) {
00715     fprintf(MSG_OUT, "libdvdnav: ************ this chapter FOUND!\n");
00716     fprintf(MSG_OUT, "libdvdnav: VTS_PTT_SRPT - Title %3i part %3i: PGC: %3i PG: %3i\n",
00717              title, part,
00718              vts_ptt_srpt->title[vts_ttn-1].ptt[part-1].pgcn ,
00719              vts_ptt_srpt->title[vts_ttn-1].ptt[part-1].pgn );
00720   }
00721 #endif
00722   *title_result = title;
00723   *part_result = part;
00724   return 1;
00725 }
00726 
00727 /* Return the substream id for 'logical' audio stream audioN.
00728  * 0 <= audioN < 8
00729  */
00730 int vm_get_audio_stream(vm_t *vm, int audioN) {
00731   int streamN = -1;
00732   const uint AC3_OFFSET  = 0x80;
00733   const uint DTS_OFFSET  = 0x88;
00734   const uint LPCM_OFFSET = 0xA0;
00735   const uint MP2_OFFSET  = 0xC0;
00736 
00737   int stream_id = audioN;
00738   if (stream_id >= MP2_OFFSET) {
00739     stream_id -= MP2_OFFSET;
00740   } else if (stream_id >= LPCM_OFFSET) {
00741     stream_id -= LPCM_OFFSET;
00742   } else if (stream_id >= DTS_OFFSET) {
00743     stream_id -= DTS_OFFSET;
00744   } else if (stream_id >= AC3_OFFSET) {
00745     stream_id -= AC3_OFFSET;
00746   }
00747    
00748   if((vm->state).domain != VTS_DOMAIN)
00749     stream_id = 0;
00750 
00751   if(stream_id < 8) {
00752     /* Is there any control info for this logical stream */
00753     if((vm->state).pgc->audio_control[stream_id] & (1<<15)) {
00754       streamN = ((vm->state).pgc->audio_control[stream_id] >> 8) & 0x07;
00755     }
00756   }
00757 
00758   if((vm->state).domain != VTS_DOMAIN && streamN == -1)
00759     streamN = 0;
00760 
00761   return streamN;
00762 }
00763 
00764 /* Return the substream id for 'logical' subpicture stream subpN and given mode.
00765  * 0 <= subpN < 32
00766  * mode == 0 - widescreen
00767  * mode == 1 - letterbox
00768  * mode == 2 - pan&scan
00769  */
00770 int vm_get_subp_stream(vm_t *vm, int subpN, int mode) {
00771   int streamN = -1;
00772   int source_aspect = vm_get_video_aspect(vm);
00773 
00774   if((vm->state).domain != VTS_DOMAIN)
00775     subpN = 0;
00776 
00777   if(subpN < 32) { /* a valid logical stream */
00778     /* Is this logical stream present */
00779     if((vm->state).pgc->subp_control[subpN] & (1<<31)) {
00780       if(source_aspect == 0) /* 4:3 */
00781         streamN = ((vm->state).pgc->subp_control[subpN] >> 24) & 0x1f;
00782       if(source_aspect == 3) /* 16:9 */
00783         switch (mode) {
00784         case 0:
00785           streamN = ((vm->state).pgc->subp_control[subpN] >> 16) & 0x1f;
00786           break;
00787         case 1:
00788           streamN = ((vm->state).pgc->subp_control[subpN] >> 8) & 0x1f;
00789           break;
00790         case 2:
00791           streamN = (vm->state).pgc->subp_control[subpN] & 0x1f;
00792         }
00793     }
00794   }
00795 
00796   if((vm->state).domain != VTS_DOMAIN && streamN == -1)
00797     streamN = 0;
00798 
00799   /* FIXME: Should also check in vtsi/vmgi status what kind of stream it is. */
00800   return streamN;
00801 }
00802 
00803 int vm_get_audio_active_stream(vm_t *vm) {
00804   int audioN;
00805   int streamN;
00806   audioN = (vm->state).AST_REG ;
00807   streamN = vm_get_audio_stream(vm, audioN);
00808 
00809   /* If no such stream, then select the first one that exists. */
00810   if(streamN == -1) {
00811     for(audioN = 0; audioN < 8; audioN++) {
00812       if((vm->state).pgc->audio_control[audioN] & (1<<15)) {
00813         if ((streamN = vm_get_audio_stream(vm, audioN)) >= 0)
00814           break;
00815       }
00816     }
00817   }
00818 
00819   return streamN;
00820 }
00821 
00822 int vm_set_audio_active_stream(vm_t *vm, int audioN) {
00823 
00824     if (audioN >= 8)
00825       return -1;
00826 
00827     /* verify that stream exists */
00828     if(! (vm->state).pgc->audio_control[audioN] & (1<<15))
00829         return -1;
00830 
00831     (vm->state).AST_REG = audioN;
00832     return 0;
00833 }
00834 
00835 int vm_get_subp_active_stream(vm_t *vm, int mode) {
00836   int subpN;
00837   int streamN;
00838   subpN = (vm->state).SPST_REG & ~0x40;
00839   streamN = vm_get_subp_stream(vm, subpN, mode);
00840 
00841   /* If no such stream, then select the first one that exists. */
00842   if(streamN == -1) {
00843     for(subpN = 0; subpN < 32; subpN++) {
00844       if((vm->state).pgc->subp_control[subpN] & (1<<31)) {
00845         if ((streamN = vm_get_subp_stream(vm, subpN, mode)) >= 0)
00846           break;
00847       }
00848     }
00849   }
00850 
00851   if((vm->state).domain == VTS_DOMAIN && !((vm->state).SPST_REG & 0x40))
00852     /* Bit 7 set means hide, and only let Forced display show */
00853     return (streamN | 0x80);
00854   else
00855     return streamN;
00856 }
00857 
00858 void vm_get_angle_info(vm_t *vm, int *current, int *num_avail) {
00859   *num_avail = 1;
00860   *current = 1;
00861 
00862   if((vm->state).domain == VTS_DOMAIN) {
00863     title_info_t *title;
00864     /* TTN_REG does not allways point to the correct title.. */
00865     if((vm->state).TTN_REG > vm->vmgi->tt_srpt->nr_of_srpts)
00866       return;
00867     title = &vm->vmgi->tt_srpt->title[(vm->state).TTN_REG - 1];
00868     if(title->title_set_nr != (vm->state).vtsN ||
00869        title->vts_ttn != (vm->state).VTS_TTN_REG)
00870       return;
00871     *num_avail = title->nr_of_angles;
00872     *current = (vm->state).AGL_REG;
00873   }
00874 }
00875 
00876 #if 0
00877 /* currently unused */
00878 void vm_get_audio_info(vm_t *vm, int *current, int *num_avail) {
00879   switch ((vm->state).domain) {
00880   case VTS_DOMAIN:
00881     *num_avail = vm->vtsi->vtsi_mat->nr_of_vts_audio_streams;
00882     *current = (vm->state).AST_REG;
00883     break;
00884   case VTSM_DOMAIN:
00885     *num_avail = vm->vtsi->vtsi_mat->nr_of_vtsm_audio_streams; /*  1 */
00886     *current = 1;
00887     break;
00888   case VMGM_DOMAIN:
00889   case FP_DOMAIN:
00890     *num_avail = vm->vmgi->vmgi_mat->nr_of_vmgm_audio_streams; /*  1 */
00891     *current = 1;
00892     break;
00893   }
00894 }
00895 
00896 /* currently unused */
00897 void vm_get_subp_info(vm_t *vm, int *current, int *num_avail) {
00898   switch ((vm->state).domain) {
00899   case VTS_DOMAIN:
00900     *num_avail = vm->vtsi->vtsi_mat->nr_of_vts_subp_streams;
00901     *current = (vm->state).SPST_REG;
00902     break;
00903   case VTSM_DOMAIN:
00904     *num_avail = vm->vtsi->vtsi_mat->nr_of_vtsm_subp_streams; /*  1 */
00905     *current = 0x41;
00906     break;
00907   case VMGM_DOMAIN:
00908   case FP_DOMAIN:
00909     *num_avail = vm->vmgi->vmgi_mat->nr_of_vmgm_subp_streams; /*  1 */
00910     *current = 0x41;
00911     break;
00912   }
00913 }
00914 #endif
00915 
00916 void vm_get_video_res(vm_t *vm, int *width, int *height) {
00917   video_attr_t attr = vm_get_video_attr(vm);
00918 
00919   if(attr.video_format != 0)
00920     *height = 576;
00921   else
00922     *height = 480;
00923   switch(attr.picture_size) {
00924   case 0:
00925     *width = 720;
00926     break;
00927   case 1:
00928     *width = 704;
00929     break;
00930   case 2:
00931     *width = 352;
00932     break;
00933   case 3:
00934     *width = 352;
00935     *height /= 2;
00936     break;
00937   }
00938 }
00939 
00940 int vm_get_video_aspect(vm_t *vm) {
00941   int aspect = vm_get_video_attr(vm).display_aspect_ratio;
00942 
00943   assert(aspect == 0 || aspect == 3);
00944   (vm->state).registers.SPRM[14] &= ~(0x3 << 10);
00945   (vm->state).registers.SPRM[14] |= aspect << 10;
00946 
00947   return aspect;
00948 }
00949 
00950 int vm_get_video_scale_permission(vm_t *vm) {
00951   return vm_get_video_attr(vm).permitted_df;
00952 }
00953 
00954 int vm_get_video_format(vm_t *vm) {
00955   return vm_get_video_attr(vm).video_format;
00956 }
00957 
00958 video_attr_t vm_get_video_attr(vm_t *vm) {
00959   switch ((vm->state).domain) {
00960   case VTS_DOMAIN:
00961     return vm->vtsi->vtsi_mat->vts_video_attr;
00962   case VTSM_DOMAIN:
00963     return vm->vtsi->vtsi_mat->vtsm_video_attr;
00964   case VMGM_DOMAIN:
00965   case FP_DOMAIN:
00966     return vm->vmgi->vmgi_mat->vmgm_video_attr;
00967   default:
00968     abort();
00969   }
00970 }
00971 
00972 audio_attr_t vm_get_audio_attr(vm_t *vm, int streamN) {
00973   switch ((vm->state).domain) {
00974   case VTS_DOMAIN:
00975     return vm->vtsi->vtsi_mat->vts_audio_attr[streamN];
00976   case VTSM_DOMAIN:
00977     return vm->vtsi->vtsi_mat->vtsm_audio_attr;
00978   case VMGM_DOMAIN:
00979   case FP_DOMAIN:
00980     return vm->vmgi->vmgi_mat->vmgm_audio_attr;
00981   default:
00982     abort();
00983   }
00984 }
00985 
00986 subp_attr_t vm_get_subp_attr(vm_t *vm, int streamN) {
00987   switch ((vm->state).domain) {
00988   case VTS_DOMAIN:
00989     return vm->vtsi->vtsi_mat->vts_subp_attr[streamN];
00990   case VTSM_DOMAIN:
00991     return vm->vtsi->vtsi_mat->vtsm_subp_attr;
00992   case VMGM_DOMAIN:
00993   case FP_DOMAIN:
00994     return vm->vmgi->vmgi_mat->vmgm_subp_attr;
00995   default:
00996     abort();
00997   }
00998 }
00999 
01000 
01001 /* Playback control */
01002 
01003 static link_t play_PGC(vm_t *vm) {
01004   link_t link_values;
01005 
01006 #ifdef TRACE
01007   fprintf(MSG_OUT, "libdvdnav: play_PGC:");
01008   if((vm->state).domain != FP_DOMAIN) {
01009     fprintf(MSG_OUT, " (vm->state).pgcN (%i)\n", get_PGCN(vm));
01010   } else {
01011     fprintf(MSG_OUT, " first_play_pgc\n");
01012   }
01013 #endif
01014 
01015   /* This must be set before the pre-commands are executed because they
01016    * might contain a CallSS that will save resume state */
01017 
01018   /* FIXME: This may be only a temporary fix for something... */
01019   (vm->state).pgN = 1;
01020   (vm->state).cellN = 0;
01021   (vm->state).blockN = 0;
01022 
01023   /* eval -> updates the state and returns either
01024      - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN)
01025      - just play video i.e first PG
01026        (This is what happens if you fall of the end of the pre_cmds)
01027      - or an error (are there more cases?) */
01028   if((vm->state).pgc->command_tbl && (vm->state).pgc->command_tbl->nr_of_pre) {
01029     if(vmEval_CMD((vm->state).pgc->command_tbl->pre_cmds,
01030                   (vm->state).pgc->command_tbl->nr_of_pre,
01031                   &(vm->state).registers, &link_values)) {
01032       /*  link_values contains the 'jump' return value */
01033       return link_values;
01034     } else {
01035 #ifdef TRACE
01036       fprintf(MSG_OUT, "libdvdnav: PGC pre commands didn't do a Jump, Link or Call\n");
01037 #endif
01038     }
01039   }
01040   return play_PG(vm);
01041 }
01042 
01043 static link_t play_PGC_PG(vm_t *vm, int pgN) {
01044   link_t link_values;
01045 
01046 #ifdef TRACE
01047   fprintf(MSG_OUT, "libdvdnav: play_PGC_PG:");
01048   if((vm->state).domain != FP_DOMAIN) {
01049     fprintf(MSG_OUT, " (vm->state).pgcN (%i)\n", get_PGCN(vm));
01050   } else {
01051     fprintf(MSG_OUT, " first_play_pgc\n");
01052   }
01053 #endif
01054 
01055   /*  This must be set before the pre-commands are executed because they
01056    *  might contain a CallSS that will save resume state */
01057 
01058   /* FIXME: This may be only a temporary fix for something... */
01059   (vm->state).pgN = pgN;
01060   (vm->state).cellN = 0;
01061   (vm->state).blockN = 0;
01062 
01063   /* eval -> updates the state and returns either
01064      - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN)
01065      - just play video i.e first PG
01066        (This is what happens if you fall of the end of the pre_cmds)
01067      - or an error (are there more cases?) */
01068   if((vm->state).pgc->command_tbl && (vm->state).pgc->command_tbl->nr_of_pre) {
01069     if(vmEval_CMD((vm->state).pgc->command_tbl->pre_cmds,
01070                   (vm->state).pgc->command_tbl->nr_of_pre,
01071                   &(vm->state).registers, &link_values)) {
01072       /*  link_values contains the 'jump' return value */
01073       return link_values;
01074     } else {
01075 #ifdef TRACE
01076       fprintf(MSG_OUT, "libdvdnav: PGC pre commands didn't do a Jump, Link or Call\n");
01077 #endif
01078     }
01079   }
01080   return play_PG(vm);
01081 }
01082 
01083 static link_t play_PGC_post(vm_t *vm) {
01084   link_t link_values;
01085 
01086 #ifdef TRACE
01087   fprintf(MSG_OUT, "libdvdnav: play_PGC_post:\n");
01088 #endif
01089 
01090   /* eval -> updates the state and returns either
01091      - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN)
01092      - just go to next PGC
01093        (This is what happens if you fall of the end of the post_cmds)
01094      - or an error (are there more cases?) */
01095   if((vm->state).pgc->command_tbl && (vm->state).pgc->command_tbl->nr_of_post &&
01096      vmEval_CMD((vm->state).pgc->command_tbl->post_cmds,
01097                 (vm->state).pgc->command_tbl->nr_of_post,
01098                 &(vm->state).registers, &link_values)) {
01099     return link_values;
01100   }
01101 
01102 #ifdef TRACE
01103   fprintf(MSG_OUT, "libdvdnav: ** Fell of the end of the pgc, continuing in NextPGC\n");
01104 #endif
01105   /* Should end up in the STOP_DOMAIN if next_pgc is 0. */
01106   if(!set_PGCN(vm, (vm->state).pgc->next_pgc_nr)) {
01107     link_values.command = Exit;
01108     link_values.data1 = link_values.data2 = link_values.data3 = 0;
01109     return link_values;
01110   }
01111   return play_PGC(vm);
01112 }
01113 
01114 static link_t play_PG(vm_t *vm) {
01115 #ifdef TRACE
01116   fprintf(MSG_OUT, "libdvdnav: play_PG: (vm->state).pgN (%i)\n", (vm->state).pgN);
01117 #endif
01118 
01119   assert((vm->state).pgN > 0);
01120   if((vm->state).pgN > (vm->state).pgc->nr_of_programs) {
01121 #ifdef TRACE
01122     fprintf(MSG_OUT, "libdvdnav: play_PG: (vm->state).pgN (%i) > pgc->nr_of_programs (%i)\n",
01123             (vm->state).pgN, (vm->state).pgc->nr_of_programs );
01124 #endif
01125     assert((vm->state).pgN == (vm->state).pgc->nr_of_programs + 1);
01126     return play_PGC_post(vm);
01127   }
01128 
01129   (vm->state).cellN = (vm->state).pgc->program_map[(vm->state).pgN - 1];
01130 
01131   return play_Cell(vm);
01132 }
01133 
01134 static link_t play_Cell(vm_t *vm) {
01135   static const link_t play_this = {PlayThis, /* Block in Cell */ 0, 0, 0};
01136 
01137 #ifdef TRACE
01138   fprintf(MSG_OUT, "libdvdnav: play_Cell: (vm->state).cellN (%i)\n", (vm->state).cellN);
01139 #endif
01140 
01141   assert((vm->state).cellN > 0);
01142   if((vm->state).cellN > (vm->state).pgc->nr_of_cells) {
01143 #ifdef TRACE
01144     fprintf(MSG_OUT, "libdvdnav: (vm->state).cellN (%i) > pgc->nr_of_cells (%i)\n",
01145             (vm->state).cellN, (vm->state).pgc->nr_of_cells );
01146 #endif
01147     assert((vm->state).cellN == (vm->state).pgc->nr_of_cells + 1);
01148     return play_PGC_post(vm);
01149   }
01150 
01151   /* Multi angle/Interleaved */
01152   switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode) {
01153   case 0: /*  Normal */
01154     assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 0);
01155     break;
01156   case 1: /*  The first cell in the block */
01157     switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type) {
01158     case 0: /*  Not part of a block */
01159       assert(0);
01160       break;
01161     case 1: /*  Angle block */
01162       /* Loop and check each cell instead? So we don't get outside the block? */
01163       (vm->state).cellN += (vm->state).AGL_REG - 1;
01164 #ifdef STRICT
01165       assert((vm->state).cellN <= (vm->state).pgc->nr_of_cells);
01166       assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode != 0);
01167       assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 1);
01168 #else
01169       if (!((vm->state).cellN <= (vm->state).pgc->nr_of_cells) ||
01170           !((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode != 0) ||
01171           !((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 1)) {
01172         fprintf(MSG_OUT, "libdvdnav: Invalid angle block\n");
01173         (vm->state).cellN -= (vm->state).AGL_REG - 1;
01174       }
01175 #endif
01176       break;
01177     case 2: /*  ?? */
01178     case 3: /*  ?? */
01179     default:
01180       fprintf(MSG_OUT, "libdvdnav: Invalid? Cell block_mode (%d), block_type (%d)\n",
01181               (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode,
01182               (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type);
01183       assert(0);
01184     }
01185     break;
01186   case 2: /*  Cell in the block */
01187   case 3: /*  Last cell in the block */
01188   /* These might perhaps happen for RSM or LinkC commands? */
01189   default:
01190     fprintf(MSG_OUT, "libdvdnav: Cell is in block but did not enter at first cell!\n");
01191   }
01192 
01193   /* Updates (vm->state).pgN and PTTN_REG */
01194   if(!set_PGN(vm)) {
01195     /* Should not happen */
01196     assert(0);
01197     return play_PGC_post(vm);
01198   }
01199   (vm->state).cell_restart++;
01200   (vm->state).blockN = 0;
01201 #ifdef TRACE
01202   fprintf(MSG_OUT, "libdvdnav: Cell should restart here\n");
01203 #endif
01204   return play_this;
01205 }
01206 
01207 static link_t play_Cell_post(vm_t *vm) {
01208   cell_playback_t *cell;
01209 
01210 #ifdef TRACE
01211   fprintf(MSG_OUT, "libdvdnav: play_Cell_post: (vm->state).cellN (%i)\n", (vm->state).cellN);
01212 #endif
01213 
01214   cell = &(vm->state).pgc->cell_playback[(vm->state).cellN - 1];
01215 
01216   /* Still time is already taken care of before we get called. */
01217 
01218   /* Deal with a Cell command, if any */
01219   if(cell->cell_cmd_nr != 0) {
01220     link_t link_values;
01221 
01222 /*  These asserts are now not needed.
01223  *  Some DVDs have no cell commands listed in the PGC,
01224  *  but the Cell itself points to a cell command that does not exist.
01225  *  For this situation, just ignore the cell command and continue.
01226  *
01227  *  assert((vm->state).pgc->command_tbl != NULL);
01228  *  assert((vm->state).pgc->command_tbl->nr_of_cell >= cell->cell_cmd_nr);
01229  */
01230 
01231     if ((vm->state).pgc->command_tbl != NULL &&
01232         (vm->state).pgc->command_tbl->nr_of_cell >= cell->cell_cmd_nr) {
01233 #ifdef TRACE
01234       fprintf(MSG_OUT, "libdvdnav: Cell command present, executing\n");
01235 #endif
01236       if(vmEval_CMD(&(vm->state).pgc->command_tbl->cell_cmds[cell->cell_cmd_nr - 1], 1,
01237                     &(vm->state).registers, &link_values)) {
01238         return link_values;
01239       } else {
01240 #ifdef TRACE
01241         fprintf(MSG_OUT, "libdvdnav: Cell command didn't do a Jump, Link or Call\n");
01242 #endif
01243       }
01244     } else {
01245 #ifdef TRACE
01246       fprintf(MSG_OUT, "libdvdnav: Invalid Cell command\n");
01247 #endif
01248     }
01249   }
01250 
01251   /* Where to continue after playing the cell... */
01252   /* Multi angle/Interleaved */
01253   switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode) {
01254   case 0: /*  Normal */
01255     assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 0);
01256     (vm->state).cellN++;
01257     break;
01258   case 1: /*  The first cell in the block */
01259   case 2: /*  A cell in the block */
01260   case 3: /*  The last cell in the block */
01261   default:
01262     switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type) {
01263     case 0: /*  Not part of a block */
01264       assert(0);
01265       break;
01266     case 1: /*  Angle block */
01267       /* Skip the 'other' angles */
01268       (vm->state).cellN++;
01269       while((vm->state).cellN <= (vm->state).pgc->nr_of_cells &&
01270             (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode >= 2) {
01271         (vm->state).cellN++;
01272       }
01273       break;
01274     case 2: /*  ?? */
01275     case 3: /*  ?? */
01276     default:
01277       fprintf(MSG_OUT, "libdvdnav: Invalid? Cell block_mode (%d), block_type (%d)\n",
01278               (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode,
01279               (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type);
01280       assert(0);
01281     }
01282     break;
01283   }
01284 
01285   /* Figure out the correct pgN for the new cell */
01286   if(!set_PGN(vm)) {
01287 #ifdef TRACE
01288     fprintf(MSG_OUT, "libdvdnav: last cell in this PGC\n");
01289 #endif
01290     return play_PGC_post(vm);
01291   }
01292   return play_Cell(vm);
01293 }
01294 
01295 
01296 /* link processing */
01297 
01298 static int process_command(vm_t *vm, link_t link_values) {
01299 
01300   while(link_values.command != PlayThis) {
01301 
01302 #ifdef TRACE
01303     fprintf(MSG_OUT, "libdvdnav: Before printout starts:\n");
01304     vm_print_link(link_values);
01305     fprintf(MSG_OUT, "libdvdnav: Link values %i %i %i %i\n", link_values.command,
01306             link_values.data1, link_values.data2, link_values.data3);
01307     vm_print_current_domain_state(vm);
01308     fprintf(MSG_OUT, "libdvdnav: Before printout ends.\n");
01309 #endif
01310 
01311     switch(link_values.command) {
01312     case LinkNoLink:
01313       /* BUTTON number:data1 */
01314       if(link_values.data1 != 0)
01315         (vm->state).HL_BTNN_REG = link_values.data1 << 10;
01316       return 0;  /* no actual jump */
01317 
01318     case LinkTopC:
01319       /* Restart playing from the beginning of the current Cell. */
01320       /* BUTTON number:data1 */
01321       if(link_values.data1 != 0)
01322         (vm->state).HL_BTNN_REG = link_values.data1 << 10;
01323       link_values = play_Cell(vm);
01324       break;
01325     case LinkNextC:
01326       /* Link to Next Cell */
01327       /* BUTTON number:data1 */
01328       if(link_values.data1 != 0)
01329         (vm->state).HL_BTNN_REG = link_values.data1 << 10;
01330       (vm->state).cellN += 1;
01331       link_values = play_Cell(vm);
01332       break;
01333     case LinkPrevC:
01334       /* Link to Previous Cell */
01335       /* BUTTON number:data1 */
01336       if(link_values.data1 != 0)
01337         (vm->state).HL_BTNN_REG = link_values.data1 << 10;
01338       assert((vm->state).cellN > 1);
01339       (vm->state).cellN -= 1;
01340       link_values = play_Cell(vm);
01341       break;
01342 
01343     case LinkTopPG:
01344       /* Link to Top of current Program */
01345       /* BUTTON number:data1 */
01346       if(link_values.data1 != 0)
01347         (vm->state).HL_BTNN_REG = link_values.data1 << 10;
01348       link_values = play_PG(vm);
01349       break;
01350     case LinkNextPG:
01351       /* Link to Next Program */
01352       /* BUTTON number:data1 */
01353       if(link_values.data1 != 0)
01354         (vm->state).HL_BTNN_REG = link_values.data1 << 10;
01355       (vm->state).pgN += 1;
01356       link_values = play_PG(vm);
01357       break;
01358     case LinkPrevPG:
01359       /* Link to Previous Program */
01360       /* BUTTON number:data1 */
01361       if(link_values.data1 != 0)
01362         (vm->state).HL_BTNN_REG = link_values.data1 << 10;
01363       assert((vm->state).pgN > 1);
01364       (vm->state).pgN -= 1;
01365       link_values = play_PG(vm);
01366       break;
01367 
01368     case LinkTopPGC:
01369       /* Restart playing from beginning of current Program Chain */
01370       /* BUTTON number:data1 */
01371       if(link_values.data1 != 0)
01372         (vm->state).HL_BTNN_REG = link_values.data1 << 10;
01373       link_values = play_PGC(vm);
01374       break;
01375     case LinkNextPGC:
01376       /* Link to Next Program Chain */
01377       /* BUTTON number:data1 */
01378       if(link_values.data1 != 0)
01379         (vm->state).HL_BTNN_REG = link_values.data1 << 10;
01380       assert((vm->state).pgc->next_pgc_nr != 0);
01381       if(set_PGCN(vm, (vm->state).pgc->next_pgc_nr))
01382         link_values = play_PGC(vm);
01383       else
01384         link_values.command = Exit;
01385       break;
01386     case LinkPrevPGC:
01387       /* Link to Previous Program Chain */
01388       /* BUTTON number:data1 */
01389       if(link_values.data1 != 0)
01390         (vm->state).HL_BTNN_REG = link_values.data1 << 10;
01391       assert((vm->state).pgc->prev_pgc_nr != 0);
01392       if(set_PGCN(vm, (vm->state).pgc->prev_pgc_nr))
01393         link_values = play_PGC(vm);
01394       else
01395         link_values.command = Exit;
01396       break;
01397     case LinkGoUpPGC:
01398       /* Link to GoUp Program Chain */
01399       /* BUTTON number:data1 */
01400       if(link_values.data1 != 0)
01401         (vm->state).HL_BTNN_REG = link_values.data1 << 10;
01402       assert((vm->state).pgc->goup_pgc_nr != 0);
01403       if(set_PGCN(vm, (vm->state).pgc->goup_pgc_nr))
01404         link_values = play_PGC(vm);
01405       else
01406         link_values.command = Exit;
01407       break;
01408     case LinkTailPGC:
01409       /* Link to Tail of Program Chain */
01410       /* BUTTON number:data1 */
01411       if(link_values.data1 != 0)
01412         (vm->state).HL_BTNN_REG = link_values.data1 << 10;
01413       link_values = play_PGC_post(vm);
01414     break;
01415 
01416     case LinkRSM:
01417       {
01418         /* Link to Resume point */
01419         int i;
01420 
01421         /* Check and see if there is any rsm info!! */
01422         if (!(vm->state).rsm_vtsN) {
01423           fprintf(MSG_OUT, "libdvdnav: trying to resume without any resume info set\n");
01424           link_values.command = Exit;
01425           break;
01426         }
01427 
01428         (vm->state).domain = VTS_DOMAIN;
01429         if (!ifoOpenNewVTSI(vm, vm->dvd, (vm->state).rsm_vtsN))
01430           assert(0);
01431         set_PGCN(vm, (vm->state).rsm_pgcN);
01432 
01433         /* These should never be set in SystemSpace and/or MenuSpace */
01434         /* (vm->state).TTN_REG = rsm_tt; ?? */
01435         /* (vm->state).TT_PGCN_REG = (vm->state).rsm_pgcN; ?? */
01436         for(i = 0; i < 5; i++) {
01437           (vm->state).registers.SPRM[4 + i] = (vm->state).rsm_regs[i];
01438         }
01439 
01440         if(link_values.data1 != 0)
01441           (vm->state).HL_BTNN_REG = link_values.data1 << 10;
01442 
01443         if((vm->state).rsm_cellN == 0) {
01444           assert((vm->state).cellN); /*  Checking if this ever happens */
01445           (vm->state).pgN = 1;
01446           link_values = play_PG(vm);
01447         } else {
01448           /* (vm->state).pgN = ?? this gets the right value in set_PGN() below */
01449           (vm->state).cellN = (vm->state).rsm_cellN;
01450           link_values.command = PlayThis;
01451           link_values.data1 = (vm->state).rsm_blockN & 0xffff;
01452           link_values.data2 = (vm->state).rsm_blockN >> 16;
01453           if(!set_PGN(vm)) {
01454             /* Were at the end of the PGC, should not happen for a RSM */
01455             assert(0);
01456             link_values.command = LinkTailPGC;
01457             link_values.data1 = 0;  /* No button */
01458           }
01459         }
01460       }
01461       break;
01462     case LinkPGCN:
01463       /* Link to Program Chain Number:data1 */
01464       if(!set_PGCN(vm, link_values.data1))
01465         assert(0);
01466       link_values = play_PGC(vm);
01467       break;
01468     case LinkPTTN:
01469       /* Link to Part of current Title Number:data1 */
01470       /* BUTTON number:data2 */
01471       /* PGC Pre-Commands are not executed */
01472       assert((vm->state).domain == VTS_DOMAIN);
01473       if(link_values.data2 != 0)
01474         (vm->state).HL_BTNN_REG = link_values.data2 << 10;
01475       if(!set_VTS_PTT(vm, (vm->state).vtsN, (vm->state).VTS_TTN_REG, link_values.data1))
01476         link_values.command = Exit;
01477       else
01478         link_values = play_PG(vm);
01479       break;
01480     case LinkPGN:
01481       /* Link to Program Number:data1 */
01482       /* BUTTON number:data2 */
01483       if(link_values.data2 != 0)
01484         (vm->state).HL_BTNN_REG = link_values.data2 << 10;
01485       /* Update any other state, PTTN perhaps? */
01486       (vm->state).pgN = link_values.data1;
01487       link_values = play_PG(vm);
01488       break;
01489     case LinkCN:
01490       /* Link to Cell Number:data1 */
01491       /* BUTTON number:data2 */
01492       if(link_values.data2 != 0)
01493         (vm->state).HL_BTNN_REG = link_values.data2 << 10;
01494       /* Update any other state, pgN, PTTN perhaps? */
01495       (vm->state).cellN = link_values.data1;
01496       link_values = play_Cell(vm);
01497       break;
01498 
01499     case Exit:
01500       vm->stopped = 1;
01501       return 0;
01502 
01503     case JumpTT:
01504       /* Jump to VTS Title Domain */
01505       /* Only allowed from the First Play domain(PGC) */
01506       /* or the Video Manager domain (VMG) */
01507       /* Stop SPRM9 Timer */
01508       /* Set SPRM1 and SPRM2 */
01509       assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN); /* ?? */
01510       if(set_TT(vm, link_values.data1))
01511         link_values = play_PGC(vm);
01512       else
01513         link_values.command = Exit;
01514       break;
01515     case JumpVTS_TT:
01516       /* Jump to Title:data1 in same VTS Title Domain */
01517       /* Only allowed from the VTS Menu Domain(VTSM) */
01518       /* or the Video Title Set Domain(VTS) */
01519       /* Stop SPRM9 Timer */
01520       /* Set SPRM1 and SPRM2 */
01521       assert((vm->state).domain == VTSM_DOMAIN || (vm->state).domain == VTS_DOMAIN); /* ?? */
01522       if(!set_VTS_TT(vm, (vm->state).vtsN, link_values.data1))
01523         link_values.command = Exit;
01524       else
01525         link_values = play_PGC(vm);
01526       break;
01527     case JumpVTS_PTT:
01528       /* Jump to Part:data2 of Title:data1 in same VTS Title Domain */
01529       /* Only allowed from the VTS Menu Domain(VTSM) */
01530       /* or the Video Title Set Domain(VTS) */
01531       /* Stop SPRM9 Timer */
01532       /* Set SPRM1 and SPRM2 */
01533       assert((vm->state).domain == VTSM_DOMAIN || (vm->state).domain == VTS_DOMAIN); /* ?? */
01534       if(!set_VTS_PTT(vm, (vm->state).vtsN, link_values.data1, link_values.data2))
01535         link_values.command = Exit;
01536       else
01537         link_values = play_PGC_PG(vm, (vm->state).pgN);
01538       break;
01539 
01540     case JumpSS_FP:
01541       /* Jump to First Play Domain */
01542       /* Only allowed from the VTS Menu Domain(VTSM) */
01543       /* or the Video Manager domain (VMG) */
01544       /* Stop SPRM9 Timer and any GPRM counters */
01545       assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == VTSM_DOMAIN); /* ?? */
01546       if (!set_FP_PGC(vm))
01547         assert(0);
01548       link_values = play_PGC(vm);
01549       break;
01550     case JumpSS_VMGM_MENU:
01551       /* Jump to Video Manager domain - Title Menu:data1 or any PGC in VMG */
01552       /* Allowed from anywhere except the VTS Title domain */
01553       /* Stop SPRM9 Timer and any GPRM counters */
01554       assert((vm->state).domain != VTS_DOMAIN); /* ?? */
01555       if(vm->vmgi == NULL || vm->vmgi->pgci_ut == NULL) {
01556         link_values.command = Exit;
01557         break;
01558       }
01559       (vm->state).domain = VMGM_DOMAIN;
01560       if(!set_MENU(vm, link_values.data1))
01561         assert(0);
01562       link_values = play_PGC(vm);
01563       break;
01564     case JumpSS_VTSM:
01565       /* Jump to a menu in Video Title domain, */
01566       /* or to a Menu is the current VTS */
01567       /* Stop SPRM9 Timer and any GPRM counters */
01568       /* ifoOpenNewVTSI:data1 */
01569       /* VTS_TTN_REG:data2 */
01570       /* get_MENU:data3 */
01571       if(link_values.data1 != 0) {
01572         if (link_values.data1 != (vm->state).vtsN) {
01573           /* the normal case */
01574           assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN); /* ?? */
01575           if (!ifoOpenNewVTSI(vm, vm->dvd, link_values.data1))  /* Also sets (vm->state).vtsN */
01576             assert(0);
01577           if(vm->vtsi == NULL || vm->vtsi->pgci_ut == NULL) {
01578             link_values.command = Exit;
01579             break;
01580           }
01581           (vm->state).domain = VTSM_DOMAIN;
01582         } else {
01583           /* This happens on some discs like "Captain Scarlet & the Mysterons" or
01584            * the German RC2 of "Anatomie" in VTSM. */
01585           assert((vm->state).domain == VTSM_DOMAIN ||
01586             (vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN); /* ?? */
01587           if(vm->vtsi == NULL || vm->vtsi->pgci_ut == NULL) {
01588             link_values.command = Exit;
01589             break;
01590           }
01591           (vm->state).domain = VTSM_DOMAIN;
01592         }
01593       } else {
01594         /*  This happens on 'The Fifth Element' region 2. */
01595         assert((vm->state).domain == VTSM_DOMAIN);
01596       }
01597       /*  I don't know what title is supposed to be used for. */
01598       /*  Alien or Aliens has this != 1, I think. */
01599       /* assert(link_values.data2 == 1); */
01600       (vm->state).VTS_TTN_REG = link_values.data2;
01601       /* TTN_REG (SPRM4), VTS_TTN_REG (SPRM5), TT_PGCN_REG (SPRM6) are linked, */
01602       /* so if one changes, the others must change to match it. */
01603       (vm->state).TTN_REG     = get_TT(vm, (vm->state).vtsN, (vm->state).VTS_TTN_REG);
01604       if(!set_MENU(vm, link_values.data3))
01605         assert(0);
01606       link_values = play_PGC(vm);
01607       break;
01608     case JumpSS_VMGM_PGC:
01609       /* set_PGCN:data1 */
01610       /* Stop SPRM9 Timer and any GPRM counters */
01611       assert((vm->state).domain != VTS_DOMAIN); /* ?? */
01612       if(vm->vmgi == NULL || vm->vmgi->pgci_ut == NULL) {
01613         link_values.command = Exit;
01614         break;
01615       }
01616       (vm->state).domain = VMGM_DOMAIN;
01617       if(!set_PGCN(vm, link_values.data1))
01618         assert(0);
01619       link_values = play_PGC(vm);
01620       break;
01621 
01622     case CallSS_FP:
01623       /* set_RSMinfo:data1 */
01624       assert((vm->state).domain == VTS_DOMAIN); /* ?? */
01625       /* Must be called before domain is changed */
01626       set_RSMinfo(vm, link_values.data1, /* We dont have block info */ 0);
01627       set_FP_PGC(vm);
01628       link_values = play_PGC(vm);
01629       break;
01630     case CallSS_VMGM_MENU:
01631       /* set_MENU:data1 */
01632       /* set_RSMinfo:data2 */
01633       assert((vm->state).domain == VTS_DOMAIN); /* ?? */
01634       /* Must be called before domain is changed */
01635       if(vm->vmgi == NULL || vm->vmgi->pgci_ut == NULL) {
01636         link_values.command = Exit;
01637         break;
01638       }
01639       set_RSMinfo(vm, link_values.data2, /* We dont have block info */ 0);
01640       (vm->state).domain = VMGM_DOMAIN;
01641       if(!set_MENU(vm, link_values.data1))
01642         assert(0);
01643       link_values = play_PGC(vm);
01644       break;
01645     case CallSS_VTSM:
01646       /* set_MENU:data1 */
01647       /* set_RSMinfo:data2 */
01648       assert((vm->state).domain == VTS_DOMAIN); /* ?? */
01649       /* Must be called before domain is changed */
01650       if(vm->vtsi == NULL || vm->vtsi->pgci_ut == NULL) {
01651         link_values.command = Exit;
01652         break;
01653       }
01654       set_RSMinfo(vm, link_values.data2, /* We dont have block info */ 0);
01655       (vm->state).domain = VTSM_DOMAIN;
01656       if(!set_MENU(vm, link_values.data1))
01657         assert(0);
01658       link_values = play_PGC(vm);
01659       break;
01660     case CallSS_VMGM_PGC:
01661       /* set_PGC:data1 */
01662       /* set_RSMinfo:data2 */
01663       assert((vm->state).domain == VTS_DOMAIN); /* ?? */
01664       /* Must be called before domain is changed */
01665       if(vm->vmgi == NULL || vm->vmgi->pgci_ut == NULL) {
01666         link_values.command = Exit;
01667         break;
01668       }
01669       set_RSMinfo(vm, link_values.data2, /* We dont have block info */ 0);
01670       (vm->state).domain = VMGM_DOMAIN;
01671       if(!set_PGCN(vm, link_values.data1))
01672         assert(0);
01673       link_values = play_PGC(vm);
01674       break;
01675     case PlayThis:
01676       /* Should never happen. */
01677       assert(0);
01678       break;
01679     }
01680 
01681 #ifdef TRACE
01682     fprintf(MSG_OUT, "libdvdnav: After printout starts:\n");
01683     vm_print_current_domain_state(vm);
01684     fprintf(MSG_OUT, "libdvdnav: After printout ends.\n");
01685 #endif
01686 
01687   }
01688   (vm->state).blockN = link_values.data1 | (link_values.data2 << 16);
01689   return 1;
01690 }
01691 
01692 
01693 /* Set functions */
01694 
01695 static int set_TT(vm_t *vm, int tt) {
01696   return set_PTT(vm, tt, 1);
01697 }
01698 
01699 static int set_PTT(vm_t *vm, int tt, int ptt) {
01700   assert(tt <= vm->vmgi->tt_srpt->nr_of_srpts);
01701   return set_VTS_PTT(vm, vm->vmgi->tt_srpt->title[tt - 1].title_set_nr,
01702                      vm->vmgi->tt_srpt->title[tt - 1].vts_ttn, ptt);
01703 }
01704 
01705 static int set_VTS_TT(vm_t *vm, int vtsN, int vts_ttn) {
01706   return set_VTS_PTT(vm, vtsN, vts_ttn, 1);
01707 }
01708 
01709 static int set_VTS_PTT(vm_t *vm, int vtsN, int vts_ttn, int part) {
01710   int pgcN, pgN, res;
01711 
01712   (vm->state).domain = VTS_DOMAIN;
01713 
01714   if (vtsN != (vm->state).vtsN)
01715     if (!ifoOpenNewVTSI(vm, vm->dvd, vtsN))  /* Also sets (vm->state).vtsN */
01716       return 0;
01717 
01718   if ((vts_ttn < 1) || (vts_ttn > vm->vtsi->vts_ptt_srpt->nr_of_srpts) ||
01719       (part < 1) || (part > vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].nr_of_ptts) ) {
01720     return 0;
01721   }
01722 
01723   pgcN = vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].ptt[part - 1].pgcn;
01724   pgN = vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].ptt[part - 1].pgn;
01725 
01726   (vm->state).TT_PGCN_REG = pgcN;
01727   (vm->state).PTTN_REG    = part;
01728   (vm->state).TTN_REG     = get_TT(vm, vtsN, vts_ttn);
01729   if( (vm->state.TTN_REG) == 0 )
01730     return 0;
01731 
01732   (vm->state).VTS_TTN_REG = vts_ttn;
01733   (vm->state).vtsN        = vtsN;  /* Not sure about this one. We can get to it easily from TTN_REG */
01734   /* Any other registers? */
01735 
01736   res = set_PGCN(vm, pgcN);   /* This clobber's state.pgN (sets it to 1), but we don't want clobbering here. */
01737   (vm->state).pgN = pgN;
01738   return res;
01739 }
01740 
01741 static int set_PROG(vm_t *vm, int tt, int pgcn, int pgn) {
01742   assert(tt <= vm->vmgi->tt_srpt->nr_of_srpts);
01743   return set_VTS_PROG(vm, vm->vmgi->tt_srpt->title[tt - 1].title_set_nr,
01744                      vm->vmgi->tt_srpt->title[tt - 1].vts_ttn, pgcn, pgn);
01745 }
01746 
01747 static int set_VTS_PROG(vm_t *vm, int vtsN, int vts_ttn, int pgcn, int pgn) {
01748   int pgcN, pgN, res, title, part = 0;
01749 
01750   (vm->state).domain = VTS_DOMAIN;
01751 
01752   if (vtsN != (vm->state).vtsN)
01753     if (!ifoOpenNewVTSI(vm, vm->dvd, vtsN))  /* Also sets (vm->state).vtsN */
01754       return 0;
01755 
01756   if ((vts_ttn < 1) || (vts_ttn > vm->vtsi->vts_ptt_srpt->nr_of_srpts)) {
01757     return 0;
01758   }
01759 
01760   pgcN = pgcn;
01761   pgN = pgn;
01762 
01763   (vm->state).TT_PGCN_REG = pgcN;
01764   (vm->state).TTN_REG     = get_TT(vm, vtsN, vts_ttn);
01765   assert( (vm->state.TTN_REG) != 0 );
01766   (vm->state).VTS_TTN_REG = vts_ttn;
01767   (vm->state).vtsN        = vtsN;  /* Not sure about this one. We can get to it easily from TTN_REG */
01768   /* Any other registers? */
01769 
01770   res = set_PGCN(vm, pgcN);   /* This clobber's state.pgN (sets it to 1), but we don't want clobbering here. */
01771   (vm->state).pgN = pgN;
01772   vm_get_current_title_part(vm, &title, &part);
01773   (vm->state).PTTN_REG    = part;
01774   return res;
01775 }
01776 
01777 static int set_FP_PGC(vm_t *vm) {
01778   (vm->state).domain = FP_DOMAIN;
01779   if (!vm->vmgi->first_play_pgc) {
01780     return set_PGCN(vm, 1);
01781   }
01782   (vm->state).pgc = vm->vmgi->first_play_pgc;
01783   (vm->state).pgcN = vm->vmgi->vmgi_mat->first_play_pgc;
01784   return 1;
01785 }
01786 
01787 
01788 static int set_MENU(vm_t *vm, int menu) {
01789   assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == VTSM_DOMAIN);
01790   return set_PGCN(vm, get_ID(vm, menu));
01791 }
01792 
01793 static int set_PGCN(vm_t *vm, int pgcN) {
01794   pgcit_t *pgcit;
01795 
01796   pgcit = get_PGCIT(vm);
01797   assert(pgcit != NULL);  /* ?? Make this return -1 instead */
01798 
01799   if(pgcN < 1 || pgcN > pgcit->nr_of_pgci_srp) {
01800 #ifdef TRACE
01801     fprintf(MSG_OUT, "libdvdnav:  ** No such pgcN = %d\n", pgcN);
01802 #endif
01803     return 0;
01804   }
01805 
01806   (vm->state).pgc = pgcit->pgci_srp[pgcN - 1].pgc;
01807   (vm->state).pgcN = pgcN;
01808   (vm->state).pgN = 1;
01809 
01810   if((vm->state).domain == VTS_DOMAIN)
01811     (vm->state).TT_PGCN_REG = pgcN;
01812 
01813   return 1;
01814 }
01815 
01816 /* Figure out the correct pgN from the cell and update (vm->state). */
01817 static int set_PGN(vm_t *vm) {
01818   int new_pgN = 0;
01819   int dummy, part = 0;
01820 
01821   while(new_pgN < (vm->state).pgc->nr_of_programs
01822         && (vm->state).cellN >= (vm->state).pgc->program_map[new_pgN])
01823     new_pgN++;
01824 
01825   if(new_pgN == (vm->state).pgc->nr_of_programs) /* We are at the last program */
01826     if((vm->state).cellN > (vm->state).pgc->nr_of_cells)
01827       return 0; /* We are past the last cell */
01828 
01829   (vm->state).pgN = new_pgN;
01830 
01831   if((vm->state).domain == VTS_DOMAIN) {
01832     playback_type_t *pb_ty;
01833     if((vm->state).TTN_REG > vm->vmgi->tt_srpt->nr_of_srpts)
01834       return 0; /* ?? */
01835     pb_ty = &vm->vmgi->tt_srpt->title[(vm->state).TTN_REG - 1].pb_ty;
01836       vm_get_current_title_part(vm, &dummy, &part);
01837       (vm->state).PTTN_REG = part;
01838   }
01839   return 1;
01840 }
01841 
01842 /* Must be called before domain is changed (set_PGCN()) */
01843 static void set_RSMinfo(vm_t *vm, int cellN, int blockN) {
01844   int i;
01845 
01846   if(cellN) {
01847     (vm->state).rsm_cellN = cellN;
01848     (vm->state).rsm_blockN = blockN;
01849   } else {
01850     (vm->state).rsm_cellN = (vm->state).cellN;
01851     (vm->state).rsm_blockN = blockN;
01852   }
01853   (vm->state).rsm_vtsN = (vm->state).vtsN;
01854   (vm->state).rsm_pgcN = get_PGCN(vm);
01855 
01856   /* assert((vm->state).rsm_pgcN == (vm->state).TT_PGCN_REG);  for VTS_DOMAIN */
01857 
01858   for(i = 0; i < 5; i++) {
01859     (vm->state).rsm_regs[i] = (vm->state).registers.SPRM[4 + i];
01860   }
01861 }
01862 
01863 
01864 /* Get functions */
01865 
01866 /* Searches the TT tables, to find the current TT.
01867  * returns the current TT.
01868  * returns 0 if not found.
01869  */
01870 static int get_TT(vm_t *vm, int vtsN, int vts_ttn) {
01871   int i;
01872   int tt=0;
01873 
01874   for(i = 1; i <= vm->vmgi->tt_srpt->nr_of_srpts; i++) {
01875     if( vm->vmgi->tt_srpt->title[i - 1].title_set_nr == vtsN &&
01876         vm->vmgi->tt_srpt->title[i - 1].vts_ttn == vts_ttn) {
01877       tt=i;
01878       break;
01879     }
01880   }
01881   return tt;
01882 }
01883 
01884 /* Search for entry_id match of the PGC Category in the current VTS PGCIT table.
01885  * Return pgcN based on entry_id match.
01886  */
01887 static int get_ID(vm_t *vm, int id) {
01888   int pgcN, i;
01889   pgcit_t *pgcit;
01890 
01891   /* Relies on state to get the correct pgcit. */
01892   pgcit = get_PGCIT(vm);
01893   assert(pgcit != NULL);
01894 #ifdef TRACE
01895   fprintf(MSG_OUT, "libdvdnav: ** Searching for menu (0x%x) entry PGC\n", id);
01896 #endif
01897 
01898   /* Force high bit set. */
01899   id |=0x80;
01900 
01901   /* Get menu/title */
01902   for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
01903     if( (pgcit->pgci_srp[i].entry_id) == id) {
01904       pgcN = i + 1;
01905 #ifdef TRACE
01906       fprintf(MSG_OUT, "libdvdnav: Found menu.\n");
01907 #endif
01908       return pgcN;
01909     }
01910   }
01911 #ifdef TRACE
01912   fprintf(MSG_OUT, "libdvdnav: ** No such id/menu (0x%02x) entry PGC\n", id & 0x7f);
01913   for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
01914     if ( (pgcit->pgci_srp[i].entry_id & 0x80) == 0x80) {
01915       fprintf(MSG_OUT, "libdvdnav: Available menus: 0x%x\n",
01916                      pgcit->pgci_srp[i].entry_id & 0x7f);
01917     }
01918   }
01919 #endif
01920   return 0; /*  error */
01921 }
01922 
01923 /* FIXME: we have a pgcN member in the vm's state now, so this should be obsolete */
01924 static int get_PGCN(vm_t *vm) {
01925   pgcit_t *pgcit;
01926   int pgcN = 1;
01927 
01928   pgcit = get_PGCIT(vm);
01929 
01930   if (pgcit) {
01931     while(pgcN <= pgcit->nr_of_pgci_srp) {
01932       if(pgcit->pgci_srp[pgcN - 1].pgc == (vm->state).pgc) {
01933         assert((vm->state).pgcN == pgcN);
01934         return pgcN;
01935       }
01936       pgcN++;
01937     }
01938   }
01939   fprintf(MSG_OUT, "libdvdnav: get_PGCN failed. Was trying to find pgcN in domain %d\n",
01940          (vm->state).domain);
01941   return 0; /*  error */
01942 }
01943 
01944 static pgcit_t* get_MENU_PGCIT(vm_t *vm, ifo_handle_t *h, uint16_t lang) {
01945   int i;
01946 
01947   if(h == NULL || h->pgci_ut == NULL) {
01948     fprintf(MSG_OUT, "libdvdnav: *** pgci_ut handle is NULL ***\n");
01949     return NULL; /*  error? */
01950   }
01951 
01952   i = 0;
01953   while(i < h->pgci_ut->nr_of_lus
01954         && h->pgci_ut->lu[i].lang_code != lang)
01955     i++;
01956   if(i == h->pgci_ut->nr_of_lus) {
01957     fprintf(MSG_OUT, "libdvdnav: Language '%c%c' not found, using '%c%c' instead\n",
01958             (char)(lang >> 8), (char)(lang & 0xff),
01959             (char)(h->pgci_ut->lu[0].lang_code >> 8),
01960             (char)(h->pgci_ut->lu[0].lang_code & 0xff));
01961     fprintf(MSG_OUT, "libdvdnav: Menu Languages available: ");
01962     for(i = 0; i < h->pgci_ut->nr_of_lus; i++) {
01963       fprintf(MSG_OUT, "%c%c ",
01964             (char)(h->pgci_ut->lu[i].lang_code >> 8),
01965             (char)(h->pgci_ut->lu[i].lang_code & 0xff));
01966     }
01967     fprintf(MSG_OUT, "\n");
01968     i = 0; /*  error? */
01969   }
01970 
01971   return h->pgci_ut->lu[i].pgcit;
01972 }
01973 
01974 /* Uses state to decide what to return */
01975 static pgcit_t* get_PGCIT(vm_t *vm) {
01976   pgcit_t *pgcit = NULL;
01977 
01978   switch ((vm->state).domain) {
01979   case VTS_DOMAIN:
01980     if(!vm->vtsi) return NULL;
01981     pgcit = vm->vtsi->vts_pgcit;
01982     break;
01983   case VTSM_DOMAIN:
01984     if(!vm->vtsi) return NULL;
01985     pgcit = get_MENU_PGCIT(vm, vm->vtsi, (vm->state).registers.SPRM[0]);
01986     break;
01987   case VMGM_DOMAIN:
01988   case FP_DOMAIN:
01989     pgcit = get_MENU_PGCIT(vm, vm->vmgi, (vm->state).registers.SPRM[0]);
01990     break;
01991   default:
01992     abort();
01993   }
01994 
01995   return pgcit;
01996 }
01997 
01998 //return the ifo_handle_t describing required title, used to
01999 //identify chapters
02000 ifo_handle_t *vm_get_title_ifo(vm_t *vm, uint32_t title)
02001 {
02002   ifo_handle_t *ifo = NULL;
02003   uint8_t titleset_nr;
02004   if((title < 1) || (title > vm->vmgi->tt_srpt->nr_of_srpts))
02005     return NULL;
02006   titleset_nr = vm->vmgi->tt_srpt->title[title-1].title_set_nr;
02007   ifo = ifoOpen(vm->dvd, titleset_nr);
02008   return ifo;
02009 }
02010 
02011 void vm_ifo_close(ifo_handle_t *ifo)
02012 {
02013   ifoClose(ifo);
02014 }
02015 
02016 /* Debug functions */
02017 
02018 #ifdef TRACE
02019 void vm_position_print(vm_t *vm, vm_position_t *position) {
02020   fprintf(MSG_OUT, "libdvdnav: But=%x Spu=%x Aud=%x Ang=%x Hop=%x vts=%x dom=%x cell=%x cell_restart=%x cell_start=%x still=%x block=%x\n",
02021   position->button,
02022   position->spu_channel,
02023   position->audio_channel,
02024   position->angle_channel,
02025   position->hop_channel,
02026   position->vts,
02027   position->domain,
02028   position->cell,
02029   position->cell_restart,
02030   position->cell_start,
02031   position->still,
02032   position->block);
02033 }
02034 #endif
02035 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends