Mercurial > libdvdnav.hg
annotate vm.c @ 3:328eadb3f37e src
Added initial example programs directory and make sure all debug/error output goes to stderr.
author | richwareham |
---|---|
date | Mon, 01 Apr 2002 18:56:28 +0000 |
parents | 3ddf0eaece51 |
children | 99bed5d6db2f |
rev | line source |
---|---|
0 | 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 | |
37 #include <dvdread/ifo_types.h> | |
38 #include <dvdread/ifo_read.h> | |
39 | |
40 #include "decoder.h" | |
41 #include "vmcmd.h" | |
42 #include "vm.h" | |
43 | |
44 /* Local prototypes */ | |
45 | |
46 static void saveRSMinfo(vm_t *self,int cellN, int blockN); | |
47 static int set_PGN(vm_t *self); | |
48 static link_t play_PGC(vm_t *self); | |
49 static link_t play_PG(vm_t *self); | |
50 static link_t play_Cell(vm_t *self); | |
51 static link_t play_Cell_post(vm_t *self); | |
52 static link_t play_PGC_post(vm_t *self); | |
53 static link_t process_command(vm_t *self,link_t link_values); | |
54 | |
55 static void ifoOpenNewVTSI(vm_t *self,dvd_reader_t *dvd, int vtsN); | |
56 static pgcit_t* get_PGCIT(vm_t *self); | |
57 static int get_video_aspect(vm_t *self); | |
58 | |
59 /* Can only be called when in VTS_DOMAIN */ | |
60 static int get_TT(vm_t *self,int tt); | |
61 static int get_VTS_TT(vm_t *self,int vtsN, int vts_ttn); | |
62 static int get_VTS_PTT(vm_t *self,int vtsN, int vts_ttn, int part); | |
63 | |
64 static int get_MENU(vm_t *self,int menu); /* VTSM & VMGM */ | |
65 static int get_FP_PGC(vm_t *self); /* FP */ | |
66 | |
67 /* Called in any domain */ | |
68 static int get_ID(vm_t *self,int id); | |
69 static int get_PGC(vm_t *self,int pgcN); | |
70 static int get_PGCN(vm_t *self); | |
71 | |
72 /* Initialisation */ | |
73 | |
74 vm_t* vm_new_vm() { | |
75 vm_t *vm = (vm_t*)calloc(sizeof(vm_t), sizeof(char)); | |
76 | |
77 return vm; | |
78 } | |
79 | |
80 static void vm_print_current_domain_state(vm_t *self) { | |
81 switch((self->state).domain) { | |
82 case VTS_DOMAIN: | |
83 fprintf(stderr, "Video Title Domain: -\n"); | |
84 break; | |
85 | |
86 case VTSM_DOMAIN: | |
87 fprintf(stderr, "Video Title Menu Domain: -\n"); | |
88 break; | |
89 | |
90 case VMGM_DOMAIN: | |
91 fprintf(stderr, "Video Manager Menu Domain: -\n"); | |
92 break; | |
93 | |
94 case FP_DOMAIN: | |
95 fprintf(stderr, "First Play Domain: -\n"); | |
96 break; | |
97 | |
98 default: | |
99 fprintf(stderr, "Unknown Domain: -\n"); | |
100 break; | |
101 } | |
102 fprintf(stderr, "VTS:%d PG:%u CELL:%u BLOCK:%u VTS_TTN:%u TTN:%u TT_PGCN:%u\n", | |
103 (self->state).vtsN, | |
104 (self->state).pgN, | |
105 (self->state).cellN, | |
106 (self->state).blockN, | |
107 (self->state).VTS_TTN_REG, | |
108 (self->state).TTN_REG, | |
109 (self->state).TT_PGCN_REG); | |
110 } | |
111 | |
112 void vm_stop(vm_t *self) { | |
113 if(!self) | |
114 return; | |
115 | |
116 if(self->vmgi) { | |
117 ifoClose(self->vmgi); | |
118 } | |
119 | |
120 if(self->vtsi) { | |
121 ifoClose(self->vtsi); | |
122 } | |
123 | |
124 if(self->dvd) { | |
125 DVDClose(self->dvd); | |
126 } | |
127 } | |
128 | |
129 void vm_free_vm(vm_t *self) { | |
130 if(self) { | |
131 vm_stop(self); | |
132 free(self); | |
133 } | |
134 } | |
135 | |
136 /* IFO Access */ | |
137 | |
138 ifo_handle_t *vm_get_vmgi(vm_t *self) { | |
139 if(!self) | |
140 return NULL; | |
141 | |
142 return self->vmgi; | |
143 } | |
144 | |
145 ifo_handle_t *vm_get_vtsi(vm_t *self) { | |
146 if(!self) | |
147 return NULL; | |
148 | |
149 return self->vtsi; | |
150 } | |
151 | |
152 /* Reader Access */ | |
153 | |
154 dvd_reader_t *vm_get_dvd_reader(vm_t *self) { | |
155 if(!self) | |
156 return NULL; | |
157 | |
158 return self->dvd; | |
159 } | |
160 | |
161 int vm_reset(vm_t *self, char *dvdroot) /* , register_t regs) */ { | |
162 /* Setup State */ | |
163 memset((self->state).registers.SPRM, 0, sizeof(uint16_t)*24); | |
164 memset((self->state).registers.GPRM, 0, sizeof((self->state).registers.GPRM)); | |
165 (self->state).registers.SPRM[0] = ('e'<<8)|'n'; /* Player Menu Languange code */ | |
166 (self->state).AST_REG = 15; /* 15 why? */ | |
167 (self->state).SPST_REG = 62; /* 62 why? */ | |
168 (self->state).AGL_REG = 1; | |
169 (self->state).TTN_REG = 1; | |
170 (self->state).VTS_TTN_REG = 1; | |
171 /* (self->state).TT_PGCN_REG = 0 */ | |
172 (self->state).PTTN_REG = 1; | |
173 (self->state).HL_BTNN_REG = 1 << 10; | |
174 | |
175 (self->state).PTL_REG = 15; /* Parental Level */ | |
176 (self->state).registers.SPRM[12] = ('U'<<8)|'S'; /* Parental Management Country Code */ | |
177 (self->state).registers.SPRM[16] = ('e'<<8)|'n'; /* Initial Language Code for Audio */ | |
178 (self->state).registers.SPRM[18] = ('e'<<8)|'n'; /* Initial Language Code for Spu */ | |
179 /* Player Regional Code Mask. | |
180 * bit0 = Region 1 | |
181 * bit1 = Region 2 | |
182 */ | |
183 (self->state).registers.SPRM[20] = 0x1; /* Player Regional Code Mask. Region free! */ | |
184 (self->state).registers.SPRM[14] = 0x100; /* Try Pan&Scan */ | |
185 | |
186 (self->state).pgN = 0; | |
187 (self->state).cellN = 0; | |
188 | |
189 (self->state).domain = FP_DOMAIN; | |
190 (self->state).rsm_vtsN = 0; | |
191 (self->state).rsm_cellN = 0; | |
192 (self->state).rsm_blockN = 0; | |
193 | |
194 (self->state).vtsN = -1; | |
195 | |
196 self->dvd = DVDOpen(dvdroot); | |
197 if(!self->dvd) { | |
198 fprintf(stderr, "vm: faild to open/read the DVD\n"); | |
199 return -1; | |
200 } | |
201 | |
202 self->vmgi = ifoOpenVMGI(self->dvd); | |
203 if(!self->vmgi) { | |
204 fprintf(stderr, "vm: faild to read VIDEO_TS.IFO\n"); | |
205 return -1; | |
206 } | |
207 if(!ifoRead_FP_PGC(self->vmgi)) { | |
208 fprintf(stderr, "vm: ifoRead_FP_PGC failed\n"); | |
209 return -1; | |
210 } | |
211 if(!ifoRead_TT_SRPT(self->vmgi)) { | |
212 fprintf(stderr, "vm: ifoRead_TT_SRPT failed\n"); | |
213 return -1; | |
214 } | |
215 if(!ifoRead_PGCI_UT(self->vmgi)) { | |
216 fprintf(stderr, "vm: ifoRead_PGCI_UT failed\n"); | |
217 return -1; | |
218 } | |
219 if(!ifoRead_PTL_MAIT(self->vmgi)) { | |
220 fprintf(stderr, "vm: ifoRead_PTL_MAIT failed\n"); | |
221 ; /* return -1; Not really used for now.. */ | |
222 } | |
223 if(!ifoRead_VTS_ATRT(self->vmgi)) { | |
224 fprintf(stderr, "vm: ifoRead_VTS_ATRT failed\n"); | |
225 ; /* return -1; Not really used for now.. */ | |
226 } | |
227 if(!ifoRead_VOBU_ADMAP(self->vmgi)) { | |
228 fprintf(stderr, "vm: ifoRead_VOBU_ADMAP vgmi failed\n"); | |
229 ; /* return -1; Not really used for now.. */ | |
230 } | |
231 /* ifoRead_TXTDT_MGI(vmgi); Not implemented yet */ | |
232 | |
233 return 0; | |
234 } | |
235 | |
236 /* FIXME TODO XXX $$$ Handle error condition too... */ | |
237 int vm_start(vm_t *self) | |
238 { | |
239 link_t link_values; | |
240 | |
241 /* Set pgc to FP pgc */ | |
242 get_FP_PGC(self); | |
243 link_values = play_PGC(self); | |
244 link_values = process_command(self,link_values); | |
245 assert(link_values.command == PlayThis); | |
246 (self->state).blockN = link_values.data1; | |
247 | |
248 return 0; /* ?? */ | |
249 } | |
250 | |
251 int vm_start_title(vm_t *self, int tt) { | |
252 link_t link_values; | |
253 | |
254 get_TT(self, tt); | |
255 link_values = play_PGC(self); | |
256 link_values = process_command(self, link_values); | |
257 assert(link_values.command == PlayThis); | |
258 (self->state).blockN = link_values.data1; | |
259 | |
260 return 0; /* ?? */ | |
261 } | |
262 | |
263 int vm_jump_prog(vm_t *self, int pr) { | |
264 link_t link_values; | |
265 | |
266 (self->state).pgN = pr; /* ?? */ | |
267 | |
268 get_PGC(self, get_PGCN(self)); | |
269 link_values = play_PG(self); | |
270 link_values = process_command(self, link_values); | |
271 assert(link_values.command == PlayThis); | |
272 (self->state).blockN = link_values.data1; | |
273 | |
274 return 0; /* ?? */ | |
275 } | |
276 | |
277 int vm_eval_cmd(vm_t *self, vm_cmd_t *cmd) | |
278 { | |
279 link_t link_values; | |
280 | |
281 if(vmEval_CMD(cmd, 1, &(self->state).registers, &link_values)) { | |
282 link_values = process_command(self, link_values); | |
283 assert(link_values.command == PlayThis); | |
284 (self->state).blockN = link_values.data1; | |
285 return 1; /* Something changed, Jump */ | |
286 } else { | |
287 return 0; /* It updated some state thats all... */ | |
288 } | |
289 } | |
290 | |
291 int vm_get_next_cell(vm_t *self) | |
292 { | |
293 link_t link_values; | |
294 link_values = play_Cell_post(self); | |
295 link_values = process_command(self,link_values); | |
296 assert(link_values.command == PlayThis); | |
297 (self->state).blockN = link_values.data1; | |
298 | |
299 return 0; /* ?? */ | |
300 } | |
301 | |
302 int vm_top_pg(vm_t *self) | |
303 { | |
304 link_t link_values; | |
305 link_values = play_PG(self); | |
306 link_values = process_command(self,link_values); | |
307 assert(link_values.command == PlayThis); | |
308 (self->state).blockN = link_values.data1; | |
309 | |
310 return 1; /* Jump */ | |
311 } | |
312 | |
313 int vm_go_up(vm_t *self) | |
314 { | |
315 link_t link_values; | |
316 | |
317 if(get_PGC(self, (self->state).pgc->goup_pgc_nr)) | |
318 assert(0); | |
319 | |
320 link_values = play_PGC(self); | |
321 link_values = process_command(self,link_values); | |
322 assert(link_values.command == PlayThis); | |
323 (self->state).blockN = link_values.data1; | |
324 | |
325 return 1; /* Jump */ | |
326 } | |
327 | |
328 int vm_next_pg(vm_t *self) | |
329 { | |
330 /* Do we need to get a updated pgN value first? */ | |
331 (self->state).pgN += 1; | |
332 return vm_top_pg(self); | |
333 } | |
334 | |
335 int vm_prev_pg(vm_t *self) | |
336 { | |
337 /* Do we need to get a updated pgN value first? */ | |
338 (self->state).pgN -= 1; | |
339 if((self->state).pgN == 0) { | |
340 /* Check for previous PGCN ? */ | |
341 (self->state).pgN = 1; | |
342 /* return 0; */ | |
343 } | |
344 return vm_top_pg(self); | |
345 } | |
346 | |
347 | |
348 static domain_t menuid2domain(DVDMenuID_t menuid) | |
349 { | |
350 domain_t result = VTSM_DOMAIN; /* Really shouldn't have to.. */ | |
351 | |
352 switch(menuid) { | |
353 case DVD_MENU_Title: | |
354 result = VMGM_DOMAIN; | |
355 break; | |
356 case DVD_MENU_Root: | |
357 case DVD_MENU_Subpicture: | |
358 case DVD_MENU_Audio: | |
359 case DVD_MENU_Angle: | |
360 case DVD_MENU_Part: | |
361 result = VTSM_DOMAIN; | |
362 break; | |
363 } | |
364 | |
365 return result; | |
366 } | |
367 | |
368 int vm_menu_call(vm_t *self, DVDMenuID_t menuid, int block) | |
369 { | |
370 domain_t old_domain; | |
371 link_t link_values; | |
372 | |
373 /* Should check if we are allowed/can acces this menu */ | |
374 | |
375 | |
376 /* FIXME XXX $$$ How much state needs to be restored | |
377 * when we fail to find a menu? */ | |
378 | |
379 old_domain = (self->state).domain; | |
380 | |
381 switch((self->state).domain) { | |
382 case VTS_DOMAIN: | |
383 saveRSMinfo(self, 0, block); | |
384 /* FALL THROUGH */ | |
385 case VTSM_DOMAIN: | |
386 case VMGM_DOMAIN: | |
387 (self->state).domain = menuid2domain(menuid); | |
388 if(get_PGCIT(self) != NULL && get_MENU(self, menuid) != -1) { | |
389 link_values = play_PGC(self); | |
390 link_values = process_command(self, link_values); | |
391 assert(link_values.command == PlayThis); | |
392 (self->state).blockN = link_values.data1; | |
393 return 1; /* Jump */ | |
394 } else { | |
395 (self->state).domain = old_domain; | |
396 } | |
397 break; | |
398 case FP_DOMAIN: /* FIXME XXX $$$ What should we do here? */ | |
399 break; | |
400 } | |
401 | |
402 return 0; | |
403 } | |
404 | |
405 | |
406 int vm_resume(vm_t *self) | |
407 { | |
408 int i; | |
409 link_t link_values; | |
410 | |
411 /* Check and see if there is any rsm info!! */ | |
412 if((self->state).rsm_vtsN == 0) { | |
413 return 0; | |
414 } | |
415 | |
416 (self->state).domain = VTS_DOMAIN; | |
417 ifoOpenNewVTSI(self, self->dvd, (self->state).rsm_vtsN); | |
418 get_PGC(self, (self->state).rsm_pgcN); | |
419 | |
420 /* These should never be set in SystemSpace and/or MenuSpace */ | |
421 /* (self->state).TTN_REG = (self->state).rsm_tt; */ | |
422 /* (self->state).TT_PGCN_REG = (self->state).rsm_pgcN; */ | |
423 /* (self->state).HL_BTNN_REG = (self->state).rsm_btnn; */ | |
424 for(i = 0; i < 5; i++) { | |
425 (self->state).registers.SPRM[4 + i] = (self->state).rsm_regs[i]; | |
426 } | |
427 | |
428 if((self->state).rsm_cellN == 0) { | |
429 assert((self->state).cellN); /* Checking if this ever happens */ | |
430 (self->state).pgN = 1; | |
431 link_values = play_PG(self); | |
432 link_values = process_command(self, link_values); | |
433 assert(link_values.command == PlayThis); | |
434 (self->state).blockN = link_values.data1; | |
435 } else { | |
436 (self->state).cellN = (self->state).rsm_cellN; | |
437 (self->state).blockN = (self->state).rsm_blockN; | |
438 /* (self->state).pgN = ?? does this gets the righ value in play_Cell, no! */ | |
439 if(set_PGN(self)) { | |
440 /* Were at or past the end of the PGC, should not happen for a RSM */ | |
441 assert(0); | |
442 play_PGC_post(self); | |
443 } | |
444 } | |
445 | |
446 return 1; /* Jump */ | |
447 } | |
448 | |
449 /** | |
450 * Return the substream id for 'logical' audio stream audioN. | |
451 * 0 <= audioN < 8 | |
452 */ | |
453 int vm_get_audio_stream(vm_t *self, int audioN) | |
454 { | |
455 int streamN = -1; | |
3
328eadb3f37e
Added initial example programs directory and make sure all debug/error output goes to stderr.
richwareham
parents:
0
diff
changeset
|
456 fprintf(stderr,"dvdnav:vm.c:get_audio_stream audioN=%d\n",audioN); |
0 | 457 if((self->state).domain == VTSM_DOMAIN |
458 || (self->state).domain == VMGM_DOMAIN | |
459 || (self->state).domain == FP_DOMAIN) { | |
460 audioN = 0; | |
461 } | |
462 | |
463 if(audioN < 8) { | |
464 /* Is there any contol info for this logical stream */ | |
465 if((self->state).pgc->audio_control[audioN] & (1<<15)) { | |
466 streamN = ((self->state).pgc->audio_control[audioN] >> 8) & 0x07; | |
467 } | |
468 } | |
469 | |
470 if((self->state).domain == VTSM_DOMAIN | |
471 || (self->state).domain == VMGM_DOMAIN | |
472 || (self->state).domain == FP_DOMAIN) { | |
473 if(streamN == -1) | |
474 streamN = 0; | |
475 } | |
476 | |
477 /* Should also check in vtsi/vmgi status that what kind of stream | |
478 * it is (ac3/lpcm/dts/sdds...) to find the right (sub)stream id */ | |
479 return streamN; | |
480 } | |
481 | |
482 /** | |
483 * Return the substream id for 'logical' subpicture stream subpN. | |
484 * 0 <= subpN < 32 | |
485 */ | |
486 int vm_get_subp_stream(vm_t *self, int subpN) | |
487 { | |
488 int streamN = -1; | |
489 int source_aspect = get_video_aspect(self); | |
490 | |
491 if((self->state).domain == VTSM_DOMAIN | |
492 || (self->state).domain == VMGM_DOMAIN | |
493 || (self->state).domain == FP_DOMAIN) { | |
494 subpN = 0; | |
495 } | |
496 | |
497 if(subpN < 32) { /* a valid logical stream */ | |
498 /* Is this logical stream present */ | |
499 if((self->state).pgc->subp_control[subpN] & (1<<31)) { | |
500 if(source_aspect == 0) /* 4:3 */ | |
501 streamN = ((self->state).pgc->subp_control[subpN] >> 24) & 0x1f; | |
502 if(source_aspect == 3) /* 16:9 */ | |
503 streamN = ((self->state).pgc->subp_control[subpN] >> 16) & 0x1f; | |
504 } | |
505 } | |
506 | |
507 /* Paranoia.. if no stream select 0 anyway */ | |
508 /* I am not paranoid */ | |
509 /* if((self->state).domain == VTSM_DOMAIN | |
510 || (self->state).domain == VMGM_DOMAIN | |
511 || (self->state).domain == FP_DOMAIN) { | |
512 if(streamN == -1) | |
513 streamN = 0; | |
514 } | |
515 */ | |
516 /* Should also check in vtsi/vmgi status that what kind of stream it is. */ | |
517 return streamN; | |
518 } | |
519 | |
520 int vm_get_subp_active_stream(vm_t *self) | |
521 { | |
522 int subpN; | |
523 int streamN; | |
524 subpN = (self->state).SPST_REG & ~0x40; | |
525 streamN = vm_get_subp_stream(self, subpN); | |
526 | |
527 /* If no such stream, then select the first one that exists. */ | |
528 if(streamN == -1) { | |
529 for(subpN = 0; subpN < 32; subpN++) { | |
530 if((self->state).pgc->subp_control[subpN] & (1<<31)) { | |
531 | |
532 streamN = vm_get_subp_stream(self, subpN); | |
533 break; | |
534 } | |
535 } | |
536 } | |
537 | |
538 /* We should instead send the on/off status to the spudecoder / mixer */ | |
539 /* If we are in the title domain see if the spu mixing is on */ | |
540 if((self->state).domain == VTS_DOMAIN && !((self->state).SPST_REG & 0x40)) { | |
541 /* Bit 7 set means hide, and only let Forced display show */ | |
542 return (streamN | 0x80); | |
543 } else { | |
544 return streamN; | |
545 } | |
546 } | |
547 | |
548 int vm_get_audio_active_stream(vm_t *self) | |
549 { | |
550 int audioN; | |
551 int streamN; | |
552 audioN = (self->state).AST_REG ; | |
553 streamN = vm_get_audio_stream(self, audioN); | |
554 | |
555 /* If no such stream, then select the first one that exists. */ | |
556 if(streamN == -1) { | |
557 for(audioN = 0; audioN < 8; audioN++) { | |
558 if((self->state).pgc->audio_control[audioN] & (1<<15)) { | |
559 streamN = vm_get_audio_stream(self, audioN); | |
560 break; | |
561 } | |
562 } | |
563 } | |
564 | |
565 return streamN; | |
566 } | |
567 | |
568 | |
569 void vm_get_angle_info(vm_t *self, int *num_avail, int *current) | |
570 { | |
571 *num_avail = 1; | |
572 *current = 1; | |
573 | |
574 if((self->state).domain == VTS_DOMAIN) { | |
575 /* TTN_REG does not allways point to the correct title.. */ | |
576 title_info_t *title; | |
577 if((self->state).TTN_REG > self->vmgi->tt_srpt->nr_of_srpts) | |
578 return; | |
579 title = &self->vmgi->tt_srpt->title[(self->state).TTN_REG - 1]; | |
580 if(title->title_set_nr != (self->state).vtsN || | |
581 title->vts_ttn != (self->state).VTS_TTN_REG) | |
582 return; | |
583 *num_avail = title->nr_of_angles; | |
584 *current = (self->state).AGL_REG; | |
585 if(*current > *num_avail) /* Is this really a good idea? */ | |
586 *current = *num_avail; | |
587 } | |
588 } | |
589 | |
590 | |
591 void vm_get_audio_info(vm_t *self, int *num_avail, int *current) | |
592 { | |
593 if((self->state).domain == VTS_DOMAIN) { | |
594 *num_avail = self->vtsi->vtsi_mat->nr_of_vts_audio_streams; | |
595 *current = (self->state).AST_REG; | |
596 } else if((self->state).domain == VTSM_DOMAIN) { | |
597 *num_avail = self->vtsi->vtsi_mat->nr_of_vtsm_audio_streams; /* 1 */ | |
598 *current = 1; | |
599 } else if((self->state).domain == VMGM_DOMAIN || (self->state).domain == FP_DOMAIN) { | |
600 *num_avail = self->vmgi->vmgi_mat->nr_of_vmgm_audio_streams; /* 1 */ | |
601 *current = 1; | |
602 } | |
603 } | |
604 | |
605 void vm_get_subp_info(vm_t *self, int *num_avail, int *current) | |
606 { | |
607 if((self->state).domain == VTS_DOMAIN) { | |
608 *num_avail = self->vtsi->vtsi_mat->nr_of_vts_subp_streams; | |
609 *current = (self->state).SPST_REG; | |
610 } else if((self->state).domain == VTSM_DOMAIN) { | |
611 *num_avail = self->vtsi->vtsi_mat->nr_of_vtsm_subp_streams; /* 1 */ | |
612 *current = 0x41; | |
613 } else if((self->state).domain == VMGM_DOMAIN || (self->state).domain == FP_DOMAIN) { | |
614 *num_avail = self->vmgi->vmgi_mat->nr_of_vmgm_subp_streams; /* 1 */ | |
615 *current = 0x41; | |
616 } | |
617 } | |
618 | |
619 subp_attr_t vm_get_subp_attr(vm_t *self, int streamN) | |
620 { | |
621 subp_attr_t attr; | |
622 | |
623 if((self->state).domain == VTS_DOMAIN) { | |
624 attr = self->vtsi->vtsi_mat->vts_subp_attr[streamN]; | |
625 } else if((self->state).domain == VTSM_DOMAIN) { | |
626 attr = self->vtsi->vtsi_mat->vtsm_subp_attr; | |
627 } else if((self->state).domain == VMGM_DOMAIN || (self->state).domain == FP_DOMAIN) { | |
628 attr = self->vmgi->vmgi_mat->vmgm_subp_attr; | |
629 } | |
630 return attr; | |
631 } | |
632 | |
633 audio_attr_t vm_get_audio_attr(vm_t *self, int streamN) | |
634 { | |
635 audio_attr_t attr; | |
636 | |
637 if((self->state).domain == VTS_DOMAIN) { | |
638 attr = self->vtsi->vtsi_mat->vts_audio_attr[streamN]; | |
639 } else if((self->state).domain == VTSM_DOMAIN) { | |
640 attr = self->vtsi->vtsi_mat->vtsm_audio_attr; | |
641 } else if((self->state).domain == VMGM_DOMAIN || (self->state).domain == FP_DOMAIN) { | |
642 attr = self->vmgi->vmgi_mat->vmgm_audio_attr; | |
643 } | |
644 return attr; | |
645 } | |
646 | |
647 video_attr_t vm_get_video_attr(vm_t *self) | |
648 { | |
649 video_attr_t attr; | |
650 | |
651 if((self->state).domain == VTS_DOMAIN) { | |
652 attr = self->vtsi->vtsi_mat->vts_video_attr; | |
653 } else if((self->state).domain == VTSM_DOMAIN) { | |
654 attr = self->vtsi->vtsi_mat->vtsm_video_attr; | |
655 } else if((self->state).domain == VMGM_DOMAIN || (self->state).domain == FP_DOMAIN) { | |
656 attr = self->vmgi->vmgi_mat->vmgm_video_attr; | |
657 } | |
658 return attr; | |
659 } | |
660 | |
661 void vm_get_video_res(vm_t *self, int *width, int *height) | |
662 { | |
663 video_attr_t attr; | |
664 | |
665 attr = vm_get_video_attr(self); | |
666 | |
667 if(attr.video_format != 0) | |
668 *height = 576; | |
669 else | |
670 *height = 480; | |
671 switch(attr.picture_size) { | |
672 case 0: | |
673 *width = 720; | |
674 break; | |
675 case 1: | |
676 *width = 704; | |
677 break; | |
678 case 2: | |
679 *width = 352; | |
680 break; | |
681 case 3: | |
682 *width = 352; | |
683 *height /= 2; | |
684 break; | |
685 } | |
686 } | |
687 | |
688 /* Must be called before domain is changed (get_PGCN()) */ | |
689 static void saveRSMinfo(vm_t *self, int cellN, int blockN) | |
690 { | |
691 int i; | |
692 | |
693 if(cellN != 0) { | |
694 (self->state).rsm_cellN = cellN; | |
695 (self->state).rsm_blockN = 0; | |
696 } else { | |
697 (self->state).rsm_cellN = (self->state).cellN; | |
698 (self->state).rsm_blockN = blockN; | |
699 } | |
700 (self->state).rsm_vtsN = (self->state).vtsN; | |
701 (self->state).rsm_pgcN = get_PGCN(self); | |
702 | |
703 /* assert((self->state).rsm_pgcN == (self->state).TT_PGCN_REG); // for VTS_DOMAIN */ | |
704 | |
705 for(i = 0; i < 5; i++) { | |
706 (self->state).rsm_regs[i] = (self->state).registers.SPRM[4 + i]; | |
707 } | |
708 } | |
709 | |
710 | |
711 | |
712 /* Figure out the correct pgN from the cell and update (self->state). */ | |
713 static int set_PGN(vm_t *self) { | |
714 int new_pgN = 0; | |
715 | |
716 while(new_pgN < (self->state).pgc->nr_of_programs | |
717 && (self->state).cellN >= (self->state).pgc->program_map[new_pgN]) | |
718 new_pgN++; | |
719 | |
720 if(new_pgN == (self->state).pgc->nr_of_programs) /* We are at the last program */ | |
721 if((self->state).cellN > (self->state).pgc->nr_of_cells) | |
722 return 1; /* We are past the last cell */ | |
723 | |
724 (self->state).pgN = new_pgN; | |
725 | |
726 if((self->state).domain == VTS_DOMAIN) { | |
727 playback_type_t *pb_ty; | |
728 if((self->state).TTN_REG > self->vmgi->tt_srpt->nr_of_srpts) | |
729 return 0; /* ?? */ | |
730 pb_ty = &self->vmgi->tt_srpt->title[(self->state).TTN_REG - 1].pb_ty; | |
731 if(pb_ty->multi_or_random_pgc_title == /* One_Sequential_PGC_Title */ 0) { | |
732 #if 0 /* TTN_REG can't be trusted to have a correct value here... */ | |
733 vts_ptt_srpt_t *ptt_srpt = vtsi->vts_ptt_srpt; | |
734 assert((self->state).VTS_TTN_REG <= ptt_srpt->nr_of_srpts); | |
735 assert(get_PGCN() == ptt_srpt->title[(self->state).VTS_TTN_REG - 1].ptt[0].pgcn); | |
736 assert(1 == ptt_srpt->title[(self->state).VTS_TTN_REG - 1].ptt[0].pgn); | |
737 #endif | |
738 (self->state).PTTN_REG = (self->state).pgN; | |
739 } | |
740 } | |
741 | |
742 return 0; | |
743 } | |
744 | |
745 | |
746 | |
747 | |
748 | |
749 static link_t play_PGC(vm_t *self) | |
750 { | |
751 link_t link_values; | |
752 | |
753 fprintf(stderr, "vm: play_PGC:"); | |
754 if((self->state).domain != FP_DOMAIN) | |
755 fprintf(stderr, " (self->state).pgcN (%i)\n", get_PGCN(self)); | |
756 else | |
757 fprintf(stderr, " first_play_pgc\n"); | |
758 | |
759 /* This must be set before the pre-commands are executed because they */ | |
760 /* might contain a CallSS that will save resume state */ | |
761 (self->state).pgN = 1; | |
762 (self->state).cellN = 0; | |
763 | |
764 /* eval -> updates the state and returns either | |
765 - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN) | |
766 - just play video i.e first PG | |
767 (This is what happens if you fall of the end of the pre_cmds) | |
768 - or a error (are there more cases?) */ | |
769 if((self->state).pgc->command_tbl && (self->state).pgc->command_tbl->nr_of_pre) { | |
770 if(vmEval_CMD((self->state).pgc->command_tbl->pre_cmds, | |
771 (self->state).pgc->command_tbl->nr_of_pre, | |
772 &(self->state).registers, &link_values)) { | |
773 /* link_values contains the 'jump' return value */ | |
774 return link_values; | |
775 } else { | |
776 fprintf(stderr, "PGC pre commands didn't do a Jump, Link or Call\n"); | |
777 } | |
778 } | |
779 return play_PG(self); | |
780 } | |
781 | |
782 | |
783 static link_t play_PG(vm_t *self) | |
784 { | |
785 fprintf(stderr, "play_PG: (self->state).pgN (%i)\n", (self->state).pgN); | |
786 | |
787 assert((self->state).pgN > 0); | |
788 if((self->state).pgN > (self->state).pgc->nr_of_programs) { | |
789 fprintf(stderr, "(self->state).pgN (%i) == pgc->nr_of_programs + 1 (%i)\n", | |
790 (self->state).pgN, (self->state).pgc->nr_of_programs + 1); | |
791 assert((self->state).pgN == (self->state).pgc->nr_of_programs + 1); | |
792 return play_PGC_post(self); | |
793 } | |
794 | |
795 (self->state).cellN = (self->state).pgc->program_map[(self->state).pgN - 1]; | |
796 | |
797 return play_Cell(self); | |
798 } | |
799 | |
800 | |
801 static link_t play_Cell(vm_t *self) | |
802 { | |
803 fprintf(stderr, "play_Cell: (self->state).cellN (%i)\n", (self->state).cellN); | |
804 | |
805 assert((self->state).cellN > 0); | |
806 if((self->state).cellN > (self->state).pgc->nr_of_cells) { | |
807 fprintf(stderr, "(self->state).cellN (%i) == pgc->nr_of_cells + 1 (%i)\n", | |
808 (self->state).cellN, (self->state).pgc->nr_of_cells + 1); | |
809 assert((self->state).cellN == (self->state).pgc->nr_of_cells + 1); | |
810 return play_PGC_post(self); | |
811 } | |
812 | |
813 | |
814 /* Multi angle/Interleaved */ | |
815 switch((self->state).pgc->cell_playback[(self->state).cellN - 1].block_mode) { | |
816 case 0: /* Normal */ | |
817 assert((self->state).pgc->cell_playback[(self->state).cellN - 1].block_type == 0); | |
818 break; | |
819 case 1: /* The first cell in the block */ | |
820 switch((self->state).pgc->cell_playback[(self->state).cellN - 1].block_type) { | |
821 case 0: /* Not part of a block */ | |
822 assert(0); | |
823 case 1: /* Angle block */ | |
824 /* Loop and check each cell instead? So we don't get outsid the block. */ | |
825 (self->state).cellN += (self->state).AGL_REG - 1; | |
826 assert((self->state).cellN <= (self->state).pgc->nr_of_cells); | |
827 assert((self->state).pgc->cell_playback[(self->state).cellN - 1].block_mode != 0); | |
828 assert((self->state).pgc->cell_playback[(self->state).cellN - 1].block_type == 1); | |
829 break; | |
830 case 2: /* ?? */ | |
831 case 3: /* ?? */ | |
832 default: | |
833 fprintf(stderr, "Invalid? Cell block_mode (%d), block_type (%d)\n", | |
834 (self->state).pgc->cell_playback[(self->state).cellN - 1].block_mode, | |
835 (self->state).pgc->cell_playback[(self->state).cellN - 1].block_type); | |
836 } | |
837 break; | |
838 case 2: /* Cell in the block */ | |
839 case 3: /* Last cell in the block */ | |
840 /* These might perhaps happen for RSM or LinkC commands? */ | |
841 default: | |
842 fprintf(stderr, "Cell is in block but did not enter at first cell!\n"); | |
843 } | |
844 | |
845 /* Updates (self->state).pgN and PTTN_REG */ | |
846 if(set_PGN(self)) { | |
847 /* Should not happen */ | |
848 link_t tmp = {LinkTailPGC, /* No Button */ 0, 0, 0}; | |
849 assert(0); | |
850 return tmp; | |
851 } | |
852 | |
853 { | |
854 link_t tmp = {PlayThis, /* Block in Cell */ 0, 0, 0}; | |
855 return tmp; | |
856 } | |
857 | |
858 } | |
859 | |
860 static link_t play_Cell_post(vm_t *self) | |
861 { | |
862 cell_playback_t *cell; | |
863 | |
864 fprintf(stderr, "play_Cell_post: (self->state).cellN (%i)\n", (self->state).cellN); | |
865 | |
866 cell = &(self->state).pgc->cell_playback[(self->state).cellN - 1]; | |
867 | |
868 /* Still time is already taken care of before we get called. */ | |
869 | |
870 /* Deal with a Cell command, if any */ | |
871 if(cell->cell_cmd_nr != 0) { | |
872 link_t link_values; | |
873 | |
874 assert((self->state).pgc->command_tbl != NULL); | |
875 assert((self->state).pgc->command_tbl->nr_of_cell >= cell->cell_cmd_nr); | |
876 fprintf(stderr, "Cell command pressent, executing\n"); | |
877 if(vmEval_CMD(&(self->state).pgc->command_tbl->cell_cmds[cell->cell_cmd_nr - 1], 1, | |
878 &(self->state).registers, &link_values)) { | |
879 return link_values; | |
880 } else { | |
881 fprintf(stderr, "Cell command didn't do a Jump, Link or Call\n"); | |
882 /* Error ?? goto tail? goto next PG? or what? just continue? */ | |
883 } | |
884 } | |
885 | |
886 | |
887 /* Where to continue after playing the cell... */ | |
888 /* Multi angle/Interleaved */ | |
889 switch((self->state).pgc->cell_playback[(self->state).cellN - 1].block_mode) { | |
890 case 0: /* Normal */ | |
891 assert((self->state).pgc->cell_playback[(self->state).cellN - 1].block_type == 0); | |
892 (self->state).cellN++; | |
893 break; | |
894 case 1: /* The first cell in the block */ | |
895 case 2: /* A cell in the block */ | |
896 case 3: /* The last cell in the block */ | |
897 default: | |
898 switch((self->state).pgc->cell_playback[(self->state).cellN - 1].block_type) { | |
899 case 0: /* Not part of a block */ | |
900 assert(0); | |
901 case 1: /* Angle block */ | |
902 /* Skip the 'other' angles */ | |
903 (self->state).cellN++; | |
904 while((self->state).cellN <= (self->state).pgc->nr_of_cells | |
905 && (self->state).pgc->cell_playback[(self->state).cellN - 1].block_mode >= 2) { | |
906 (self->state).cellN++; | |
907 } | |
908 break; | |
909 case 2: /* ?? */ | |
910 case 3: /* ?? */ | |
911 default: | |
912 fprintf(stderr, "Invalid? Cell block_mode (%d), block_type (%d)\n", | |
913 (self->state).pgc->cell_playback[(self->state).cellN - 1].block_mode, | |
914 (self->state).pgc->cell_playback[(self->state).cellN - 1].block_type); | |
915 } | |
916 break; | |
917 } | |
918 | |
919 | |
920 /* Figure out the correct pgN for the new cell */ | |
921 if(set_PGN(self)) { | |
922 fprintf(stderr, "last cell in this PGC\n"); | |
923 return play_PGC_post(self); | |
924 } | |
925 | |
926 return play_Cell(self); | |
927 } | |
928 | |
929 | |
930 static link_t play_PGC_post(vm_t *self) | |
931 { | |
932 link_t link_values; | |
933 | |
934 fprintf(stderr, "play_PGC_post:\n"); | |
935 | |
936 assert((self->state).pgc->still_time == 0); /* FIXME $$$ */ | |
937 | |
938 /* eval -> updates the state and returns either | |
939 - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN) | |
940 - or a error (are there more cases?) | |
941 - if you got to the end of the post_cmds, then what ?? */ | |
942 if((self->state).pgc->command_tbl && | |
943 vmEval_CMD((self->state).pgc->command_tbl->post_cmds, | |
944 (self->state).pgc->command_tbl->nr_of_post, | |
945 &(self->state).registers, &link_values)) { | |
946 return link_values; | |
947 } | |
948 | |
949 /* Or perhaps handle it here? */ | |
950 { | |
951 link_t link_next_pgc = {LinkNextPGC, 0, 0, 0}; | |
952 fprintf(stderr, "** Fell of the end of the pgc, continuing in NextPGC\n"); | |
953 assert((self->state).pgc->next_pgc_nr != 0); | |
954 return link_next_pgc; | |
955 } | |
956 } | |
957 | |
958 | |
959 static link_t process_command(vm_t *self, link_t link_values) | |
960 { | |
961 /* FIXME $$$ Move this to a separate function? */ | |
962 self->badness_counter++; | |
963 if (self->badness_counter > 1) fprintf(stderr, "**** process_command re-entered %d*****\n",self->badness_counter); | |
964 while(link_values.command != PlayThis) { | |
965 | |
966 vmPrint_LINK(link_values); | |
967 | |
968 fprintf(stderr, "Link values %i %i %i %i\n", link_values.command, | |
969 link_values.data1, link_values.data2, link_values.data3); | |
970 | |
971 fprintf(stderr, "Before:"); | |
972 vm_print_current_domain_state(self); | |
973 | |
974 switch(link_values.command) { | |
975 case LinkNoLink: | |
976 /* No Link */ | |
977 if(link_values.data1 != 0) | |
978 (self->state).HL_BTNN_REG = link_values.data1 << 10; | |
979 exit(1); | |
980 | |
981 case LinkTopC: | |
982 /* Link to Top?? Cell */ | |
983 if(link_values.data1 != 0) | |
984 (self->state).HL_BTNN_REG = link_values.data1 << 10; | |
985 link_values = play_Cell(self); | |
986 break; | |
987 case LinkNextC: | |
988 /* Link to Next Cell */ | |
989 if(link_values.data1 != 0) | |
990 (self->state).HL_BTNN_REG = link_values.data1 << 10; | |
991 (self->state).cellN += 1; /* FIXME: What if cellN becomes > nr_of_cells? */ | |
992 link_values = play_Cell(self); | |
993 break; | |
994 case LinkPrevC: | |
995 /* Link to Previous Cell */ | |
996 if(link_values.data1 != 0) | |
997 (self->state).HL_BTNN_REG = link_values.data1 << 10; | |
998 (self->state).cellN -= 1; /* FIXME: What if cellN becomes < 1? */ | |
999 link_values = play_Cell(self); | |
1000 break; | |
1001 | |
1002 case LinkTopPG: | |
1003 /* Link to Top Program */ | |
1004 if(link_values.data1 != 0) | |
1005 (self->state).HL_BTNN_REG = link_values.data1 << 10; | |
1006 /* Does pgN always contain the current value? */ | |
1007 link_values = play_PG(self); | |
1008 break; | |
1009 case LinkNextPG: | |
1010 /* Link to Next Program */ | |
1011 if(link_values.data1 != 0) | |
1012 (self->state).HL_BTNN_REG = link_values.data1 << 10; | |
1013 /* Does pgN always contain the current value? */ | |
1014 (self->state).pgN += 1; /* FIXME: What if pgN becomes > pgc.nr_of_programs? */ | |
1015 link_values = play_PG(self); | |
1016 break; | |
1017 case LinkPrevPG: | |
1018 /* Link to Previous Program */ | |
1019 if(link_values.data1 != 0) | |
1020 (self->state).HL_BTNN_REG = link_values.data1 << 10; | |
1021 /* Does pgN always contain the current value? */ | |
1022 (self->state).pgN -= 1; /* FIXME: What if pgN becomes < 1? */ | |
1023 link_values = play_PG(self); | |
1024 break; | |
1025 | |
1026 case LinkTopPGC: | |
1027 /* Link to Top Program Chain */ | |
1028 if(link_values.data1 != 0) | |
1029 (self->state).HL_BTNN_REG = link_values.data1 << 10; | |
1030 link_values = play_PGC(self); | |
1031 break; | |
1032 case LinkNextPGC: | |
1033 /* Link to Next Program Chain */ | |
1034 if(link_values.data1 != 0) | |
1035 (self->state).HL_BTNN_REG = link_values.data1 << 10; | |
1036 assert((self->state).pgc->next_pgc_nr != 0); | |
1037 if(get_PGC(self, (self->state).pgc->next_pgc_nr)) | |
1038 assert(0); | |
1039 link_values = play_PGC(self); | |
1040 break; | |
1041 case LinkPrevPGC: | |
1042 /* Link to Previous Program Chain */ | |
1043 if(link_values.data1 != 0) | |
1044 (self->state).HL_BTNN_REG = link_values.data1 << 10; | |
1045 assert((self->state).pgc->prev_pgc_nr != 0); | |
1046 if(get_PGC(self, (self->state).pgc->prev_pgc_nr)) | |
1047 assert(0); | |
1048 link_values = play_PGC(self); | |
1049 break; | |
1050 case LinkGoUpPGC: | |
1051 /* Link to GoUp??? Program Chain */ | |
1052 if(link_values.data1 != 0) | |
1053 (self->state).HL_BTNN_REG = link_values.data1 << 10; | |
1054 assert((self->state).pgc->goup_pgc_nr != 0); | |
1055 if(get_PGC(self, (self->state).pgc->goup_pgc_nr)) | |
1056 assert(0); | |
1057 link_values = play_PGC(self); | |
1058 break; | |
1059 case LinkTailPGC: | |
1060 /* Link to Tail??? Program Chain */ | |
1061 if(link_values.data1 != 0) | |
1062 (self->state).HL_BTNN_REG = link_values.data1 << 10; | |
1063 link_values = play_PGC_post(self); | |
1064 break; | |
1065 | |
1066 case LinkRSM: | |
1067 { | |
1068 /* Link to Resume */ | |
1069 int i; | |
1070 /* Check and see if there is any rsm info!! */ | |
1071 (self->state).domain = VTS_DOMAIN; | |
1072 ifoOpenNewVTSI(self, self->dvd, (self->state).rsm_vtsN); | |
1073 get_PGC(self, (self->state).rsm_pgcN); | |
1074 | |
1075 /* These should never be set in SystemSpace and/or MenuSpace */ | |
1076 /* (self->state).TTN_REG = rsm_tt; ?? */ | |
1077 /* (self->state).TT_PGCN_REG = (self->state).rsm_pgcN; ?? */ | |
1078 for(i = 0; i < 5; i++) { | |
1079 (self->state).registers.SPRM[4 + i] = (self->state).rsm_regs[i]; | |
1080 } | |
1081 | |
1082 if(link_values.data1 != 0) | |
1083 (self->state).HL_BTNN_REG = link_values.data1 << 10; | |
1084 | |
1085 if((self->state).rsm_cellN == 0) { | |
1086 assert((self->state).cellN); /* Checking if this ever happens */ | |
1087 /* assert( time/block/vobu is 0 ); */ | |
1088 (self->state).pgN = 1; | |
1089 link_values = play_PG(self); | |
1090 } else { | |
1091 /* assert( time/block/vobu is _not_ 0 ); */ | |
1092 /* play_Cell_at_time */ | |
1093 /* (self->state).pgN = ?? this gets the righ value in play_Cell */ | |
1094 (self->state).cellN = (self->state).rsm_cellN; | |
1095 link_values.command = PlayThis; | |
1096 link_values.data1 = (self->state).rsm_blockN; | |
1097 if(set_PGN(self)) { | |
1098 /* Were at the end of the PGC, should not happen for a RSM */ | |
1099 assert(0); | |
1100 link_values.command = LinkTailPGC; | |
1101 link_values.data1 = 0; /* No button */ | |
1102 } | |
1103 } | |
1104 } | |
1105 break; | |
1106 case LinkPGCN: | |
1107 /* Link to Program Chain Number:data1 */ | |
1108 if(get_PGC(self, link_values.data1)) | |
1109 assert(0); | |
1110 link_values = play_PGC(self); | |
1111 break; | |
1112 case LinkPTTN: | |
1113 /* Link to Part of this Title Number:data1 */ | |
1114 assert((self->state).domain == VTS_DOMAIN); | |
1115 if(link_values.data2 != 0) | |
1116 (self->state).HL_BTNN_REG = link_values.data2 << 10; | |
1117 if(get_VTS_PTT(self, (self->state).vtsN, (self->state).VTS_TTN_REG, link_values.data1) == -1) | |
1118 assert(0); | |
1119 link_values = play_PG(self); | |
1120 break; | |
1121 case LinkPGN: | |
1122 /* Link to Program Number:data1 */ | |
1123 if(link_values.data2 != 0) | |
1124 (self->state).HL_BTNN_REG = link_values.data2 << 10; | |
1125 /* Update any other state, PTTN perhaps? */ | |
1126 (self->state).pgN = link_values.data1; | |
1127 link_values = play_PG(self); | |
1128 break; | |
1129 case LinkCN: | |
1130 /* Link to Cell Number:data1 */ | |
1131 if(link_values.data2 != 0) | |
1132 (self->state).HL_BTNN_REG = link_values.data2 << 10; | |
1133 /* Update any other state, pgN, PTTN perhaps? */ | |
1134 (self->state).cellN = link_values.data1; | |
1135 link_values = play_Cell(self); | |
1136 break; | |
1137 | |
1138 case Exit: | |
1139 exit(1); /* What should we do here?? */ | |
1140 | |
1141 case JumpTT: | |
1142 /* Jump to VTS Title Domain */ | |
1143 /* Only allowed from the First Play domain(PGC) */ | |
1144 /* or the Video Manager domain (VMG) */ | |
1145 //fprintf(stderr,"****** JumpTT is Broken, please fix me!!! ****\n"); | |
1146 assert((self->state).domain == VMGM_DOMAIN || (self->state).domain == FP_DOMAIN); /* ?? */ | |
1147 if(get_TT(self,link_values.data1) == -1) | |
1148 assert(0); | |
1149 link_values = play_PGC(self); | |
1150 break; | |
1151 case JumpVTS_TT: | |
1152 /* Jump to Title:data1 in same VTS Title Domain */ | |
1153 /* Only allowed from the VTS Menu Domain(VTSM) */ | |
1154 /* or the Video Title Set Domain(VTS) */ | |
1155 assert((self->state).domain == VTSM_DOMAIN || (self->state).domain == VTS_DOMAIN); /* ?? */ | |
1156 /* FIXME: Should be able to use get_VTS_PTT here */ | |
1157 if(get_VTS_TT(self,(self->state).vtsN, link_values.data1) == -1) | |
1158 assert(0); | |
1159 link_values = play_PGC(self); | |
1160 break; | |
1161 case JumpVTS_PTT: | |
1162 /* Jump to Part:data2 of Title:data1 in same VTS Title Domain */ | |
1163 /* Only allowed from the VTS Menu Domain(VTSM) */ | |
1164 /* or the Video Title Set Domain(VTS) */ | |
1165 assert((self->state).domain == VTSM_DOMAIN || (self->state).domain == VTS_DOMAIN); /* ?? */ | |
1166 if(get_VTS_PTT(self,(self->state).vtsN, link_values.data1, link_values.data2) == -1) | |
1167 assert(0); | |
1168 link_values = play_PG(self); | |
1169 break; | |
1170 | |
1171 case JumpSS_FP: | |
1172 /* Jump to First Play Domain */ | |
1173 /* Only allowed from the VTS Menu Domain(VTSM) */ | |
1174 /* or the Video Manager domain (VMG) */ | |
1175 assert((self->state).domain == VMGM_DOMAIN || (self->state).domain == VTSM_DOMAIN); /* ?? */ | |
1176 get_FP_PGC(self); | |
1177 link_values = play_PGC(self); | |
1178 break; | |
1179 case JumpSS_VMGM_MENU: | |
1180 /* Jump to Video Manger domain - Title Menu:data1 or any PGC in VMG */ | |
1181 /* Allowed from anywhere except the VTS Title domain */ | |
1182 assert((self->state).domain == VMGM_DOMAIN || | |
1183 (self->state).domain == VTSM_DOMAIN || | |
1184 (self->state).domain == FP_DOMAIN); /* ?? */ | |
1185 (self->state).domain = VMGM_DOMAIN; | |
1186 if(get_MENU(self,link_values.data1) == -1) | |
1187 assert(0); | |
1188 link_values = play_PGC(self); | |
1189 break; | |
1190 case JumpSS_VTSM: | |
1191 /* Jump to a menu in Video Title domain, */ | |
1192 /* or to a Menu is the current VTS */ | |
1193 /* FIXME: This goes badly wrong for some DVDs. */ | |
1194 /* FIXME: Keep in touch with ogle people regarding what to do here */ | |
1195 fprintf(stderr, "dvdnav: BUG TRACKING *******************************************************************\n"); | |
1196 fprintf(stderr, "dvdnav: If you see this message, please report these values to the dvd-devel mailing list.\n"); | |
1197 fprintf(stderr, " data1=%u data2=%u data3=%u\n", | |
1198 link_values.data1, | |
1199 link_values.data2, | |
1200 link_values.data3); | |
1201 fprintf(stderr, "dvdnav: *******************************************************************\n"); | |
1202 | |
1203 if(link_values.data1 !=0) { | |
1204 assert((self->state).domain == VMGM_DOMAIN || (self->state).domain == FP_DOMAIN); /* ?? */ | |
1205 (self->state).domain = VTSM_DOMAIN; | |
1206 ifoOpenNewVTSI(self, self->dvd, link_values.data1); /* Also sets (self->state).vtsN */ | |
1207 } else { | |
1208 /* This happens on 'The Fifth Element' region 2. */ | |
1209 assert((self->state).domain == VTSM_DOMAIN); | |
1210 } | |
1211 /* I don't know what title is supposed to be used for. */ | |
1212 /* Alien or Aliens has this != 1, I think. */ | |
1213 /* assert(link_values.data2 == 1); */ | |
1214 (self->state).VTS_TTN_REG = link_values.data2; | |
1215 if(get_MENU(self, link_values.data3) == -1) | |
1216 assert(0); | |
1217 link_values = play_PGC(self); | |
1218 break; | |
1219 case JumpSS_VMGM_PGC: | |
1220 assert((self->state).domain == VMGM_DOMAIN || | |
1221 (self->state).domain == VTSM_DOMAIN || | |
1222 (self->state).domain == FP_DOMAIN); /* ?? */ | |
1223 (self->state).domain = VMGM_DOMAIN; | |
1224 if(get_PGC(self,link_values.data1) == -1) | |
1225 assert(0); | |
1226 link_values = play_PGC(self); | |
1227 break; | |
1228 | |
1229 case CallSS_FP: | |
1230 assert((self->state).domain == VTS_DOMAIN); /* ?? */ | |
1231 /* Must be called before domain is changed */ | |
1232 saveRSMinfo(self, link_values.data1, /* We dont have block info */ 0); | |
1233 get_FP_PGC(self); | |
1234 link_values = play_PGC(self); | |
1235 break; | |
1236 case CallSS_VMGM_MENU: | |
1237 assert((self->state).domain == VTS_DOMAIN); /* ?? */ | |
1238 /* Must be called before domain is changed */ | |
1239 saveRSMinfo(self,link_values.data2, /* We dont have block info */ 0); | |
1240 (self->state).domain = VMGM_DOMAIN; | |
1241 if(get_MENU(self,link_values.data1) == -1) | |
1242 assert(0); | |
1243 link_values = play_PGC(self); | |
1244 break; | |
1245 case CallSS_VTSM: | |
1246 assert((self->state).domain == VTS_DOMAIN); /* ?? */ | |
1247 /* Must be called before domain is changed */ | |
1248 saveRSMinfo(self,link_values.data2, /* We dont have block info */ 0); | |
1249 (self->state).domain = VTSM_DOMAIN; | |
1250 if(get_MENU(self,link_values.data1) == -1) | |
1251 assert(0); | |
1252 link_values = play_PGC(self); | |
1253 break; | |
1254 case CallSS_VMGM_PGC: | |
1255 assert((self->state).domain == VTS_DOMAIN); /* ?? */ | |
1256 /* Must be called before domain is changed */ | |
1257 saveRSMinfo(self,link_values.data2, /* We dont have block info */ 0); | |
1258 (self->state).domain = VMGM_DOMAIN; | |
1259 if(get_PGC(self,link_values.data1) == -1) | |
1260 assert(0); | |
1261 link_values = play_PGC(self); | |
1262 break; | |
1263 case PlayThis: | |
1264 /* Should never happen. */ | |
1265 break; | |
1266 } | |
1267 fprintf(stderr, "After:"); | |
1268 vm_print_current_domain_state(self); | |
1269 | |
1270 } | |
1271 self->badness_counter--; | |
1272 return link_values; | |
1273 } | |
1274 | |
1275 static int get_TT(vm_t *self, int tt) | |
1276 { | |
1277 //fprintf(stderr,"****** get_TT is Broken, please fix me!!! ****\n"); | |
1278 assert(tt <= self->vmgi->tt_srpt->nr_of_srpts); | |
1279 | |
1280 (self->state).TTN_REG = tt; | |
1281 | |
1282 return get_VTS_TT(self, self->vmgi->tt_srpt->title[tt - 1].title_set_nr, | |
1283 self->vmgi->tt_srpt->title[tt - 1].vts_ttn); | |
1284 } | |
1285 | |
1286 | |
1287 static int get_VTS_TT(vm_t *self, int vtsN, int vts_ttn) | |
1288 { | |
1289 int pgcN; | |
1290 //fprintf(stderr,"****** get_VTS_TT is Broken, please fix me!!! ****\n"); | |
1291 fprintf(stderr,"title_set_nr=%d\n", vtsN); | |
1292 fprintf(stderr,"vts_ttn=%d\n", vts_ttn); | |
1293 | |
1294 (self->state).domain = VTS_DOMAIN; | |
1295 if(vtsN != (self->state).vtsN) { | |
1296 fprintf(stderr,"****** opening new VTSI ****\n"); | |
1297 ifoOpenNewVTSI(self, self->dvd, vtsN); /* Also sets (self->state).vtsN */ | |
1298 fprintf(stderr,"****** opened VTSI ****\n"); | |
1299 } | |
1300 | |
1301 pgcN = get_ID(self, vts_ttn); /* This might return -1 */ | |
1302 assert(pgcN != -1); | |
1303 | |
1304 /* (self->state).TTN_REG = ?? Must search tt_srpt for a matching entry... */ | |
1305 (self->state).VTS_TTN_REG = vts_ttn; | |
1306 /* Any other registers? */ | |
1307 | |
1308 return get_PGC(self, pgcN); | |
1309 } | |
1310 | |
1311 | |
1312 static int get_VTS_PTT(vm_t *self, int vtsN, int /* is this really */ vts_ttn, int part) | |
1313 { | |
1314 int pgcN, pgN; | |
1315 | |
1316 (self->state).domain = VTS_DOMAIN; | |
1317 if(vtsN != (self->state).vtsN) | |
1318 ifoOpenNewVTSI(self, self->dvd, vtsN); /* Also sets (self->state).vtsN */ | |
1319 | |
1320 assert(vts_ttn <= self->vtsi->vts_ptt_srpt->nr_of_srpts); | |
1321 assert(part <= self->vtsi->vts_ptt_srpt->title[vts_ttn - 1].nr_of_ptts); | |
1322 | |
1323 pgcN = self->vtsi->vts_ptt_srpt->title[vts_ttn - 1].ptt[part - 1].pgcn; | |
1324 pgN = self->vtsi->vts_ptt_srpt->title[vts_ttn - 1].ptt[part - 1].pgn; | |
1325 | |
1326 /* (self->state).TTN_REG = ?? Must search tt_srpt for a matchhing entry... */ | |
1327 (self->state).VTS_TTN_REG = vts_ttn; | |
1328 /* Any other registers? */ | |
1329 | |
1330 (self->state).pgN = pgN; /* ?? */ | |
1331 | |
1332 return get_PGC(self, pgcN); | |
1333 } | |
1334 | |
1335 | |
1336 | |
1337 static int get_FP_PGC(vm_t *self) | |
1338 { | |
1339 (self->state).domain = FP_DOMAIN; | |
1340 | |
1341 (self->state).pgc = self->vmgi->first_play_pgc; | |
1342 | |
1343 return 0; | |
1344 } | |
1345 | |
1346 | |
1347 static int get_MENU(vm_t *self, int menu) | |
1348 { | |
1349 assert((self->state).domain == VMGM_DOMAIN || (self->state).domain == VTSM_DOMAIN); | |
1350 return get_PGC(self, get_ID(self, menu)); | |
1351 } | |
1352 | |
1353 static int get_ID(vm_t *self, int id) | |
1354 { | |
1355 int pgcN, i; | |
1356 pgcit_t *pgcit; | |
1357 | |
1358 /* Relies on state to get the correct pgcit. */ | |
1359 pgcit = get_PGCIT(self); | |
1360 assert(pgcit != NULL); | |
1361 | |
1362 /* Get menu/title */ | |
1363 for(i = 0; i < pgcit->nr_of_pgci_srp; i++) { | |
1364 if((pgcit->pgci_srp[i].entry_id & 0x7f) == id) { | |
1365 assert((pgcit->pgci_srp[i].entry_id & 0x80) == 0x80); | |
1366 pgcN = i + 1; | |
1367 return pgcN; | |
1368 } | |
1369 } | |
1370 fprintf(stderr, "** No such id/menu (%d) entry PGC\n", id); | |
1371 return -1; /* error */ | |
1372 } | |
1373 | |
1374 | |
1375 | |
1376 static int get_PGC(vm_t *self, int pgcN) | |
1377 { | |
1378 /* FIXME: Keep this up to date with the ogle people */ | |
1379 pgcit_t *pgcit; | |
1380 | |
1381 pgcit = get_PGCIT(self); | |
1382 | |
1383 assert(pgcit != NULL); /* ?? Make this return -1 instead */ | |
1384 if(pgcN < 1 || pgcN > pgcit->nr_of_pgci_srp) { | |
1385 /* if(pgcit->nr_of_pgci_srp != 1) */ | |
1386 return -1; /* error */ | |
1387 /* pgcN = 1; */ | |
1388 } | |
1389 | |
1390 /* (self->state).pgcN = pgcN; */ | |
1391 (self->state).pgc = pgcit->pgci_srp[pgcN - 1].pgc; | |
1392 | |
1393 if((self->state).domain == VTS_DOMAIN) | |
1394 (self->state).TT_PGCN_REG = pgcN; | |
1395 | |
1396 return 0; | |
1397 } | |
1398 | |
1399 static int get_PGCN(vm_t *self) | |
1400 { | |
1401 pgcit_t *pgcit; | |
1402 int pgcN = 1; | |
1403 | |
1404 pgcit = get_PGCIT(self); | |
1405 | |
1406 assert(pgcit != NULL); | |
1407 | |
1408 while(pgcN <= pgcit->nr_of_pgci_srp) { | |
1409 if(pgcit->pgci_srp[pgcN - 1].pgc == (self->state).pgc) | |
1410 return pgcN; | |
1411 pgcN++; | |
1412 } | |
1413 | |
1414 return -1; /* error */ | |
1415 } | |
1416 | |
1417 | |
1418 static int get_video_aspect(vm_t *self) | |
1419 { | |
1420 int aspect = 0; | |
1421 | |
1422 if((self->state).domain == VTS_DOMAIN) { | |
1423 aspect = self->vtsi->vtsi_mat->vts_video_attr.display_aspect_ratio; | |
1424 } else if((self->state).domain == VTSM_DOMAIN) { | |
1425 aspect = self->vtsi->vtsi_mat->vtsm_video_attr.display_aspect_ratio; | |
1426 } else if((self->state).domain == VMGM_DOMAIN) { | |
1427 aspect = self->vmgi->vmgi_mat->vmgm_video_attr.display_aspect_ratio; | |
1428 } | |
1429 fprintf(stderr, "dvdnav:get_video_aspect:aspect=%d\n",aspect); | |
1430 assert(aspect == 0 || aspect == 3); | |
1431 (self->state).registers.SPRM[14] &= ~(0x3 << 10); | |
1432 (self->state).registers.SPRM[14] |= aspect << 10; | |
1433 | |
1434 return aspect; | |
1435 } | |
1436 | |
1437 | |
1438 | |
1439 | |
1440 | |
1441 | |
1442 static void ifoOpenNewVTSI(vm_t *self, dvd_reader_t *dvd, int vtsN) | |
1443 { | |
1444 if((self->state).vtsN == vtsN) { | |
1445 return; /* We alread have it */ | |
1446 } | |
1447 | |
1448 if(self->vtsi != NULL) | |
1449 ifoClose(self->vtsi); | |
1450 | |
1451 self->vtsi = ifoOpenVTSI(dvd, vtsN); | |
1452 if(self->vtsi == NULL) { | |
1453 fprintf(stderr, "ifoOpenVTSI failed\n"); | |
1454 exit(1); | |
1455 } | |
1456 if(!ifoRead_VTS_PTT_SRPT(self->vtsi)) { | |
1457 fprintf(stderr, "ifoRead_VTS_PTT_SRPT failed\n"); | |
1458 exit(1); | |
1459 } | |
1460 if(!ifoRead_PGCIT(self->vtsi)) { | |
1461 fprintf(stderr, "ifoRead_PGCIT failed\n"); | |
1462 exit(1); | |
1463 } | |
1464 if(!ifoRead_PGCI_UT(self->vtsi)) { | |
1465 fprintf(stderr, "ifoRead_PGCI_UT failed\n"); | |
1466 exit(1); | |
1467 } | |
1468 if(!ifoRead_VOBU_ADMAP(self->vtsi)) { | |
1469 fprintf(stderr, "ifoRead_VOBU_ADMAP vtsi failed\n"); | |
1470 exit(1); | |
1471 } | |
1472 if(!ifoRead_TITLE_VOBU_ADMAP(self->vtsi)) { | |
1473 fprintf(stderr, "ifoRead_TITLE_VOBU_ADMAP vtsi failed\n"); | |
1474 exit(1); | |
1475 } | |
1476 (self->state).vtsN = vtsN; | |
1477 } | |
1478 | |
1479 | |
1480 | |
1481 | |
1482 static pgcit_t* get_MENU_PGCIT(vm_t *self, ifo_handle_t *h, uint16_t lang) | |
1483 { | |
1484 int i; | |
1485 | |
1486 if(h == NULL || h->pgci_ut == NULL) { | |
1487 fprintf(stderr, "*** pgci_ut handle is NULL ***\n"); | |
1488 return NULL; /* error? */ | |
1489 } | |
1490 | |
1491 i = 0; | |
1492 while(i < h->pgci_ut->nr_of_lus | |
1493 && h->pgci_ut->lu[i].lang_code != lang) | |
1494 i++; | |
1495 if(i == h->pgci_ut->nr_of_lus) { | |
1496 fprintf(stderr, "Language '%c%c' not found, using '%c%c' instead\n", | |
1497 (char)(lang >> 8), (char)(lang & 0xff), | |
1498 (char)(h->pgci_ut->lu[0].lang_code >> 8), | |
1499 (char)(h->pgci_ut->lu[0].lang_code & 0xff)); | |
1500 i = 0; /* error? */ | |
1501 } | |
1502 | |
1503 return h->pgci_ut->lu[i].pgcit; | |
1504 } | |
1505 | |
1506 /* Uses state to decide what to return */ | |
1507 static pgcit_t* get_PGCIT(vm_t *self) { | |
1508 pgcit_t *pgcit; | |
1509 | |
1510 if((self->state).domain == VTS_DOMAIN) { | |
1511 pgcit = self->vtsi->vts_pgcit; | |
1512 } else if((self->state).domain == VTSM_DOMAIN) { | |
1513 pgcit = get_MENU_PGCIT(self, self->vtsi, (self->state).registers.SPRM[0]); | |
1514 } else if((self->state).domain == VMGM_DOMAIN) { | |
1515 pgcit = get_MENU_PGCIT(self, self->vmgi, (self->state).registers.SPRM[0]); | |
1516 } else { | |
1517 pgcit = NULL; /* Should never hapen */ | |
1518 } | |
1519 | |
1520 return pgcit; | |
1521 } | |
1522 | |
1523 /* | |
1524 * $Log$ | |
3
328eadb3f37e
Added initial example programs directory and make sure all debug/error output goes to stderr.
richwareham
parents:
0
diff
changeset
|
1525 * Revision 1.2 2002/04/01 18:56:28 richwareham |
328eadb3f37e
Added initial example programs directory and make sure all debug/error output goes to stderr.
richwareham
parents:
0
diff
changeset
|
1526 * Added initial example programs directory and make sure all debug/error output goes to stderr. |
328eadb3f37e
Added initial example programs directory and make sure all debug/error output goes to stderr.
richwareham
parents:
0
diff
changeset
|
1527 * |
328eadb3f37e
Added initial example programs directory and make sure all debug/error output goes to stderr.
richwareham
parents:
0
diff
changeset
|
1528 * Revision 1.1.1.1 2002/03/12 19:45:55 richwareham |
328eadb3f37e
Added initial example programs directory and make sure all debug/error output goes to stderr.
richwareham
parents:
0
diff
changeset
|
1529 * Initial import |
0 | 1530 * |
1531 * Revision 1.18 2002/01/22 16:56:49 jcdutton | |
1532 * Fix clut after seeking. | |
1533 * Add a few virtual machine debug messages, to help diagnose problems with "Deep Purple - Total Abandon" DVD as I don't have the DVD itself. | |
1534 * Fix a few debug messages, so they do not say FIXME. | |
1535 * Move the FIXME debug messages to comments in the code. | |
1536 * | |
1537 * Revision 1.17 2002/01/21 01:16:30 jcdutton | |
1538 * Added some debug messages, to hopefully get info from users. | |
1539 * | |
1540 * Revision 1.16 2002/01/20 21:40:46 jcdutton | |
1541 * Start to fix some assert failures. | |
1542 * | |
1543 * Revision 1.15 2002/01/19 20:24:38 jcdutton | |
1544 * Just some FIXME notes added. | |
1545 * | |
1546 * Revision 1.14 2002/01/13 22:17:57 jcdutton | |
1547 * Change logging. | |
1548 * | |
1549 * | |
1550 */ |