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