📄 vm.c
字号:
/* * Copyright (C) 2000, 2001 Håkan Hjort * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net> * 2002-2004 the dvdnav project * * This file is part of libdvdnav, a DVD navigation library. It is modified * from a file originally part of the Ogle DVD player. * * libdvdnav is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * libdvdnav is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * * $Id: vm.c,v 1.32 2004/12/20 19:27:20 mroi Exp $ * */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <stdio.h>#include <string.h>#include <stdlib.h>#include <unistd.h>#include <inttypes.h>#include <assert.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include "ifo_types.h"#include "ifo_read.h"#include "dvdnav_internal.h"#ifdef _MSC_VER#include <io.h> /* read() */#endif /* _MSC_VER *//*#define STRICT*//* Local prototypes *//* get_XYZ returns a value. * set_XYZ sets state using passed parameters. * returns success/failure. *//* Play */static link_t play_PGC(vm_t *vm);static link_t play_PGC_PG(vm_t *vm, int pgN);static link_t play_PGC_post(vm_t *vm);static link_t play_PG(vm_t *vm);static link_t play_Cell(vm_t *vm);static link_t play_Cell_post(vm_t *vm);/* Process link - returns 1 if a hop has been performed */static int process_command(vm_t *vm,link_t link_values);/* Set */static int set_TT(vm_t *vm, int tt);static int set_PTT(vm_t *vm, int tt, int ptt);static int set_VTS_TT(vm_t *vm, int vtsN, int vts_ttn);static int set_VTS_PTT(vm_t *vm, int vtsN, int vts_ttn, int part);static int set_FP_PGC(vm_t *vm);static int set_MENU(vm_t *vm, int menu);static int set_PGCN(vm_t *vm, int pgcN);static int set_PGN(vm_t *vm); /* Set PGN based on (vm->state).CellN */static void set_RSMinfo(vm_t *vm, int cellN, int blockN);/* Get */static int get_TT(vm_t *vm, int vtsN, int vts_ttn);static int get_ID(vm_t *vm, int id);static int get_PGCN(vm_t *vm);static pgcit_t* get_MENU_PGCIT(vm_t *vm, ifo_handle_t *h, uint16_t lang);static pgcit_t* get_PGCIT(vm_t *vm);/* Helper functions */#ifdef TRACEstatic void vm_print_current_domain_state(vm_t *vm) { switch((vm->state).domain) { case VTS_DOMAIN: fprintf(MSG_OUT, "libdvdnav: Video Title Domain: -\n"); break; case VTSM_DOMAIN: fprintf(MSG_OUT, "libdvdnav: Video Title Menu Domain: -\n"); break; case VMGM_DOMAIN: fprintf(MSG_OUT, "libdvdnav: Video Manager Menu Domain: -\n"); break; case FP_DOMAIN: fprintf(MSG_OUT, "libdvdnav: First Play Domain: -\n"); break; default: fprintf(MSG_OUT, "libdvdnav: Unknown Domain: -\n"); break; } fprintf(MSG_OUT, "libdvdnav: VTS:%d PGC:%d PG:%u CELL:%u BLOCK:%u VTS_TTN:%u TTN:%u TT_PGCN:%u\n", (vm->state).vtsN, get_PGCN(vm), (vm->state).pgN, (vm->state).cellN, (vm->state).blockN, (vm->state).VTS_TTN_REG, (vm->state).TTN_REG, (vm->state).TT_PGCN_REG);}#endifstatic void dvd_read_name(char *name, const char *device) { /* Because we are compiling with _FILE_OFFSET_BITS=64 * all off_t are 64bit. */ off_t off; int fd, i; uint8_t data[DVD_VIDEO_LB_LEN]; /* Read DVD name */ fd = open(device, O_RDONLY); if (fd > 0) { off = lseek( fd, 32 * (off_t) DVD_VIDEO_LB_LEN, SEEK_SET ); if( off == ( 32 * (off_t) DVD_VIDEO_LB_LEN ) ) { off = read( fd, data, DVD_VIDEO_LB_LEN ); close(fd); if (off == ( (off_t) DVD_VIDEO_LB_LEN )) { fprintf(MSG_OUT, "libdvdnav: DVD Title: "); for(i=25; i < 73; i++ ) { if((data[i] == 0)) break; if((data[i] > 32) && (data[i] < 127)) { fprintf(MSG_OUT, "%c", data[i]); } else { fprintf(MSG_OUT, " "); } } strncpy(name, &data[25], 48); name[48] = 0; fprintf(MSG_OUT, "\nlibdvdnav: DVD Serial Number: "); for(i=73; i < 89; i++ ) { if((data[i] == 0)) break; if((data[i] > 32) && (data[i] < 127)) { fprintf(MSG_OUT, "%c", data[i]); } else { fprintf(MSG_OUT, " "); } } fprintf(MSG_OUT, "\nlibdvdnav: DVD Title (Alternative): "); for(i=89; i < 128; i++ ) { if((data[i] == 0)) break; if((data[i] > 32) && (data[i] < 127)) { fprintf(MSG_OUT, "%c", data[i]); } else { fprintf(MSG_OUT, " "); } } fprintf(MSG_OUT, "\n"); } else { fprintf(MSG_OUT, "libdvdnav: Can't read name block. Probably not a DVD-ROM device.\n"); } } else { fprintf(MSG_OUT, "libdvdnav: Can't seek to block %u\n", 32 ); } close(fd); } else { fprintf(MSG_OUT, "NAME OPEN FAILED\n"); }}static int ifoOpenNewVTSI(vm_t *vm, dvd_reader_t *dvd, int vtsN) { if((vm->state).vtsN == vtsN) { return 1; /* We alread have it */ } if(vm->vtsi != NULL) ifoClose(vm->vtsi); vm->vtsi = ifoOpenVTSI(dvd, vtsN); if(vm->vtsi == NULL) { fprintf(MSG_OUT, "libdvdnav: ifoOpenVTSI failed\n"); return 0; } if(!ifoRead_VTS_PTT_SRPT(vm->vtsi)) { fprintf(MSG_OUT, "libdvdnav: ifoRead_VTS_PTT_SRPT failed\n"); return 0; } if(!ifoRead_PGCIT(vm->vtsi)) { fprintf(MSG_OUT, "libdvdnav: ifoRead_PGCIT failed\n"); return 0; } if(!ifoRead_PGCI_UT(vm->vtsi)) { fprintf(MSG_OUT, "libdvdnav: ifoRead_PGCI_UT failed\n"); return 0; } if(!ifoRead_VOBU_ADMAP(vm->vtsi)) { fprintf(MSG_OUT, "libdvdnav: ifoRead_VOBU_ADMAP vtsi failed\n"); return 0; } if(!ifoRead_TITLE_VOBU_ADMAP(vm->vtsi)) { fprintf(MSG_OUT, "libdvdnav: ifoRead_TITLE_VOBU_ADMAP vtsi failed\n"); return 0; } (vm->state).vtsN = vtsN; return 1;}/* Initialisation & Destruction */vm_t* vm_new_vm() { return (vm_t*)calloc(sizeof(vm_t), sizeof(char));}void vm_free_vm(vm_t *vm) { vm_stop(vm); free(vm);}/* IFO Access */ifo_handle_t *vm_get_vmgi(vm_t *vm) { return vm->vmgi;}ifo_handle_t *vm_get_vtsi(vm_t *vm) { return vm->vtsi;}/* Reader Access */dvd_reader_t *vm_get_dvd_reader(vm_t *vm) { return vm->dvd;}/* Basic Handling */int vm_start(vm_t *vm) { /* Set pgc to FP (First Play) pgc */ set_FP_PGC(vm); process_command(vm, play_PGC(vm)); return !vm->stopped;}void vm_stop(vm_t *vm) { if(vm->vmgi) { ifoClose(vm->vmgi); vm->vmgi=NULL; } if(vm->vtsi) { ifoClose(vm->vtsi); vm->vtsi=NULL; } if(vm->dvd) { DVDClose(vm->dvd); vm->dvd=NULL; } vm->stopped = 1;} int vm_reset(vm_t *vm, const char *dvdroot) { /* Setup State */ memset((vm->state).registers.SPRM, 0, sizeof((vm->state).registers.SPRM)); memset((vm->state).registers.GPRM, 0, sizeof((vm->state).registers.GPRM)); memset((vm->state).registers.GPRM_mode, 0, sizeof((vm->state).registers.GPRM_mode)); memset((vm->state).registers.GPRM_mode, 0, sizeof((vm->state).registers.GPRM_mode)); memset((vm->state).registers.GPRM_time, 0, sizeof((vm->state).registers.GPRM_time)); (vm->state).registers.SPRM[0] = ('e'<<8)|'n'; /* Player Menu Languange code */ (vm->state).AST_REG = 15; /* 15 why? */ (vm->state).SPST_REG = 62; /* 62 why? */ (vm->state).AGL_REG = 1; (vm->state).TTN_REG = 1; (vm->state).VTS_TTN_REG = 1; /* (vm->state).TT_PGCN_REG = 0 */ (vm->state).PTTN_REG = 1; (vm->state).HL_BTNN_REG = 1 << 10; (vm->state).PTL_REG = 15; /* Parental Level */ (vm->state).registers.SPRM[12] = ('U'<<8)|'S'; /* Parental Management Country Code */ (vm->state).registers.SPRM[16] = ('e'<<8)|'n'; /* Initial Language Code for Audio */ (vm->state).registers.SPRM[18] = ('e'<<8)|'n'; /* Initial Language Code for Spu */ (vm->state).registers.SPRM[20] = 0x1; /* Player Regional Code Mask. Region free! */ (vm->state).registers.SPRM[14] = 0x100; /* Try Pan&Scan */ (vm->state).pgN = 0; (vm->state).cellN = 0; (vm->state).cell_restart = 0; (vm->state).domain = FP_DOMAIN; (vm->state).rsm_vtsN = 0; (vm->state).rsm_cellN = 0; (vm->state).rsm_blockN = 0; (vm->state).vtsN = -1; if (vm->dvd && dvdroot) { /* a new dvd device has been requested */ vm_stop(vm); } if (!vm->dvd) { vm->dvd = DVDOpen(dvdroot); if(!vm->dvd) { fprintf(MSG_OUT, "libdvdnav: vm: faild to open/read the DVD\n"); return 0; } dvd_read_name(vm->dvd_name, dvdroot); vm->map = remap_loadmap(vm->dvd_name); vm->vmgi = ifoOpenVMGI(vm->dvd); if(!vm->vmgi) { fprintf(MSG_OUT, "libdvdnav: vm: faild to read VIDEO_TS.IFO\n"); return 0; } if(!ifoRead_FP_PGC(vm->vmgi)) { fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_FP_PGC failed\n"); return 0; } if(!ifoRead_TT_SRPT(vm->vmgi)) { fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_TT_SRPT failed\n"); return 0; } if(!ifoRead_PGCI_UT(vm->vmgi)) { fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_PGCI_UT failed\n"); return 0; } if(!ifoRead_PTL_MAIT(vm->vmgi)) { fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_PTL_MAIT failed\n"); /* return 0; Not really used for now.. */ } if(!ifoRead_VTS_ATRT(vm->vmgi)) { fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_VTS_ATRT failed\n"); /* return 0; Not really used for now.. */ } if(!ifoRead_VOBU_ADMAP(vm->vmgi)) { fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_VOBU_ADMAP vgmi failed\n"); /* return 0; Not really used for now.. */ } /* ifoRead_TXTDT_MGI(vmgi); Not implemented yet */ } if (vm->vmgi) { int i, mask; fprintf(MSG_OUT, "libdvdnav: DVD disk reports itself with Region mask 0x%08x. Regions:", vm->vmgi->vmgi_mat->vmg_category); for (i = 1, mask = 1; i <= 8; i++, mask <<= 1) if (((vm->vmgi->vmgi_mat->vmg_category >> 16) & mask) == 0) fprintf(MSG_OUT, " %d", i); fprintf(MSG_OUT, "\n"); } return 1;}/* copying and merging */vm_t *vm_new_copy(vm_t *source) { vm_t *target = vm_new_vm(); int vtsN; int pgcN = get_PGCN(source); int pgN = (source->state).pgN; assert(pgcN); memcpy(target, source, sizeof(vm_t)); /* open a new vtsi handle, because the copy might switch to another VTS */ target->vtsi = NULL; vtsN = (target->state).vtsN; if (vtsN > 0) { (target->state).vtsN = 0; if (!ifoOpenNewVTSI(target, target->dvd, vtsN)) assert(0); /* restore pgc pointer into the new vtsi */ if (!set_PGCN(target, pgcN)) assert(0); (target->state).pgN = pgN; } return target;}void vm_merge(vm_t *target, vm_t *source) { if(target->vtsi) ifoClose(target->vtsi); memcpy(target, source, sizeof(vm_t)); memset(source, 0, sizeof(vm_t));}void vm_free_copy(vm_t *vm) { if(vm->vtsi) ifoClose(vm->vtsi); free(vm);}/* regular playback */void vm_position_get(vm_t *vm, vm_position_t *position) { position->button = (vm->state).HL_BTNN_REG >> 10; position->vts = (vm->state).vtsN; position->domain = (vm->state).domain; position->spu_channel = (vm->state).SPST_REG; position->audio_channel = (vm->state).AST_REG; position->angle_channel = (vm->state).AGL_REG; position->hop_channel = vm->hop_channel; /* Increases by one on each hop */ position->cell = (vm->state).cellN; position->cell_restart = (vm->state).cell_restart; position->cell_start = (vm->state).pgc->cell_playback[(vm->state).cellN - 1].first_sector; position->still = (vm->state).pgc->cell_playback[(vm->state).cellN - 1].still_time; position->block = (vm->state).blockN; /* handle PGC stills at PGC end */ if ((vm->state).cellN == (vm->state).pgc->nr_of_cells) position->still += (vm->state).pgc->still_time; /* still already determined */ if (position->still) return; /* This is a rough fix for some strange still situations on some strange DVDs. * There are discs (like the German "Back to the Future" RC2) where the only * indication of a still is a cell playback time higher than the time the frames * in this cell actually take to play (like 1 frame with 1 minute playback time). * On the said BTTF disc, for these cells last_sector and last_vobu_start_sector * are equal and the cells are very short, so we abuse these conditions to * detect such discs. I consider these discs broken, so the fix is somewhat * broken, too. */ if (((vm->state).pgc->cell_playback[(vm->state).cellN - 1].last_sector == (vm->state).pgc->cell_playback[(vm->state).cellN - 1].last_vobu_start_sector) && ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].last_sector - (vm->state).pgc->cell_playback[(vm->state).cellN - 1].first_sector < 1024)) { int time; int size = (vm->state).pgc->cell_playback[(vm->state).cellN - 1].last_sector - (vm->state).pgc->cell_playback[(vm->state).cellN - 1].first_sector; time = ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.hour >> 4 ) * 36000; time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.hour & 0x0f) * 3600; time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.minute >> 4 ) * 600; time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.minute & 0x0f) * 60; time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.second >> 4 ) * 10; time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.second & 0x0f) * 1; if (!time || size / time > 30) /* datarate is too high, it might be a very short, but regular cell */ return; if (time > 0xff) time = 0xff; position->still = time; }}void vm_get_next_cell(vm_t *vm) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -