comparison vm/vm.c @ 225:9b1b740e3fc9 src

big build system changes * cleaned up all Makefiles and added a Makefile.common * added relchk script * moved libdvdread files to a dvdread subdir * moved DVD VM to a vm subdir * removed unused code in read_cache.c
author mroi
date Sun, 11 Jan 2004 21:43:13 +0000
parents
children bdc6b26da039
comparison
equal deleted inserted replaced
224:f19fce15577b 225:9b1b740e3fc9
1 /*
2 * Copyright (C) 2000, 2001 Håkan Hjort
3 * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
4 *
5 * This file is part of libdvdnav, a DVD navigation library. It is modified
6 * from a file originally part of the Ogle DVD player.
7 *
8 * libdvdnav is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * libdvdnav is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
21 *
22 * $Id$
23 *
24 */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include <stdio.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <inttypes.h>
35 #include <assert.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39
40 #include "ifo_types.h"
41 #include "ifo_read.h"
42
43 #include "dvdnav_internal.h"
44
45 #ifdef _MSC_VER
46 #include <io.h> /* read() */
47 #define lseek64 lseek
48 #endif /* _MSC_VER */
49
50 #ifdef __CYGWIN__
51 # define off64_t off_t
52 # define lseek64 lseek
53 #endif
54
55 /*
56 #define STRICT
57 */
58
59 /* Local prototypes */
60
61 /* get_XYZ returns a value.
62 * set_XYZ sets state using passed parameters.
63 * returns success/failure.
64 */
65
66 /* Play */
67 static link_t play_PGC(vm_t *vm);
68 static link_t play_PGC_PG(vm_t *vm, int pgN);
69 static link_t play_PGC_post(vm_t *vm);
70 static link_t play_PG(vm_t *vm);
71 static link_t play_Cell(vm_t *vm);
72 static link_t play_Cell_post(vm_t *vm);
73
74 /* Process link - returns 1 if a hop has been performed */
75 static int process_command(vm_t *vm,link_t link_values);
76
77 /* Set */
78 static int set_TT(vm_t *vm, int tt);
79 static int set_PTT(vm_t *vm, int tt, int ptt);
80 static int set_VTS_TT(vm_t *vm, int vtsN, int vts_ttn);
81 static int set_VTS_PTT(vm_t *vm, int vtsN, int vts_ttn, int part);
82 static int set_FP_PGC(vm_t *vm);
83 static int set_MENU(vm_t *vm, int menu);
84 static int set_PGCN(vm_t *vm, int pgcN);
85 static int set_PGN(vm_t *vm); /* Set PGN based on (vm->state).CellN */
86 static void set_RSMinfo(vm_t *vm, int cellN, int blockN);
87
88 /* Get */
89 static int get_TT(vm_t *vm, int vtsN, int vts_ttn);
90 static int get_ID(vm_t *vm, int id);
91 static int get_PGCN(vm_t *vm);
92
93 static pgcit_t* get_MENU_PGCIT(vm_t *vm, ifo_handle_t *h, uint16_t lang);
94 static pgcit_t* get_PGCIT(vm_t *vm);
95
96
97 /* Helper functions */
98
99 #ifdef TRACE
100 static void vm_print_current_domain_state(vm_t *vm) {
101 switch((vm->state).domain) {
102 case VTS_DOMAIN:
103 fprintf(MSG_OUT, "libdvdnav: Video Title Domain: -\n");
104 break;
105
106 case VTSM_DOMAIN:
107 fprintf(MSG_OUT, "libdvdnav: Video Title Menu Domain: -\n");
108 break;
109
110 case VMGM_DOMAIN:
111 fprintf(MSG_OUT, "libdvdnav: Video Manager Menu Domain: -\n");
112 break;
113
114 case FP_DOMAIN:
115 fprintf(MSG_OUT, "libdvdnav: First Play Domain: -\n");
116 break;
117
118 default:
119 fprintf(MSG_OUT, "libdvdnav: Unknown Domain: -\n");
120 break;
121 }
122 fprintf(MSG_OUT, "libdvdnav: VTS:%d PGC:%d PG:%u CELL:%u BLOCK:%u VTS_TTN:%u TTN:%u TT_PGCN:%u\n",
123 (vm->state).vtsN,
124 get_PGCN(vm),
125 (vm->state).pgN,
126 (vm->state).cellN,
127 (vm->state).blockN,
128 (vm->state).VTS_TTN_REG,
129 (vm->state).TTN_REG,
130 (vm->state).TT_PGCN_REG);
131 }
132 #endif
133
134 static void dvd_read_name(char *name, const char *device) {
135 int fd, i;
136 #if !defined(__FreeBSD__) && !defined(WIN32)
137 off64_t off;
138 #else
139 off_t off;
140 #endif
141 uint8_t data[DVD_VIDEO_LB_LEN];
142
143 /* Read DVD name */
144 fd=open(device, O_RDONLY);
145 if (fd > 0) {
146 off = lseek64( fd, 32 * (int64_t) DVD_VIDEO_LB_LEN, SEEK_SET );
147 if( off == ( 32 * (int64_t) DVD_VIDEO_LB_LEN ) ) {
148 off = read( fd, data, DVD_VIDEO_LB_LEN );
149 close(fd);
150 if (off == ( (int64_t) DVD_VIDEO_LB_LEN )) {
151 fprintf(MSG_OUT, "libdvdnav: DVD Title: ");
152 for(i=25; i < 73; i++ ) {
153 if((data[i] == 0)) break;
154 if((data[i] > 32) && (data[i] < 127)) {
155 fprintf(MSG_OUT, "%c", data[i]);
156 } else {
157 fprintf(MSG_OUT, " ");
158 }
159 }
160 strncpy(name, &data[25], 48);
161 name[48] = 0;
162 fprintf(MSG_OUT, "\nlibdvdnav: DVD Serial Number: ");
163 for(i=73; i < 89; i++ ) {
164 if((data[i] == 0)) break;
165 if((data[i] > 32) && (data[i] < 127)) {
166 fprintf(MSG_OUT, "%c", data[i]);
167 } else {
168 fprintf(MSG_OUT, " ");
169 }
170 }
171 fprintf(MSG_OUT, "\nlibdvdnav: DVD Title (Alternative): ");
172 for(i=89; i < 128; i++ ) {
173 if((data[i] == 0)) break;
174 if((data[i] > 32) && (data[i] < 127)) {
175 fprintf(MSG_OUT, "%c", data[i]);
176 } else {
177 fprintf(MSG_OUT, " ");
178 }
179 }
180 fprintf(MSG_OUT, "\n");
181 } else {
182 fprintf(MSG_OUT, "libdvdnav: Can't read name block. Probably not a DVD-ROM device.\n");
183 }
184 } else {
185 fprintf(MSG_OUT, "libdvdnav: Can't seek to block %u\n", 32 );
186 }
187 close(fd);
188 } else {
189 fprintf(MSG_OUT, "NAME OPEN FAILED\n");
190 }
191 }
192
193 static void ifoOpenNewVTSI(vm_t *vm, dvd_reader_t *dvd, int vtsN) {
194 if((vm->state).vtsN == vtsN) {
195 return; /* We alread have it */
196 }
197
198 if(vm->vtsi != NULL)
199 ifoClose(vm->vtsi);
200
201 vm->vtsi = ifoOpenVTSI(dvd, vtsN);
202 if(vm->vtsi == NULL) {
203 fprintf(MSG_OUT, "libdvdnav: ifoOpenVTSI failed - CRASHING!!!\n");
204 assert(0);
205 }
206 if(!ifoRead_VTS_PTT_SRPT(vm->vtsi)) {
207 fprintf(MSG_OUT, "libdvdnav: ifoRead_VTS_PTT_SRPT failed - CRASHING!!!\n");
208 assert(0);
209 }
210 if(!ifoRead_PGCIT(vm->vtsi)) {
211 fprintf(MSG_OUT, "libdvdnav: ifoRead_PGCIT failed - CRASHING!!!\n");
212 assert(0);
213 }
214 if(!ifoRead_PGCI_UT(vm->vtsi)) {
215 fprintf(MSG_OUT, "libdvdnav: ifoRead_PGCI_UT failed - CRASHING!!!\n");
216 assert(0);
217 }
218 if(!ifoRead_VOBU_ADMAP(vm->vtsi)) {
219 fprintf(MSG_OUT, "libdvdnav: ifoRead_VOBU_ADMAP vtsi failed - CRASHING\n");
220 assert(0);
221 }
222 if(!ifoRead_TITLE_VOBU_ADMAP(vm->vtsi)) {
223 fprintf(MSG_OUT, "libdvdnav: ifoRead_TITLE_VOBU_ADMAP vtsi failed - CRASHING\n");
224 assert(0);
225 }
226 (vm->state).vtsN = vtsN;
227 }
228
229
230 /* Initialisation & Destruction */
231
232 vm_t* vm_new_vm() {
233 return (vm_t*)calloc(sizeof(vm_t), sizeof(char));
234 }
235
236 void vm_free_vm(vm_t *vm) {
237 vm_stop(vm);
238 free(vm);
239 }
240
241
242 /* IFO Access */
243
244 ifo_handle_t *vm_get_vmgi(vm_t *vm) {
245 return vm->vmgi;
246 }
247
248 ifo_handle_t *vm_get_vtsi(vm_t *vm) {
249 return vm->vtsi;
250 }
251
252
253 /* Reader Access */
254
255 dvd_reader_t *vm_get_dvd_reader(vm_t *vm) {
256 return vm->dvd;
257 }
258
259
260 /* Basic Handling */
261
262 void vm_start(vm_t *vm) {
263 /* Set pgc to FP (First Play) pgc */
264 set_FP_PGC(vm);
265 process_command(vm, play_PGC(vm));
266 }
267
268 void vm_stop(vm_t *vm) {
269 if(vm->vmgi) {
270 ifoClose(vm->vmgi);
271 vm->vmgi=NULL;
272 }
273 if(vm->vtsi) {
274 ifoClose(vm->vtsi);
275 vm->vtsi=NULL;
276 }
277 if(vm->dvd) {
278 DVDClose(vm->dvd);
279 vm->dvd=NULL;
280 }
281 vm->stopped = 1;
282 }
283
284 int vm_reset(vm_t *vm, const char *dvdroot) {
285 /* Setup State */
286 memset((vm->state).registers.SPRM, 0, sizeof((vm->state).registers.SPRM));
287 memset((vm->state).registers.GPRM, 0, sizeof((vm->state).registers.GPRM));
288 memset((vm->state).registers.GPRM_mode, 0, sizeof((vm->state).registers.GPRM_mode));
289 memset((vm->state).registers.GPRM_mode, 0, sizeof((vm->state).registers.GPRM_mode));
290 memset((vm->state).registers.GPRM_time, 0, sizeof((vm->state).registers.GPRM_time));
291 (vm->state).registers.SPRM[0] = ('e'<<8)|'n'; /* Player Menu Languange code */
292 (vm->state).AST_REG = 15; /* 15 why? */
293 (vm->state).SPST_REG = 62; /* 62 why? */
294 (vm->state).AGL_REG = 1;
295 (vm->state).TTN_REG = 1;
296 (vm->state).VTS_TTN_REG = 1;
297 /* (vm->state).TT_PGCN_REG = 0 */
298 (vm->state).PTTN_REG = 1;
299 (vm->state).HL_BTNN_REG = 1 << 10;
300 (vm->state).PTL_REG = 15; /* Parental Level */
301 (vm->state).registers.SPRM[12] = ('U'<<8)|'S'; /* Parental Management Country Code */
302 (vm->state).registers.SPRM[16] = ('e'<<8)|'n'; /* Initial Language Code for Audio */
303 (vm->state).registers.SPRM[18] = ('e'<<8)|'n'; /* Initial Language Code for Spu */
304 (vm->state).registers.SPRM[20] = 0x1; /* Player Regional Code Mask. Region free! */
305 (vm->state).registers.SPRM[14] = 0x100; /* Try Pan&Scan */
306
307 (vm->state).pgN = 0;
308 (vm->state).cellN = 0;
309 (vm->state).cell_restart = 0;
310
311 (vm->state).domain = FP_DOMAIN;
312 (vm->state).rsm_vtsN = 0;
313 (vm->state).rsm_cellN = 0;
314 (vm->state).rsm_blockN = 0;
315
316 (vm->state).vtsN = -1;
317
318 if (vm->dvd && dvdroot) {
319 /* a new dvd device has been requested */
320 vm_stop(vm);
321 }
322 if (!vm->dvd) {
323 vm->dvd = DVDOpen(dvdroot);
324 if(!vm->dvd) {
325 fprintf(MSG_OUT, "libdvdnav: vm: faild to open/read the DVD\n");
326 return 0;
327 }
328 dvd_read_name(vm->dvd_name, dvdroot);
329 vm->map = remap_loadmap(vm->dvd_name);
330 vm->vmgi = ifoOpenVMGI(vm->dvd);
331 if(!vm->vmgi) {
332 fprintf(MSG_OUT, "libdvdnav: vm: faild to read VIDEO_TS.IFO\n");
333 return 0;
334 }
335 if(!ifoRead_FP_PGC(vm->vmgi)) {
336 fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_FP_PGC failed\n");
337 return 0;
338 }
339 if(!ifoRead_TT_SRPT(vm->vmgi)) {
340 fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_TT_SRPT failed\n");
341 return 0;
342 }
343 if(!ifoRead_PGCI_UT(vm->vmgi)) {
344 fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_PGCI_UT failed\n");
345 return 0;
346 }
347 if(!ifoRead_PTL_MAIT(vm->vmgi)) {
348 fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_PTL_MAIT failed\n");
349 /* return 0; Not really used for now.. */
350 }
351 if(!ifoRead_VTS_ATRT(vm->vmgi)) {
352 fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_VTS_ATRT failed\n");
353 /* return 0; Not really used for now.. */
354 }
355 if(!ifoRead_VOBU_ADMAP(vm->vmgi)) {
356 fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_VOBU_ADMAP vgmi failed\n");
357 /* return 0; Not really used for now.. */
358 }
359 /* ifoRead_TXTDT_MGI(vmgi); Not implemented yet */
360 }
361 if (vm->vmgi) {
362 int i, mask;
363 fprintf(MSG_OUT, "libdvdnav: DVD disk reports itself with Region mask 0x%08x. Regions:",
364 vm->vmgi->vmgi_mat->vmg_category);
365 for (i = 1, mask = 1; i <= 8; i++, mask <<= 1)
366 if (((vm->vmgi->vmgi_mat->vmg_category >> 16) & mask) == 0)
367 fprintf(MSG_OUT, " %d", i);
368 fprintf(MSG_OUT, "\n");
369 }
370 return 1;
371 }
372
373
374 /* copying and merging */
375
376 vm_t *vm_new_copy(vm_t *source) {
377 vm_t *target = vm_new_vm();
378 int vtsN;
379 int pgcN = get_PGCN(source);
380 int pgN = (source->state).pgN;
381
382 assert(pgcN);
383
384 memcpy(target, source, sizeof(vm_t));
385
386 /* open a new vtsi handle, because the copy might switch to another VTS */
387 target->vtsi = NULL;
388 vtsN = (target->state).vtsN;
389 if (vtsN > 0) {
390 (target->state).vtsN = 0;
391 ifoOpenNewVTSI(target, target->dvd, vtsN);
392
393 /* restore pgc pointer into the new vtsi */
394 if (!set_PGCN(target, pgcN))
395 assert(0);
396 (target->state).pgN = pgN;
397 }
398
399 return target;
400 }
401
402 void vm_merge(vm_t *target, vm_t *source) {
403 if(target->vtsi)
404 ifoClose(target->vtsi);
405 memcpy(target, source, sizeof(vm_t));
406 memset(source, 0, sizeof(vm_t));
407 }
408
409 void vm_free_copy(vm_t *vm) {
410 if(vm->vtsi)
411 ifoClose(vm->vtsi);
412 free(vm);
413 }
414
415
416 /* regular playback */
417
418 void vm_position_get(vm_t *vm, vm_position_t *position) {
419 position->button = (vm->state).HL_BTNN_REG >> 10;
420 position->vts = (vm->state).vtsN;
421 position->domain = (vm->state).domain;
422 position->spu_channel = (vm->state).SPST_REG;
423 position->audio_channel = (vm->state).AST_REG;
424 position->angle_channel = (vm->state).AGL_REG;
425 position->hop_channel = vm->hop_channel; /* Increases by one on each hop */
426 position->cell = (vm->state).cellN;
427 position->cell_restart = (vm->state).cell_restart;
428 position->cell_start = (vm->state).pgc->cell_playback[(vm->state).cellN - 1].first_sector;
429 position->still = (vm->state).pgc->cell_playback[(vm->state).cellN - 1].still_time;
430 position->block = (vm->state).blockN;
431
432 /* handle PGC stills at PGC end */
433 if ((vm->state).cellN == (vm->state).pgc->nr_of_cells)
434 position->still += (vm->state).pgc->still_time;
435 /* still already determined */
436 if (position->still)
437 return;
438 /* This is a rough fix for some strange still situations on some strange DVDs.
439 * There are discs (like the German "Back to the Future" RC2) where the only
440 * indication of a still is a cell playback time higher than the time the frames
441 * in this cell actually take to play (like 1 frame with 1 minute playback time).
442 * On the said BTTF disc, for these cells last_sector and last_vobu_start_sector
443 * are equal and the cells are very short, so we abuse these conditions to
444 * detect such discs. I consider these discs broken, so the fix is somewhat
445 * broken, too. */
446 if (((vm->state).pgc->cell_playback[(vm->state).cellN - 1].last_sector ==
447 (vm->state).pgc->cell_playback[(vm->state).cellN - 1].last_vobu_start_sector) &&
448 ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].last_sector -
449 (vm->state).pgc->cell_playback[(vm->state).cellN - 1].first_sector < 1024)) {
450 int time;
451 int size = (vm->state).pgc->cell_playback[(vm->state).cellN - 1].last_sector -
452 (vm->state).pgc->cell_playback[(vm->state).cellN - 1].first_sector;
453 time = ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.hour >> 4 ) * 36000;
454 time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.hour & 0x0f) * 3600;
455 time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.minute >> 4 ) * 600;
456 time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.minute & 0x0f) * 60;
457 time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.second >> 4 ) * 10;
458 time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.second & 0x0f) * 1;
459 if (size / time > 30)
460 /* datarate is too high, it might be a very short, but regular cell */
461 return;
462 if (time > 0xff) time = 0xff;
463 position->still = time;
464 }
465 }
466
467 void vm_get_next_cell(vm_t *vm) {
468 process_command(vm, play_Cell_post(vm));
469 }
470
471
472 /* Jumping */
473
474 int vm_jump_pg(vm_t *vm, int pg) {
475 (vm->state).pgN = pg;
476 process_command(vm, play_PG(vm));
477 return 1;
478 }
479
480 int vm_jump_cell_block(vm_t *vm, int cell, int block) {
481 (vm->state).cellN = cell;
482 process_command(vm, play_Cell(vm));
483 /* play_Cell can jump to a different cell in case of angles */
484 if ((vm->state).cellN == cell)
485 (vm->state).blockN = block;
486 return 1;
487 }
488
489 int vm_jump_title_part(vm_t *vm, int title, int part) {
490 link_t link;
491
492 if(!set_PTT(vm, title, part))
493 return 0;
494 /* Some DVDs do not want us to jump directly into a title and have
495 * PGC pre commands taking us back to some menu. Since we do not like that,
496 * we do not execute PGC pre commands that would do a jump. */
497 /* process_command(vm, play_PGC_PG(vm, (vm->state).pgN)); */
498 link = play_PGC_PG(vm, (vm->state).pgN);
499 if (link.command != PlayThis)
500 /* jump occured -> ignore it and play the PG anyway */
501 process_command(vm, play_PG(vm));
502 else
503 process_command(vm, link);
504 return 1;
505 }
506
507 int vm_jump_top_pg(vm_t *vm) {
508 process_command(vm, play_PG(vm));
509 return 1;
510 }
511
512 int vm_jump_next_pg(vm_t *vm) {
513 if((vm->state).pgN >= (vm->state).pgc->nr_of_programs) {
514 /* last program -> move to TailPGC */
515 process_command(vm, play_PGC_post(vm));
516 return 1;
517 } else {
518 vm_jump_pg(vm, (vm->state).pgN + 1);
519 return 1;
520 }
521 }
522
523 int vm_jump_prev_pg(vm_t *vm) {
524 if ((vm->state).pgN <= 1) {
525 /* first program -> move to last program of previous PGC */
526 if ((vm->state).pgc->prev_pgc_nr && set_PGCN(vm, (vm->state).pgc->prev_pgc_nr)) {
527 process_command(vm, play_PGC(vm));
528 vm_jump_pg(vm, (vm->state).pgc->nr_of_programs);
529 return 1;
530 }
531 return 0;
532 } else {
533 vm_jump_pg(vm, (vm->state).pgN - 1);
534 return 1;
535 }
536 }
537
538 int vm_jump_up(vm_t *vm) {
539 if((vm->state).pgc->goup_pgc_nr && set_PGCN(vm, (vm->state).pgc->goup_pgc_nr)) {
540 process_command(vm, play_PGC(vm));
541 return 1;
542 }
543 return 0;
544 }
545
546 int vm_jump_menu(vm_t *vm, DVDMenuID_t menuid) {
547 domain_t old_domain = (vm->state).domain;
548
549 switch ((vm->state).domain) {
550 case VTS_DOMAIN:
551 set_RSMinfo(vm, 0, (vm->state).blockN);
552 /* FALL THROUGH */
553 case VTSM_DOMAIN:
554 case VMGM_DOMAIN:
555 switch(menuid) {
556 case DVD_MENU_Title:
557 case DVD_MENU_Escape:
558 (vm->state).domain = VMGM_DOMAIN;
559 break;
560 case DVD_MENU_Root:
561 case DVD_MENU_Subpicture:
562 case DVD_MENU_Audio:
563 case DVD_MENU_Angle:
564 case DVD_MENU_Part:
565 (vm->state).domain = VTSM_DOMAIN;
566 break;
567 }
568 if(get_PGCIT(vm) && set_MENU(vm, menuid)) {
569 process_command(vm, play_PGC(vm));
570 return 1; /* Jump */
571 } else {
572 (vm->state).domain = old_domain;
573 }
574 break;
575 case FP_DOMAIN: /* FIXME XXX $$$ What should we do here? */
576 break;
577 }
578
579 return 0;
580 }
581
582 int vm_jump_resume(vm_t *vm) {
583 link_t link_values = { LinkRSM, 0, 0, 0 };
584
585 if (!(vm->state).rsm_vtsN) /* Do we have resume info? */
586 return 0;
587 if (!process_command(vm, link_values))
588 return 0;
589 return 1;
590 }
591
592 int vm_exec_cmd(vm_t *vm, vm_cmd_t *cmd) {
593 link_t link_values;
594
595 if(vmEval_CMD(cmd, 1, &(vm->state).registers, &link_values))
596 return process_command(vm, link_values);
597 else
598 return 0; /* It updated some state thats all... */
599 }
600
601
602 /* getting information */
603
604 int vm_get_current_menu(vm_t *vm, int *menuid) {
605 pgcit_t* pgcit;
606 int pgcn;
607 pgcn = (vm->state).pgcN;
608 pgcit = get_PGCIT(vm);
609 *menuid = pgcit->pgci_srp[pgcn - 1].entry_id & 0xf ;
610 return 1;
611 }
612
613 int vm_get_current_title_part(vm_t *vm, int *title_result, int *part_result) {
614 vts_ptt_srpt_t *vts_ptt_srpt;
615 int title, part = 0, vts_ttn;
616 int found;
617 int16_t pgcN, pgN;
618
619 vts_ptt_srpt = vm->vtsi->vts_ptt_srpt;
620 pgcN = get_PGCN(vm);
621 pgN = vm->state.pgN;
622
623 found = 0;
624 for (vts_ttn = 0; (vts_ttn < vts_ptt_srpt->nr_of_srpts) && !found; vts_ttn++) {
625 for (part = 0; (part < vts_ptt_srpt->title[vts_ttn].nr_of_ptts) && !found; part++) {
626 if (vts_ptt_srpt->title[vts_ttn].ptt[part].pgcn == pgcN) {
627 if (vts_ptt_srpt->title[vts_ttn].ptt[part].pgn == pgN) {
628 found = 1;
629 break;
630 }
631 if (part > 0 && vts_ptt_srpt->title[vts_ttn].ptt[part].pgn > pgN &&
632 vts_ptt_srpt->title[vts_ttn].ptt[part - 1].pgn < pgN) {
633 part--;
634 found = 1;
635 break;
636 }
637 }
638 }
639 if (found) break;
640 }
641 vts_ttn++;
642 part++;
643
644 if (!found) {
645 fprintf(MSG_OUT, "libdvdnav: chapter NOT FOUND!\n");
646 return 0;
647 }
648
649 title = get_TT(vm, vm->state.vtsN, vts_ttn);
650
651 #ifdef TRACE
652 if (title) {
653 fprintf(MSG_OUT, "libdvdnav: ************ this chapter FOUND!\n");
654 fprintf(MSG_OUT, "libdvdnav: VTS_PTT_SRPT - Title %3i part %3i: PGC: %3i PG: %3i\n",
655 title, part,
656 vts_ptt_srpt->title[vts_ttn-1].ptt[part-1].pgcn ,
657 vts_ptt_srpt->title[vts_ttn-1].ptt[part-1].pgn );
658 }
659 #endif
660 *title_result = title;
661 *part_result = part;
662 return 1;
663 }
664
665 /* Return the substream id for 'logical' audio stream audioN.
666 * 0 <= audioN < 8
667 */
668 int vm_get_audio_stream(vm_t *vm, int audioN) {
669 int streamN = -1;
670
671 if((vm->state).domain != VTS_DOMAIN)
672 audioN = 0;
673
674 if(audioN < 8) {
675 /* Is there any control info for this logical stream */
676 if((vm->state).pgc->audio_control[audioN] & (1<<15)) {
677 streamN = ((vm->state).pgc->audio_control[audioN] >> 8) & 0x07;
678 }
679 }
680
681 if((vm->state).domain != VTS_DOMAIN && streamN == -1)
682 streamN = 0;
683
684 /* FIXME: Should also check in vtsi/vmgi status what kind of stream
685 * it is (ac3/lpcm/dts/sdds...) to find the right (sub)stream id */
686 return streamN;
687 }
688
689 /* Return the substream id for 'logical' subpicture stream subpN and given mode.
690 * 0 <= subpN < 32
691 * mode == 0 - widescreen
692 * mode == 1 - letterbox
693 * mode == 2 - pan&scan
694 */
695 int vm_get_subp_stream(vm_t *vm, int subpN, int mode) {
696 int streamN = -1;
697 int source_aspect = vm_get_video_aspect(vm);
698
699 if((vm->state).domain != VTS_DOMAIN)
700 subpN = 0;
701
702 if(subpN < 32) { /* a valid logical stream */
703 /* Is this logical stream present */
704 if((vm->state).pgc->subp_control[subpN] & (1<<31)) {
705 if(source_aspect == 0) /* 4:3 */
706 streamN = ((vm->state).pgc->subp_control[subpN] >> 24) & 0x1f;
707 if(source_aspect == 3) /* 16:9 */
708 switch (mode) {
709 case 0:
710 streamN = ((vm->state).pgc->subp_control[subpN] >> 16) & 0x1f;
711 break;
712 case 1:
713 streamN = ((vm->state).pgc->subp_control[subpN] >> 8) & 0x1f;
714 break;
715 case 2:
716 streamN = (vm->state).pgc->subp_control[subpN] & 0x1f;
717 }
718 }
719 }
720
721 if((vm->state).domain != VTS_DOMAIN && streamN == -1)
722 streamN = 0;
723
724 /* FIXME: Should also check in vtsi/vmgi status what kind of stream it is. */
725 return streamN;
726 }
727
728 int vm_get_audio_active_stream(vm_t *vm) {
729 int audioN;
730 int streamN;
731 audioN = (vm->state).AST_REG ;
732 streamN = vm_get_audio_stream(vm, audioN);
733
734 /* If no such stream, then select the first one that exists. */
735 if(streamN == -1) {
736 for(audioN = 0; audioN < 8; audioN++) {
737 if((vm->state).pgc->audio_control[audioN] & (1<<15)) {
738 if ((streamN = vm_get_audio_stream(vm, audioN)) >= 0)
739 break;
740 }
741 }
742 }
743
744 return streamN;
745 }
746
747 int vm_get_subp_active_stream(vm_t *vm, int mode) {
748 int subpN;
749 int streamN;
750 subpN = (vm->state).SPST_REG & ~0x40;
751 streamN = vm_get_subp_stream(vm, subpN, mode);
752
753 /* If no such stream, then select the first one that exists. */
754 if(streamN == -1) {
755 for(subpN = 0; subpN < 32; subpN++) {
756 if((vm->state).pgc->subp_control[subpN] & (1<<31)) {
757 if ((streamN = vm_get_subp_stream(vm, subpN, mode)) >= 0)
758 break;
759 }
760 }
761 }
762
763 if((vm->state).domain == VTS_DOMAIN && !((vm->state).SPST_REG & 0x40))
764 /* Bit 7 set means hide, and only let Forced display show */
765 return (streamN | 0x80);
766 else
767 return streamN;
768 }
769
770 void vm_get_angle_info(vm_t *vm, int *current, int *num_avail) {
771 *num_avail = 1;
772 *current = 1;
773
774 if((vm->state).domain == VTS_DOMAIN) {
775 title_info_t *title;
776 /* TTN_REG does not allways point to the correct title.. */
777 if((vm->state).TTN_REG > vm->vmgi->tt_srpt->nr_of_srpts)
778 return;
779 title = &vm->vmgi->tt_srpt->title[(vm->state).TTN_REG - 1];
780 if(title->title_set_nr != (vm->state).vtsN ||
781 title->vts_ttn != (vm->state).VTS_TTN_REG)
782 return;
783 *num_avail = title->nr_of_angles;
784 *current = (vm->state).AGL_REG;
785 }
786 }
787
788 #if 0
789 /* currently unused */
790 void vm_get_audio_info(vm_t *vm, int *current, int *num_avail) {
791 switch ((vm->state).domain) {
792 case VTS_DOMAIN:
793 *num_avail = vm->vtsi->vtsi_mat->nr_of_vts_audio_streams;
794 *current = (vm->state).AST_REG;
795 break;
796 case VTSM_DOMAIN:
797 *num_avail = vm->vtsi->vtsi_mat->nr_of_vtsm_audio_streams; /* 1 */
798 *current = 1;
799 break;
800 case VMGM_DOMAIN:
801 case FP_DOMAIN:
802 *num_avail = vm->vmgi->vmgi_mat->nr_of_vmgm_audio_streams; /* 1 */
803 *current = 1;
804 break;
805 }
806 }
807
808 /* currently unused */
809 void vm_get_subp_info(vm_t *vm, int *current, int *num_avail) {
810 switch ((vm->state).domain) {
811 case VTS_DOMAIN:
812 *num_avail = vm->vtsi->vtsi_mat->nr_of_vts_subp_streams;
813 *current = (vm->state).SPST_REG;
814 break;
815 case VTSM_DOMAIN:
816 *num_avail = vm->vtsi->vtsi_mat->nr_of_vtsm_subp_streams; /* 1 */
817 *current = 0x41;
818 break;
819 case VMGM_DOMAIN:
820 case FP_DOMAIN:
821 *num_avail = vm->vmgi->vmgi_mat->nr_of_vmgm_subp_streams; /* 1 */
822 *current = 0x41;
823 break;
824 }
825 }
826
827 /* currently unused */
828 void vm_get_video_res(vm_t *vm, int *width, int *height) {
829 video_attr_t attr = vm_get_video_attr(vm);
830
831 if(attr.video_format != 0)
832 *height = 576;
833 else
834 *height = 480;
835 switch(attr.picture_size) {
836 case 0:
837 *width = 720;
838 break;
839 case 1:
840 *width = 704;
841 break;
842 case 2:
843 *width = 352;
844 break;
845 case 3:
846 *width = 352;
847 *height /= 2;
848 break;
849 }
850 }
851 #endif
852
853 int vm_get_video_aspect(vm_t *vm) {
854 int aspect = vm_get_video_attr(vm).display_aspect_ratio;
855
856 assert(aspect == 0 || aspect == 3);
857 (vm->state).registers.SPRM[14] &= ~(0x3 << 10);
858 (vm->state).registers.SPRM[14] |= aspect << 10;
859
860 return aspect;
861 }
862
863 int vm_get_video_scale_permission(vm_t *vm) {
864 return vm_get_video_attr(vm).permitted_df;
865 }
866
867 video_attr_t vm_get_video_attr(vm_t *vm) {
868 switch ((vm->state).domain) {
869 case VTS_DOMAIN:
870 return vm->vtsi->vtsi_mat->vts_video_attr;
871 case VTSM_DOMAIN:
872 return vm->vtsi->vtsi_mat->vtsm_video_attr;
873 case VMGM_DOMAIN:
874 case FP_DOMAIN:
875 return vm->vmgi->vmgi_mat->vmgm_video_attr;
876 }
877 assert(0);
878 }
879
880 audio_attr_t vm_get_audio_attr(vm_t *vm, int streamN) {
881 switch ((vm->state).domain) {
882 case VTS_DOMAIN:
883 return vm->vtsi->vtsi_mat->vts_audio_attr[streamN];
884 case VTSM_DOMAIN:
885 return vm->vtsi->vtsi_mat->vtsm_audio_attr;
886 case VMGM_DOMAIN:
887 case FP_DOMAIN:
888 return vm->vmgi->vmgi_mat->vmgm_audio_attr;
889 }
890 assert(0);
891 }
892
893 subp_attr_t vm_get_subp_attr(vm_t *vm, int streamN) {
894 switch ((vm->state).domain) {
895 case VTS_DOMAIN:
896 return vm->vtsi->vtsi_mat->vts_subp_attr[streamN];
897 case VTSM_DOMAIN:
898 return vm->vtsi->vtsi_mat->vtsm_subp_attr;
899 case VMGM_DOMAIN:
900 case FP_DOMAIN:
901 return vm->vmgi->vmgi_mat->vmgm_subp_attr;
902 }
903 assert(0);
904 }
905
906
907 /* Playback control */
908
909 static link_t play_PGC(vm_t *vm) {
910 link_t link_values;
911
912 #ifdef TRACE
913 fprintf(MSG_OUT, "libdvdnav: play_PGC:");
914 if((vm->state).domain != FP_DOMAIN) {
915 fprintf(MSG_OUT, " (vm->state).pgcN (%i)\n", get_PGCN(vm));
916 } else {
917 fprintf(MSG_OUT, " first_play_pgc\n");
918 }
919 #endif
920
921 /* This must be set before the pre-commands are executed because they
922 * might contain a CallSS that will save resume state */
923
924 /* FIXME: This may be only a temporary fix for something... */
925 (vm->state).pgN = 1;
926 (vm->state).cellN = 0;
927 (vm->state).blockN = 0;
928
929 /* eval -> updates the state and returns either
930 - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN)
931 - just play video i.e first PG
932 (This is what happens if you fall of the end of the pre_cmds)
933 - or an error (are there more cases?) */
934 if((vm->state).pgc->command_tbl && (vm->state).pgc->command_tbl->nr_of_pre) {
935 if(vmEval_CMD((vm->state).pgc->command_tbl->pre_cmds,
936 (vm->state).pgc->command_tbl->nr_of_pre,
937 &(vm->state).registers, &link_values)) {
938 /* link_values contains the 'jump' return value */
939 return link_values;
940 } else {
941 #ifdef TRACE
942 fprintf(MSG_OUT, "libdvdnav: PGC pre commands didn't do a Jump, Link or Call\n");
943 #endif
944 }
945 }
946 return play_PG(vm);
947 }
948
949 static link_t play_PGC_PG(vm_t *vm, int pgN) {
950 link_t link_values;
951
952 #ifdef TRACE
953 fprintf(MSG_OUT, "libdvdnav: play_PGC_PG:");
954 if((vm->state).domain != FP_DOMAIN) {
955 fprintf(MSG_OUT, " (vm->state).pgcN (%i)\n", get_PGCN(vm));
956 } else {
957 fprintf(MSG_OUT, " first_play_pgc\n");
958 }
959 #endif
960
961 /* This must be set before the pre-commands are executed because they
962 * might contain a CallSS that will save resume state */
963
964 /* FIXME: This may be only a temporary fix for something... */
965 (vm->state).pgN = pgN;
966 (vm->state).cellN = 0;
967 (vm->state).blockN = 0;
968
969 /* eval -> updates the state and returns either
970 - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN)
971 - just play video i.e first PG
972 (This is what happens if you fall of the end of the pre_cmds)
973 - or an error (are there more cases?) */
974 if((vm->state).pgc->command_tbl && (vm->state).pgc->command_tbl->nr_of_pre) {
975 if(vmEval_CMD((vm->state).pgc->command_tbl->pre_cmds,
976 (vm->state).pgc->command_tbl->nr_of_pre,
977 &(vm->state).registers, &link_values)) {
978 /* link_values contains the 'jump' return value */
979 return link_values;
980 } else {
981 #ifdef TRACE
982 fprintf(MSG_OUT, "libdvdnav: PGC pre commands didn't do a Jump, Link or Call\n");
983 #endif
984 }
985 }
986 return play_PG(vm);
987 }
988
989 static link_t play_PGC_post(vm_t *vm) {
990 link_t link_values;
991
992 #ifdef TRACE
993 fprintf(MSG_OUT, "libdvdnav: play_PGC_post:\n");
994 #endif
995
996 /* eval -> updates the state and returns either
997 - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN)
998 - just go to next PGC
999 (This is what happens if you fall of the end of the post_cmds)
1000 - or an error (are there more cases?) */
1001 if((vm->state).pgc->command_tbl && (vm->state).pgc->command_tbl->nr_of_post &&
1002 vmEval_CMD((vm->state).pgc->command_tbl->post_cmds,
1003 (vm->state).pgc->command_tbl->nr_of_post,
1004 &(vm->state).registers, &link_values)) {
1005 return link_values;
1006 }
1007
1008 #ifdef TRACE
1009 fprintf(MSG_OUT, "libdvdnav: ** Fell of the end of the pgc, continuing in NextPGC\n");
1010 #endif
1011 /* Should end up in the STOP_DOMAIN if next_pgc is 0. */
1012 if(!set_PGCN(vm, (vm->state).pgc->next_pgc_nr)) {
1013 link_values.command = Exit;
1014 return link_values;
1015 }
1016 return play_PGC(vm);
1017 }
1018
1019 static link_t play_PG(vm_t *vm) {
1020 #ifdef TRACE
1021 fprintf(MSG_OUT, "libdvdnav: play_PG: (vm->state).pgN (%i)\n", (vm->state).pgN);
1022 #endif
1023
1024 assert((vm->state).pgN > 0);
1025 if((vm->state).pgN > (vm->state).pgc->nr_of_programs) {
1026 #ifdef TRACE
1027 fprintf(MSG_OUT, "libdvdnav: play_PG: (vm->state).pgN (%i) > pgc->nr_of_programs (%i)\n",
1028 (vm->state).pgN, (vm->state).pgc->nr_of_programs );
1029 #endif
1030 assert((vm->state).pgN == (vm->state).pgc->nr_of_programs + 1);
1031 return play_PGC_post(vm);
1032 }
1033
1034 (vm->state).cellN = (vm->state).pgc->program_map[(vm->state).pgN - 1];
1035
1036 return play_Cell(vm);
1037 }
1038
1039 static link_t play_Cell(vm_t *vm) {
1040 static const link_t play_this = {PlayThis, /* Block in Cell */ 0, 0, 0};
1041
1042 #ifdef TRACE
1043 fprintf(MSG_OUT, "libdvdnav: play_Cell: (vm->state).cellN (%i)\n", (vm->state).cellN);
1044 #endif
1045
1046 assert((vm->state).cellN > 0);
1047 if((vm->state).cellN > (vm->state).pgc->nr_of_cells) {
1048 #ifdef TRACE
1049 fprintf(MSG_OUT, "libdvdnav: (vm->state).cellN (%i) > pgc->nr_of_cells (%i)\n",
1050 (vm->state).cellN, (vm->state).pgc->nr_of_cells );
1051 #endif
1052 assert((vm->state).cellN == (vm->state).pgc->nr_of_cells + 1);
1053 return play_PGC_post(vm);
1054 }
1055
1056 /* Multi angle/Interleaved */
1057 switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode) {
1058 case 0: /* Normal */
1059 assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 0);
1060 break;
1061 case 1: /* The first cell in the block */
1062 switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type) {
1063 case 0: /* Not part of a block */
1064 assert(0);
1065 case 1: /* Angle block */
1066 /* Loop and check each cell instead? So we don't get outside the block? */
1067 (vm->state).cellN += (vm->state).AGL_REG - 1;
1068 #ifdef STRICT
1069 assert((vm->state).cellN <= (vm->state).pgc->nr_of_cells);
1070 assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode != 0);
1071 assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 1);
1072 #else
1073 if (!((vm->state).cellN <= (vm->state).pgc->nr_of_cells) ||
1074 !((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode != 0) ||
1075 !((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 1)) {
1076 fprintf(MSG_OUT, "libdvdnav: Invalid angle block\n");
1077 (vm->state).cellN -= (vm->state).AGL_REG - 1;
1078 }
1079 #endif
1080 break;
1081 case 2: /* ?? */
1082 case 3: /* ?? */
1083 default:
1084 fprintf(MSG_OUT, "libdvdnav: Invalid? Cell block_mode (%d), block_type (%d)\n",
1085 (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode,
1086 (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type);
1087 assert(0);
1088 }
1089 break;
1090 case 2: /* Cell in the block */
1091 case 3: /* Last cell in the block */
1092 /* These might perhaps happen for RSM or LinkC commands? */
1093 default:
1094 fprintf(MSG_OUT, "libdvdnav: Cell is in block but did not enter at first cell!\n");
1095 }
1096
1097 /* Updates (vm->state).pgN and PTTN_REG */
1098 if(!set_PGN(vm)) {
1099 /* Should not happen */
1100 assert(0);
1101 return play_PGC_post(vm);
1102 }
1103 (vm->state).cell_restart++;
1104 (vm->state).blockN = 0;
1105 #ifdef TRACE
1106 fprintf(MSG_OUT, "libdvdnav: Cell should restart here\n");
1107 #endif
1108 return play_this;
1109 }
1110
1111 static link_t play_Cell_post(vm_t *vm) {
1112 cell_playback_t *cell;
1113
1114 #ifdef TRACE
1115 fprintf(MSG_OUT, "libdvdnav: play_Cell_post: (vm->state).cellN (%i)\n", (vm->state).cellN);
1116 #endif
1117
1118 cell = &(vm->state).pgc->cell_playback[(vm->state).cellN - 1];
1119
1120 /* Still time is already taken care of before we get called. */
1121
1122 /* Deal with a Cell command, if any */
1123 if(cell->cell_cmd_nr != 0) {
1124 link_t link_values;
1125
1126 /* These asserts are now not needed.
1127 * Some DVDs have no cell commands listed in the PGC,
1128 * but the Cell itself points to a cell command that does not exist.
1129 * For this situation, just ignore the cell command and continue.
1130 *
1131 * assert((vm->state).pgc->command_tbl != NULL);
1132 * assert((vm->state).pgc->command_tbl->nr_of_cell >= cell->cell_cmd_nr);
1133 */
1134
1135 if ((vm->state).pgc->command_tbl != NULL &&
1136 (vm->state).pgc->command_tbl->nr_of_cell >= cell->cell_cmd_nr) {
1137 #ifdef TRACE
1138 fprintf(MSG_OUT, "libdvdnav: Cell command present, executing\n");
1139 #endif
1140 if(vmEval_CMD(&(vm->state).pgc->command_tbl->cell_cmds[cell->cell_cmd_nr - 1], 1,
1141 &(vm->state).registers, &link_values)) {
1142 return link_values;
1143 } else {
1144 #ifdef TRACE
1145 fprintf(MSG_OUT, "libdvdnav: Cell command didn't do a Jump, Link or Call\n");
1146 #endif
1147 }
1148 } else {
1149 #ifdef TRACE
1150 fprintf(MSG_OUT, "libdvdnav: Invalid Cell command\n");
1151 #endif
1152 }
1153 }
1154
1155 /* Where to continue after playing the cell... */
1156 /* Multi angle/Interleaved */
1157 switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode) {
1158 case 0: /* Normal */
1159 assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 0);
1160 (vm->state).cellN++;
1161 break;
1162 case 1: /* The first cell in the block */
1163 case 2: /* A cell in the block */
1164 case 3: /* The last cell in the block */
1165 default:
1166 switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type) {
1167 case 0: /* Not part of a block */
1168 assert(0);
1169 case 1: /* Angle block */
1170 /* Skip the 'other' angles */
1171 (vm->state).cellN++;
1172 while((vm->state).cellN <= (vm->state).pgc->nr_of_cells &&
1173 (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode >= 2) {
1174 (vm->state).cellN++;
1175 }
1176 break;
1177 case 2: /* ?? */
1178 case 3: /* ?? */
1179 default:
1180 fprintf(MSG_OUT, "libdvdnav: Invalid? Cell block_mode (%d), block_type (%d)\n",
1181 (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode,
1182 (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type);
1183 assert(0);
1184 }
1185 break;
1186 }
1187
1188 /* Figure out the correct pgN for the new cell */
1189 if(!set_PGN(vm)) {
1190 #ifdef TRACE
1191 fprintf(MSG_OUT, "libdvdnav: last cell in this PGC\n");
1192 #endif
1193 return play_PGC_post(vm);
1194 }
1195 return play_Cell(vm);
1196 }
1197
1198
1199 /* link processing */
1200
1201 static int process_command(vm_t *vm, link_t link_values) {
1202
1203 while(link_values.command != PlayThis) {
1204
1205 #ifdef TRACE
1206 fprintf(MSG_OUT, "libdvdnav: Before printout starts:\n");
1207 vm_print_link(link_values);
1208 fprintf(MSG_OUT, "libdvdnav: Link values %i %i %i %i\n", link_values.command,
1209 link_values.data1, link_values.data2, link_values.data3);
1210 vm_print_current_domain_state(vm);
1211 fprintf(MSG_OUT, "libdvdnav: Before printout ends.\n");
1212 #endif
1213
1214 switch(link_values.command) {
1215 case LinkNoLink:
1216 /* BUTTON number:data1 */
1217 if(link_values.data1 != 0)
1218 (vm->state).HL_BTNN_REG = link_values.data1 << 10;
1219 return 0; /* no actual jump */
1220
1221 case LinkTopC:
1222 /* Restart playing from the beginning of the current Cell. */
1223 /* BUTTON number:data1 */
1224 if(link_values.data1 != 0)
1225 (vm->state).HL_BTNN_REG = link_values.data1 << 10;
1226 link_values = play_Cell(vm);
1227 break;
1228 case LinkNextC:
1229 /* Link to Next Cell */
1230 /* BUTTON number:data1 */
1231 if(link_values.data1 != 0)
1232 (vm->state).HL_BTNN_REG = link_values.data1 << 10;
1233 (vm->state).cellN += 1;
1234 link_values = play_Cell(vm);
1235 break;
1236 case LinkPrevC:
1237 /* Link to Previous Cell */
1238 /* BUTTON number:data1 */
1239 if(link_values.data1 != 0)
1240 (vm->state).HL_BTNN_REG = link_values.data1 << 10;
1241 assert((vm->state).cellN > 1);
1242 (vm->state).cellN -= 1;
1243 link_values = play_Cell(vm);
1244 break;
1245
1246 case LinkTopPG:
1247 /* Link to Top of current Program */
1248 /* BUTTON number:data1 */
1249 if(link_values.data1 != 0)
1250 (vm->state).HL_BTNN_REG = link_values.data1 << 10;
1251 link_values = play_PG(vm);
1252 break;
1253 case LinkNextPG:
1254 /* Link to Next Program */
1255 /* BUTTON number:data1 */
1256 if(link_values.data1 != 0)
1257 (vm->state).HL_BTNN_REG = link_values.data1 << 10;
1258 (vm->state).pgN += 1;
1259 link_values = play_PG(vm);
1260 break;
1261 case LinkPrevPG:
1262 /* Link to Previous Program */
1263 /* BUTTON number:data1 */
1264 if(link_values.data1 != 0)
1265 (vm->state).HL_BTNN_REG = link_values.data1 << 10;
1266 assert((vm->state).pgN > 1);
1267 (vm->state).pgN -= 1;
1268 link_values = play_PG(vm);
1269 break;
1270
1271 case LinkTopPGC:
1272 /* Restart playing from beginning of current Program Chain */
1273 /* BUTTON number:data1 */
1274 if(link_values.data1 != 0)
1275 (vm->state).HL_BTNN_REG = link_values.data1 << 10;
1276 link_values = play_PGC(vm);
1277 break;
1278 case LinkNextPGC:
1279 /* Link to Next Program Chain */
1280 /* BUTTON number:data1 */
1281 if(link_values.data1 != 0)
1282 (vm->state).HL_BTNN_REG = link_values.data1 << 10;
1283 assert((vm->state).pgc->next_pgc_nr != 0);
1284 if(!set_PGCN(vm, (vm->state).pgc->next_pgc_nr))
1285 assert(0);
1286 link_values = play_PGC(vm);
1287 break;
1288 case LinkPrevPGC:
1289 /* Link to Previous Program Chain */
1290 /* BUTTON number:data1 */
1291 if(link_values.data1 != 0)
1292 (vm->state).HL_BTNN_REG = link_values.data1 << 10;
1293 assert((vm->state).pgc->prev_pgc_nr != 0);
1294 if(!set_PGCN(vm, (vm->state).pgc->prev_pgc_nr))
1295 assert(0);
1296 link_values = play_PGC(vm);
1297 break;
1298 case LinkGoUpPGC:
1299 /* Link to GoUp Program Chain */
1300 /* BUTTON number:data1 */
1301 if(link_values.data1 != 0)
1302 (vm->state).HL_BTNN_REG = link_values.data1 << 10;
1303 assert((vm->state).pgc->goup_pgc_nr != 0);
1304 if(!set_PGCN(vm, (vm->state).pgc->goup_pgc_nr))
1305 assert(0);
1306 link_values = play_PGC(vm);
1307 break;
1308 case LinkTailPGC:
1309 /* Link to Tail of Program Chain */
1310 /* BUTTON number:data1 */
1311 if(link_values.data1 != 0)
1312 (vm->state).HL_BTNN_REG = link_values.data1 << 10;
1313 link_values = play_PGC_post(vm);
1314 break;
1315
1316 case LinkRSM:
1317 {
1318 /* Link to Resume point */
1319 int i;
1320
1321 /* Check and see if there is any rsm info!! */
1322 if (!(vm->state).rsm_vtsN) {
1323 fprintf(MSG_OUT, "libdvdnav: trying to resume without any resume info set\n");
1324 link_values.command = Exit;
1325 break;
1326 }
1327
1328 (vm->state).domain = VTS_DOMAIN;
1329 ifoOpenNewVTSI(vm, vm->dvd, (vm->state).rsm_vtsN);
1330 set_PGCN(vm, (vm->state).rsm_pgcN);
1331
1332 /* These should never be set in SystemSpace and/or MenuSpace */
1333 /* (vm->state).TTN_REG = rsm_tt; ?? */
1334 /* (vm->state).TT_PGCN_REG = (vm->state).rsm_pgcN; ?? */
1335 for(i = 0; i < 5; i++) {
1336 (vm->state).registers.SPRM[4 + i] = (vm->state).rsm_regs[i];
1337 }
1338
1339 if(link_values.data1 != 0)
1340 (vm->state).HL_BTNN_REG = link_values.data1 << 10;
1341
1342 if((vm->state).rsm_cellN == 0) {
1343 assert((vm->state).cellN); /* Checking if this ever happens */
1344 (vm->state).pgN = 1;
1345 link_values = play_PG(vm);
1346 } else {
1347 /* (vm->state).pgN = ?? this gets the righ value in set_PGN() below */
1348 (vm->state).cellN = (vm->state).rsm_cellN;
1349 link_values.command = PlayThis;
1350 link_values.data1 = (vm->state).rsm_blockN;
1351 if(!set_PGN(vm)) {
1352 /* Were at the end of the PGC, should not happen for a RSM */
1353 assert(0);
1354 link_values.command = LinkTailPGC;
1355 link_values.data1 = 0; /* No button */
1356 }
1357 }
1358 }
1359 break;
1360 case LinkPGCN:
1361 /* Link to Program Chain Number:data1 */
1362 if(!set_PGCN(vm, link_values.data1))
1363 assert(0);
1364 link_values = play_PGC(vm);
1365 break;
1366 case LinkPTTN:
1367 /* Link to Part of current Title Number:data1 */
1368 /* BUTTON number:data2 */
1369 /* PGC Pre-Commands are not executed */
1370 assert((vm->state).domain == VTS_DOMAIN);
1371 if(link_values.data2 != 0)
1372 (vm->state).HL_BTNN_REG = link_values.data2 << 10;
1373 if(!set_VTS_PTT(vm, (vm->state).vtsN, (vm->state).VTS_TTN_REG, link_values.data1))
1374 assert(0);
1375 link_values = play_PG(vm);
1376 break;
1377 case LinkPGN:
1378 /* Link to Program Number:data1 */
1379 /* BUTTON number:data2 */
1380 if(link_values.data2 != 0)
1381 (vm->state).HL_BTNN_REG = link_values.data2 << 10;
1382 /* Update any other state, PTTN perhaps? */
1383 (vm->state).pgN = link_values.data1;
1384 link_values = play_PG(vm);
1385 break;
1386 case LinkCN:
1387 /* Link to Cell Number:data1 */
1388 /* BUTTON number:data2 */
1389 if(link_values.data2 != 0)
1390 (vm->state).HL_BTNN_REG = link_values.data2 << 10;
1391 /* Update any other state, pgN, PTTN perhaps? */
1392 (vm->state).cellN = link_values.data1;
1393 link_values = play_Cell(vm);
1394 break;
1395
1396 case Exit:
1397 vm->stopped = 1;
1398 return 0;
1399
1400 case JumpTT:
1401 /* Jump to VTS Title Domain */
1402 /* Only allowed from the First Play domain(PGC) */
1403 /* or the Video Manager domain (VMG) */
1404 /* Stop SPRM9 Timer */
1405 /* Set SPRM1 and SPRM2 */
1406 assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN); /* ?? */
1407 if(!set_TT(vm, link_values.data1))
1408 assert(0);
1409 link_values = play_PGC(vm);
1410 break;
1411 case JumpVTS_TT:
1412 /* Jump to Title:data1 in same VTS Title Domain */
1413 /* Only allowed from the VTS Menu Domain(VTSM) */
1414 /* or the Video Title Set Domain(VTS) */
1415 /* Stop SPRM9 Timer */
1416 /* Set SPRM1 and SPRM2 */
1417 assert((vm->state).domain == VTSM_DOMAIN || (vm->state).domain == VTS_DOMAIN); /* ?? */
1418 if(!set_VTS_TT(vm, (vm->state).vtsN, link_values.data1))
1419 assert(0);
1420 link_values = play_PGC(vm);
1421 break;
1422 case JumpVTS_PTT:
1423 /* Jump to Part:data2 of Title:data1 in same VTS Title Domain */
1424 /* Only allowed from the VTS Menu Domain(VTSM) */
1425 /* or the Video Title Set Domain(VTS) */
1426 /* Stop SPRM9 Timer */
1427 /* Set SPRM1 and SPRM2 */
1428 assert((vm->state).domain == VTSM_DOMAIN || (vm->state).domain == VTS_DOMAIN); /* ?? */
1429 if(!set_VTS_PTT(vm, (vm->state).vtsN, link_values.data1, link_values.data2))
1430 assert(0);
1431 link_values = play_PGC_PG(vm, (vm->state).pgN);
1432 break;
1433
1434 case JumpSS_FP:
1435 /* Jump to First Play Domain */
1436 /* Only allowed from the VTS Menu Domain(VTSM) */
1437 /* or the Video Manager domain (VMG) */
1438 /* Stop SPRM9 Timer and any GPRM counters */
1439 assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == VTSM_DOMAIN); /* ?? */
1440 if (!set_FP_PGC(vm))
1441 assert(0);
1442 link_values = play_PGC(vm);
1443 break;
1444 case JumpSS_VMGM_MENU:
1445 /* Jump to Video Manger domain - Title Menu:data1 or any PGC in VMG */
1446 /* Allowed from anywhere except the VTS Title domain */
1447 /* Stop SPRM9 Timer and any GPRM counters */
1448 assert((vm->state).domain != VTS_DOMAIN); /* ?? */
1449 (vm->state).domain = VMGM_DOMAIN;
1450 if(!set_MENU(vm, link_values.data1))
1451 assert(0);
1452 link_values = play_PGC(vm);
1453 break;
1454 case JumpSS_VTSM:
1455 /* Jump to a menu in Video Title domain, */
1456 /* or to a Menu is the current VTS */
1457 /* Stop SPRM9 Timer and any GPRM counters */
1458 /* ifoOpenNewVTSI:data1 */
1459 /* VTS_TTN_REG:data2 */
1460 /* get_MENU:data3 */
1461 if(link_values.data1 != 0) {
1462 if (link_values.data1 != (vm->state).vtsN) {
1463 /* the normal case */
1464 assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN); /* ?? */
1465 (vm->state).domain = VTSM_DOMAIN;
1466 ifoOpenNewVTSI(vm, vm->dvd, link_values.data1); /* Also sets (vm->state).vtsN */
1467 } else {
1468 /* This happens on some discs like "Captain Scarlet & the Mysterons" or
1469 * the German RC2 of "Anatomie" in VTSM. */
1470 assert((vm->state).domain == VTSM_DOMAIN ||
1471 (vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN); /* ?? */
1472 (vm->state).domain = VTSM_DOMAIN;
1473 }
1474 } else {
1475 /* This happens on 'The Fifth Element' region 2. */
1476 assert((vm->state).domain == VTSM_DOMAIN);
1477 }
1478 /* I don't know what title is supposed to be used for. */
1479 /* Alien or Aliens has this != 1, I think. */
1480 /* assert(link_values.data2 == 1); */
1481 (vm->state).VTS_TTN_REG = link_values.data2;
1482 /* TTN_REG (SPRM4), VTS_TTN_REG (SPRM5), TT_PGCN_REG (SPRM6) are linked, */
1483 /* so if one changes, the others must change to match it. */
1484 (vm->state).TTN_REG = get_TT(vm, (vm->state).vtsN, (vm->state).VTS_TTN_REG);
1485 if(!set_MENU(vm, link_values.data3))
1486 assert(0);
1487 link_values = play_PGC(vm);
1488 break;
1489 case JumpSS_VMGM_PGC:
1490 /* set_PGCN:data1 */
1491 /* Stop SPRM9 Timer and any GPRM counters */
1492 assert((vm->state).domain != VTS_DOMAIN); /* ?? */
1493 (vm->state).domain = VMGM_DOMAIN;
1494 if(!set_PGCN(vm, link_values.data1))
1495 assert(0);
1496 link_values = play_PGC(vm);
1497 break;
1498
1499 case CallSS_FP:
1500 /* set_RSMinfo:data1 */
1501 assert((vm->state).domain == VTS_DOMAIN); /* ?? */
1502 /* Must be called before domain is changed */
1503 set_RSMinfo(vm, link_values.data1, /* We dont have block info */ 0);
1504 set_FP_PGC(vm);
1505 link_values = play_PGC(vm);
1506 break;
1507 case CallSS_VMGM_MENU:
1508 /* set_MENU:data1 */
1509 /* set_RSMinfo:data2 */
1510 assert((vm->state).domain == VTS_DOMAIN); /* ?? */
1511 /* Must be called before domain is changed */
1512 set_RSMinfo(vm, link_values.data2, /* We dont have block info */ 0);
1513 (vm->state).domain = VMGM_DOMAIN;
1514 if(!set_MENU(vm, link_values.data1))
1515 assert(0);
1516 link_values = play_PGC(vm);
1517 break;
1518 case CallSS_VTSM:
1519 /* set_MENU:data1 */
1520 /* set_RSMinfo:data2 */
1521 assert((vm->state).domain == VTS_DOMAIN); /* ?? */
1522 /* Must be called before domain is changed */
1523 set_RSMinfo(vm, link_values.data2, /* We dont have block info */ 0);
1524 (vm->state).domain = VTSM_DOMAIN;
1525 if(!set_MENU(vm, link_values.data1))
1526 assert(0);
1527 link_values = play_PGC(vm);
1528 break;
1529 case CallSS_VMGM_PGC:
1530 /* set_PGC:data1 */
1531 /* set_RSMinfo:data2 */
1532 assert((vm->state).domain == VTS_DOMAIN); /* ?? */
1533 /* Must be called before domain is changed */
1534 set_RSMinfo(vm, link_values.data2, /* We dont have block info */ 0);
1535 (vm->state).domain = VMGM_DOMAIN;
1536 if(!set_PGCN(vm, link_values.data1))
1537 assert(0);
1538 link_values = play_PGC(vm);
1539 break;
1540 case PlayThis:
1541 /* Should never happen. */
1542 assert(0);
1543 break;
1544 }
1545
1546 #ifdef TRACE
1547 fprintf(MSG_OUT, "libdvdnav: After printout starts:\n");
1548 vm_print_current_domain_state(vm);
1549 fprintf(MSG_OUT, "libdvdnav: After printout ends.\n");
1550 #endif
1551
1552 }
1553 (vm->state).blockN = link_values.data1;
1554 return 1;
1555 }
1556
1557
1558 /* Set functions */
1559
1560 static int set_TT(vm_t *vm, int tt) {
1561 return set_PTT(vm, tt, 1);
1562 }
1563
1564 static int set_PTT(vm_t *vm, int tt, int ptt) {
1565 assert(tt <= vm->vmgi->tt_srpt->nr_of_srpts);
1566 return set_VTS_PTT(vm, vm->vmgi->tt_srpt->title[tt - 1].title_set_nr,
1567 vm->vmgi->tt_srpt->title[tt - 1].vts_ttn, ptt);
1568 }
1569
1570 static int set_VTS_TT(vm_t *vm, int vtsN, int vts_ttn) {
1571 return set_VTS_PTT(vm, vtsN, vts_ttn, 1);
1572 }
1573
1574 static int set_VTS_PTT(vm_t *vm, int vtsN, int vts_ttn, int part) {
1575 int pgcN, pgN, res;
1576
1577 (vm->state).domain = VTS_DOMAIN;
1578
1579 if(vtsN != (vm->state).vtsN)
1580 ifoOpenNewVTSI(vm, vm->dvd, vtsN); /* Also sets (vm->state).vtsN */
1581
1582 if ((vts_ttn < 1) || (vts_ttn > vm->vtsi->vts_ptt_srpt->nr_of_srpts) ||
1583 (part < 1) || (part > vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].nr_of_ptts) ) {
1584 return 0;
1585 }
1586
1587 pgcN = vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].ptt[part - 1].pgcn;
1588 pgN = vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].ptt[part - 1].pgn;
1589
1590 (vm->state).TT_PGCN_REG = pgcN;
1591 (vm->state).PTTN_REG = part;
1592 (vm->state).TTN_REG = get_TT(vm, vtsN, vts_ttn);
1593 assert( (vm->state.TTN_REG) != 0 );
1594 (vm->state).VTS_TTN_REG = vts_ttn;
1595 (vm->state).vtsN = vtsN; /* Not sure about this one. We can get to it easily from TTN_REG */
1596 /* Any other registers? */
1597
1598 res = set_PGCN(vm, pgcN); /* This clobber's state.pgN (sets it to 1), but we don't want clobbering here. */
1599 (vm->state).pgN = pgN;
1600 return res;
1601 }
1602
1603 static int set_FP_PGC(vm_t *vm) {
1604 (vm->state).domain = FP_DOMAIN;
1605 (vm->state).pgc = vm->vmgi->first_play_pgc;
1606 (vm->state).pgcN = vm->vmgi->vmgi_mat->first_play_pgc;
1607 return 1;
1608 }
1609
1610
1611 static int set_MENU(vm_t *vm, int menu) {
1612 assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == VTSM_DOMAIN);
1613 return set_PGCN(vm, get_ID(vm, menu));
1614 }
1615
1616 static int set_PGCN(vm_t *vm, int pgcN) {
1617 pgcit_t *pgcit;
1618
1619 pgcit = get_PGCIT(vm);
1620 assert(pgcit != NULL); /* ?? Make this return -1 instead */
1621
1622 if(pgcN < 1 || pgcN > pgcit->nr_of_pgci_srp) {
1623 #ifdef TRACE
1624 fprintf(MSG_OUT, "libdvdnav: ** No such pgcN = %d\n", pgcN);
1625 #endif
1626 return 0;
1627 }
1628
1629 (vm->state).pgc = pgcit->pgci_srp[pgcN - 1].pgc;
1630 (vm->state).pgcN = pgcN;
1631 (vm->state).pgN = 1;
1632
1633 if((vm->state).domain == VTS_DOMAIN)
1634 (vm->state).TT_PGCN_REG = pgcN;
1635
1636 return 1;
1637 }
1638
1639 /* Figure out the correct pgN from the cell and update (vm->state). */
1640 static int set_PGN(vm_t *vm) {
1641 int new_pgN = 0;
1642
1643 while(new_pgN < (vm->state).pgc->nr_of_programs
1644 && (vm->state).cellN >= (vm->state).pgc->program_map[new_pgN])
1645 new_pgN++;
1646
1647 if(new_pgN == (vm->state).pgc->nr_of_programs) /* We are at the last program */
1648 if((vm->state).cellN > (vm->state).pgc->nr_of_cells)
1649 return 0; /* We are past the last cell */
1650
1651 (vm->state).pgN = new_pgN;
1652
1653 if((vm->state).domain == VTS_DOMAIN) {
1654 playback_type_t *pb_ty;
1655 if((vm->state).TTN_REG > vm->vmgi->tt_srpt->nr_of_srpts)
1656 return 0; /* ?? */
1657 pb_ty = &vm->vmgi->tt_srpt->title[(vm->state).TTN_REG - 1].pb_ty;
1658 if(pb_ty->multi_or_random_pgc_title == /* One_Sequential_PGC_Title */ 0) {
1659 int dummy, part;
1660 vm_get_current_title_part(vm, &dummy, &part);
1661 (vm->state).PTTN_REG = part;
1662 } else {
1663 /* FIXME: Handle RANDOM or SHUFFLE titles. */
1664 fprintf(MSG_OUT, "libdvdnav: RANDOM or SHUFFLE titles are NOT handled yet.\n");
1665 }
1666 }
1667 return 1;
1668 }
1669
1670 /* Must be called before domain is changed (set_PGCN()) */
1671 static void set_RSMinfo(vm_t *vm, int cellN, int blockN) {
1672 int i;
1673
1674 if(cellN) {
1675 (vm->state).rsm_cellN = cellN;
1676 (vm->state).rsm_blockN = blockN;
1677 } else {
1678 (vm->state).rsm_cellN = (vm->state).cellN;
1679 (vm->state).rsm_blockN = blockN;
1680 }
1681 (vm->state).rsm_vtsN = (vm->state).vtsN;
1682 (vm->state).rsm_pgcN = get_PGCN(vm);
1683
1684 /* assert((vm->state).rsm_pgcN == (vm->state).TT_PGCN_REG); for VTS_DOMAIN */
1685
1686 for(i = 0; i < 5; i++) {
1687 (vm->state).rsm_regs[i] = (vm->state).registers.SPRM[4 + i];
1688 }
1689 }
1690
1691
1692 /* Get functions */
1693
1694 /* Searches the TT tables, to find the current TT.
1695 * returns the current TT.
1696 * returns 0 if not found.
1697 */
1698 static int get_TT(vm_t *vm, int vtsN, int vts_ttn) {
1699 int i;
1700 int tt=0;
1701
1702 for(i = 1; i <= vm->vmgi->tt_srpt->nr_of_srpts; i++) {
1703 if( vm->vmgi->tt_srpt->title[i - 1].title_set_nr == vtsN &&
1704 vm->vmgi->tt_srpt->title[i - 1].vts_ttn == vts_ttn) {
1705 tt=i;
1706 break;
1707 }
1708 }
1709 return tt;
1710 }
1711
1712 /* Search for entry_id match of the PGC Category in the current VTS PGCIT table.
1713 * Return pgcN based on entry_id match.
1714 */
1715 static int get_ID(vm_t *vm, int id) {
1716 int pgcN, i;
1717 pgcit_t *pgcit;
1718
1719 /* Relies on state to get the correct pgcit. */
1720 pgcit = get_PGCIT(vm);
1721 assert(pgcit != NULL);
1722 #ifdef TRACE
1723 fprintf(MSG_OUT, "libdvdnav: ** Searching for menu (0x%x) entry PGC\n", id);
1724 #endif
1725
1726 /* Force high bit set. */
1727 id |=0x80;
1728
1729 /* Get menu/title */
1730 for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
1731 if( (pgcit->pgci_srp[i].entry_id) == id) {
1732 pgcN = i + 1;
1733 #ifdef TRACE
1734 fprintf(MSG_OUT, "libdvdnav: Found menu.\n");
1735 #endif
1736 return pgcN;
1737 }
1738 }
1739 #ifdef TRACE
1740 fprintf(MSG_OUT, "libdvdnav: ** No such id/menu (0x%02x) entry PGC\n", id & 0x7f);
1741 for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
1742 if ( (pgcit->pgci_srp[i].entry_id & 0x80) == 0x80) {
1743 fprintf(MSG_OUT, "libdvdnav: Available menus: 0x%x\n",
1744 pgcit->pgci_srp[i].entry_id & 0x7f);
1745 }
1746 }
1747 #endif
1748 return 0; /* error */
1749 }
1750
1751 /* FIXME: we have a pgcN member in the vm's state now, so this should be obsolete */
1752 static int get_PGCN(vm_t *vm) {
1753 pgcit_t *pgcit;
1754 int pgcN = 1;
1755
1756 pgcit = get_PGCIT(vm);
1757
1758 if (pgcit) {
1759 while(pgcN <= pgcit->nr_of_pgci_srp) {
1760 if(pgcit->pgci_srp[pgcN - 1].pgc == (vm->state).pgc) {
1761 assert((vm->state).pgcN == pgcN);
1762 return pgcN;
1763 }
1764 pgcN++;
1765 }
1766 }
1767 fprintf(MSG_OUT, "libdvdnav: get_PGCN failed. Was trying to find pgcN in domain %d\n",
1768 (vm->state).domain);
1769 return 0; /* error */
1770 }
1771
1772 static pgcit_t* get_MENU_PGCIT(vm_t *vm, ifo_handle_t *h, uint16_t lang) {
1773 int i;
1774
1775 if(h == NULL || h->pgci_ut == NULL) {
1776 fprintf(MSG_OUT, "libdvdnav: *** pgci_ut handle is NULL ***\n");
1777 return NULL; /* error? */
1778 }
1779
1780 i = 0;
1781 while(i < h->pgci_ut->nr_of_lus
1782 && h->pgci_ut->lu[i].lang_code != lang)
1783 i++;
1784 if(i == h->pgci_ut->nr_of_lus) {
1785 fprintf(MSG_OUT, "libdvdnav: Language '%c%c' not found, using '%c%c' instead\n",
1786 (char)(lang >> 8), (char)(lang & 0xff),
1787 (char)(h->pgci_ut->lu[0].lang_code >> 8),
1788 (char)(h->pgci_ut->lu[0].lang_code & 0xff));
1789 fprintf(MSG_OUT, "libdvdnav: Menu Languages available: ");
1790 for(i = 0; i < h->pgci_ut->nr_of_lus; i++) {
1791 fprintf(MSG_OUT, "%c%c ",
1792 (char)(h->pgci_ut->lu[i].lang_code >> 8),
1793 (char)(h->pgci_ut->lu[i].lang_code & 0xff));
1794 }
1795 fprintf(MSG_OUT, "\n");
1796 i = 0; /* error? */
1797 }
1798
1799 return h->pgci_ut->lu[i].pgcit;
1800 }
1801
1802 /* Uses state to decide what to return */
1803 static pgcit_t* get_PGCIT(vm_t *vm) {
1804 pgcit_t *pgcit;
1805
1806 switch ((vm->state).domain) {
1807 case VTS_DOMAIN:
1808 pgcit = vm->vtsi->vts_pgcit;
1809 break;
1810 case VTSM_DOMAIN:
1811 pgcit = get_MENU_PGCIT(vm, vm->vtsi, (vm->state).registers.SPRM[0]);
1812 break;
1813 case VMGM_DOMAIN:
1814 case FP_DOMAIN:
1815 pgcit = get_MENU_PGCIT(vm, vm->vmgi, (vm->state).registers.SPRM[0]);
1816 break;
1817 default:
1818 pgcit = NULL; /* Should never hapen */
1819 fprintf(MSG_OUT, "libdvdnav: get_PGCIT: Unknown domain:%d\n",
1820 (vm->state).domain);
1821 assert(0);
1822 break;
1823 }
1824
1825 return pgcit;
1826 }
1827
1828
1829 /* Debug functions */
1830
1831 #ifdef TRACE
1832 void vm_position_print(vm_t *vm, vm_position_t *position) {
1833 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",
1834 position->button,
1835 position->spu_channel,
1836 position->audio_channel,
1837 position->angle_channel,
1838 position->hop_channel,
1839 position->vts,
1840 position->domain,
1841 position->cell,
1842 position->cell_restart,
1843 position->cell_start,
1844 position->still,
1845 position->block);
1846 }
1847 #endif
1848