|
MythTV
0.26-pre
|
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
1.7.6.1