📄 vm.c
字号:
//assert(state.rsm_pgcN == state.TT_PGCN_REG); // for VTS_DOMAIN for(i = 0; i < 5; i++) { state.rsm_regs[i] = state.registers.SPRM[4 + i]; }}/* Figure out the correct pgN from the cell and update state. */ static int update_PGN(void) { int new_pgN = 0; while(new_pgN < state.pgc->nr_of_programs && state.cellN >= state.pgc->program_map[new_pgN]) new_pgN++; if(new_pgN == state.pgc->nr_of_programs) /* We are at the last program */ if(state.cellN > state.pgc->nr_of_cells) return 1; /* We are past the last cell */ state.pgN = new_pgN; if(state.domain == VTS_DOMAIN) { playback_type_t *pb_ty; if(state.TTN_REG > vmgi->tt_srpt->nr_of_srpts) return 0; // ?? pb_ty = &vmgi->tt_srpt->title[state.TTN_REG - 1].pb_ty; if(pb_ty->multi_or_random_pgc_title == /* One_Sequential_PGC_Title */ 0) {#if 0 /* TTN_REG can't be trusted to have a correct value here... */ vts_ptt_srpt_t *ptt_srpt = vtsi->vts_ptt_srpt; assert(state.VTS_TTN_REG <= ptt_srpt->nr_of_srpts); assert(get_PGCN() == ptt_srpt->title[state.VTS_TTN_REG - 1].ptt[0].pgcn); assert(1 == ptt_srpt->title[state.VTS_TTN_REG - 1].ptt[0].pgn);#endif state.PTTN_REG = state.pgN; } } return 0;}static int next_PG(link_t *link_values){ // TODO this is how Sequential PGCs work // we have to add Random/shuffle behaviour // Does pgN always contain the current value? if(state.pgN == state.pgc->nr_of_programs) { if(get_PGC(state.pgc->next_pgc_nr)) { /* This should be conditional on beeing a user command!! */ /* This is what a compliant player should, do it seems. */ //return -1; // there is no next PG /* We think that the following makes more sense though. */ *link_values = play_PGC_post(); } else { *link_values = play_PGC(); } } else { state.pgN += 1; *link_values = play_PG(); } return 0; // ok}static int prev_PG(link_t *link_values){ // TODO this is how Sequential PGCs work // we have to add Random/shuffle behaviour // Does pgN always contain the current value? if(state.pgN == 1) { if(get_PGC(state.pgc->prev_pgc_nr)) return -1; // error / unable to.. *link_values = play_PGC(); } else { state.pgN -= 1; *link_values = play_PG(); } return 0; // ok}static link_t play_PGC(void) { link_t link_values; if(state.domain != FP_DOMAIN) DNOTE("play_PGC: state.pgcN (%i)\n", get_PGCN()); else DNOTE("%s", "play_PGC: first_play_pgc\n"); // These should have been set before play_PGC is called. // They won't be set automaticaly and the when the pre-commands are // executed they might be used (for instance in a CallSS that will // save resume state) // state.pgN, state.cellN state.cellN = 0; // FIXME set cellN everytime pgN is set! ?! /* eval -> updates the state and returns either - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN) - just play video i.e first PG (This is what happens if you fall of the end of the pre_cmds) - or a error (are there more cases?) */ if(state.pgc->command_tbl && state.pgc->command_tbl->nr_of_pre) { if(vmEval_CMD(state.pgc->command_tbl->pre_cmds, state.pgc->command_tbl->nr_of_pre, &state.registers, &link_values)) { // link_values contains the 'jump' return value return link_values; } else { DNOTE("%s", "PGC pre commands didn't do a Jump, Link or Call\n"); } } return play_PG();} static link_t play_PG(void){ DNOTE("play_PG: state.pgN (%i)\n", state.pgN); assert(state.pgN > 0); if(state.pgN > state.pgc->nr_of_programs) { DNOTE("state.pgN (%i) == pgc->nr_of_programs + 1 (%i)\n", state.pgN, state.pgc->nr_of_programs + 1); assert(state.pgN == state.pgc->nr_of_programs + 1); return play_PGC_post(); } state.cellN = state.pgc->program_map[state.pgN - 1]; return play_Cell();}static link_t play_Cell(void){ cell_playback_t *const cell_pb = state.pgc->cell_playback; DNOTE("play_Cell: state.cellN (%i)\n", state.cellN); assert(state.cellN > 0); if(state.cellN > state.pgc->nr_of_cells) { DNOTE("state.cellN (%i) == pgc->nr_of_cells + 1 (%i)\n", state.cellN, state.pgc->nr_of_cells + 1); assert(state.cellN == state.pgc->nr_of_cells + 1); return play_PGC_post(); } /* Multi angle/Interleaved */ switch(cell_pb[state.cellN - 1].block_mode) { case BLOCK_MODE_NOT_IN_BLOCK: assert(cell_pb[state.cellN - 1].block_type == BLOCK_TYPE_NONE); break; case BLOCK_MODE_FIRST_CELL: switch(cell_pb[state.cellN - 1].block_type) { case BLOCK_TYPE_NONE: assert(0); case BLOCK_TYPE_ANGLE_BLOCK: /* Loop and check each cell instead? So we don't get outsid the block. */ state.cellN += state.AGL_REG - 1; assert(state.cellN <= state.pgc->nr_of_cells); assert(cell_pb[state.cellN - 1].block_mode != BLOCK_MODE_NOT_IN_BLOCK); assert(cell_pb[state.cellN - 1].block_type == BLOCK_TYPE_ANGLE_BLOCK); break; case 2: // ?? case 3: // ?? default: WARNING("Invalid? Cell block_mode (%d), block_type (%d)\n", cell_pb[state.cellN - 1].block_mode, cell_pb[state.cellN - 1].block_type); } break; case BLOCK_MODE_IN_BLOCK: case BLOCK_MODE_LAST_CELL: // These might perhaps happen for RSM or LinkC commands? default: WARNING("%s", "Cell is in block but did not enter at first cell!\n"); } /* Updates state.pgN and PTTN_REG */ if(update_PGN()) { /* Should not happen */ link_t tmp = {LinkTailPGC, /* No Button */ 0, 0, 0}; assert(0); return tmp; } { link_t tmp = {PlayThis, /* Block in Cell */ 0, 0, 0}; return tmp; }}static link_t play_Cell_post(void){ cell_playback_t *const cell_pb = state.pgc->cell_playback; int cell_cmd_nr; DNOTE("play_Cell_post: state.cellN (%i)\n", state.cellN); cell_cmd_nr = cell_pb[state.cellN - 1].cell_cmd_nr; /* Still time is already taken care of before we get called. */ if(cell_cmd_nr != 0 && (state.pgc->command_tbl == NULL || state.pgc->command_tbl->nr_of_cell < cell_cmd_nr)) { WARNING("Invalid Cell command vtsn=%d dom=%d pgc=%d\n", state.vtsN, state.domain, get_PGCN()); cell_cmd_nr = 0; } /* Deal with a Cell command, if any */ if(cell_cmd_nr != 0) { link_t link_values; DNOTE("%s", "Cell command pressent, executing\n"); if(vmEval_CMD(&state.pgc->command_tbl->cell_cmds[cell_cmd_nr - 1], 1, &state.registers, &link_values)) { return link_values; } else { DNOTE("%s", "Cell command didn't do a Jump, Link or Call\n"); // Error ?? goto tail? goto next PG? or what? just continue? } } /* Where to continue after playing the cell... */ /* Multi angle/Interleaved */ switch(cell_pb[state.cellN - 1].block_mode) { case BLOCK_MODE_NOT_IN_BLOCK: assert(cell_pb[state.cellN - 1].block_type == BLOCK_TYPE_NONE); state.cellN++; break; case BLOCK_MODE_FIRST_CELL: case BLOCK_MODE_IN_BLOCK: case BLOCK_MODE_LAST_CELL: default: switch(cell_pb[state.cellN - 1].block_type) { case BLOCK_TYPE_NONE: assert(0); case BLOCK_TYPE_ANGLE_BLOCK: /* Skip the 'other' angles */ state.cellN++; while(state.cellN <= state.pgc->nr_of_cells && cell_pb[state.cellN - 1].block_mode >= BLOCK_MODE_IN_BLOCK) { state.cellN++; } break; case 2: // ?? case 3: // ?? default: WARNING("Invalid? Cell block_mode (%d), block_type (%d)\n", cell_pb[state.cellN - 1].block_mode, cell_pb[state.cellN - 1].block_type); } break; } /* Figure out the correct pgN for the new cell */ if(update_PGN()) { DNOTE("%s", "last cell in this PGC\n"); return play_PGC_post(); } return play_Cell();}static link_t play_PGC_post(void){ link_t link_values; DNOTE("%s", "play_PGC_post:\n"); assert(state.pgc->still_time == 0); // FIXME $$$ /* eval -> updates the state and returns either - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN) - or a error (are there more cases?) - if you got to the end of the post_cmds, then what ?? */ if(state.pgc->command_tbl && vmEval_CMD(state.pgc->command_tbl->post_cmds, state.pgc->command_tbl->nr_of_post, &state.registers, &link_values)) { return link_values; } // Or perhaps handle it here? { link_t link_next_pgc = {LinkNextPGC, 0, 0, 0}; DNOTE("%s", "** Fell of the end of the pgc, continuing in NextPGC\n"); if(state.domain == FP_DOMAIN) { /* User should select a title them self, i.e. we should probably go to the STOP_DOMAIN. Untill we have that implemented start playing title track 1 instead since we're sure that that isn't optional. */ if(get_TT(1) == -1) assert(0); /* This won't create an infinite loop becase we can't ever get to the FP_DOMAIN again without executing a command. */ return play_PGC(); } assert(state.pgc->next_pgc_nr != 0); /* Should end up in the STOP_DOMAIN if next_pgc i 0. */ return link_next_pgc; }}/* Should only be called in the VTS Domain and only for * One_Sequential_PGC_Title PGCs. * returns 0 if it fails. */int vm_time_play(dvd_time_t *time, unsigned int offset){ playback_type_t *pb_ty; int pgcN; int seconds; if(state.domain != VTS_DOMAIN) { /* No time seek possible */ return 0; } // Should we also check the state.pgc->pg_playback_mode, or instead? // Is the TTN_REG up to date? pb_ty = &vmgi->tt_srpt->title[state.TTN_REG - 1].pb_ty; if(pb_ty->multi_or_random_pgc_title != /* One_Sequential_PGC_Title */ 0) { /* No time seek possible */ return 0; } // Do we have a Time Map table? if(!vtsi->vts_tmapt) { return 0; } seconds = time2sec(time) + offset; if(seconds < 0) seconds = 0; fprintf(stderr, "Time Play/Skipp to offset %dseconds\n", seconds); pgcN = get_PGCN(); // Is there an entry for this pgc? assert(pgcN != -1); if(pgcN <= vtsi->vts_tmapt->nr_of_tmaps) { vts_tmap_t *tmap = &vtsi->vts_tmapt->tmap[pgcN - 1]; int index; map_ent_t entry; int32_t vobu_addr; /* * Restructure this as a loop over all the entries, keeping * count of the 'flags' so we know the cell number? * Is that even guaranteed to be correct? */ if(tmap->tmu == 0) // Valid time unit (resolution) return 0; // No seek index = seconds / tmap->tmu; if(tmap->nr_of_entries < index) // Enough entries? return 0; // No seek entry = tmap->map_ent[index]; vobu_addr = entry & 0x7fffffff; // High bit is discontinuty flag /* Should we instead do a linear scan of the table and count * the 'flag's to that way get the right cell? * Can one have time seek for multiangle pgc's? */ { cell_playback_t *cells = state.pgc->cell_playback; state.cellN = get_cellN_for_vobu(vobu_addr); //scan the cell table? assert(vobu_addr >= cells[state.cellN - 1].first_sector); assert(vobu_addr <= cells[state.cellN - 1].last_vobu_start_sector); state.blockN = vobu_addr - cells[state.cellN - 1].first_sector; update_PGN(); } return 1; } return 0;}static link_t process_command(link_t link_values){ link_t do_nothing = { PlayThis, 0, 0, 0}; do_nothing.data1 = state.blockN; /* FIXME $$$ Move this to a separate function? */ while(link_values.command != PlayThis) { #ifdef TRACE vmPrint_LINK(link_values);#endif switch(link_values.command) { case LinkNoLink: return do_nothing; case LinkTopC: link_values = play_Cell(); break; case LinkNextC: // What if cellN becomes > nr_of_cells? if(state.cellN == state.pgc->nr_of_cells) return do_nothing; // it should do nothing state.cellN += 1; link_values = play_Cell(); break; case LinkPrevC: // What if cellN becomes < 1? if(state.cellN == 0) return do_nothing; // it should do nothing state.cellN -= 1; link_values = play_Cell(); break; case LinkTopPG: // Does pgN always contain the current value? link_values = play_PG(); break; case LinkNextPG: if(next_PG(&link_values) == -1) return do_nothing; // it must do nothing, do not exit... break; case LinkPrevPG: if(prev_PG(&link_values) == -1) return do_nothing; // it must do nothing, do not exit... break; case LinkTopPGC: link_values = play_PGC(); break; case LinkNextPGC: assert(state.pgc->next_pgc_nr != 0); if(get_PGC(state.pgc->next_pgc_nr)) return do_nothing; // do nothing, do not exit... assert(0); link_values = play_PGC(); break; case LinkPrevPGC: assert(state.pgc->prev_pgc_nr != 0); if(get_PGC(state.pgc->prev_pgc_nr)) return do_nothing; // do nothing, do not exit... assert(0); link_values = play_PGC(); break; case LinkGoUpPGC: assert(state.pgc->goup_pgc_nr != 0); if(get_PGC(state.pgc->goup_pgc_nr)) return do_nothing; // do nothing, do not exit... assert(0); link_values = play_PGC(); break; case LinkTailPGC: link_values = play_PGC_post(); break; case LinkRSM: /* Updates link_values if successful */ if(!vm_resume_int(&link_values)) { /* Nothing / Faild. What should we do? Do we need closer interaction * with the command evaluatore to be able to turn this in to a NOP? */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -