# HG changeset patch # User mroi # Date 1045755141 0 # Node ID b6834e6359cfdce8cd5f2a8a972873ccf97f3a52 # Parent ec2df154be567768c2c540e33cb9da677a87c1eb big libdvdnav cleanup, quoting the ChangeLog: * some bugfixes * code cleanup * build process polishing * more sensible event order in get_next_block to ensure useful event delivery * VOBU level resume * fixed: seeking in a multiangle feature briefly showed the wrong angle diff -r ec2df154be56 -r b6834e6359cf Makefile.am --- a/Makefile.am Mon Jan 13 13:33:45 2003 +0000 +++ b/Makefile.am Thu Feb 20 15:32:21 2003 +0000 @@ -1,6 +1,6 @@ lib_LTLIBRARIES = libdvdnav.la -AM_CFLAGS = @GLOBAL_CFLAGS@ @THREAD_CFLAGS@ +AM_CFLAGS = @THREAD_CFLAGS@ libdvdnav_la_SOURCES = decoder.c dvdnav.c vm.c vmcmd.c \ read_cache.c navigation.c highlight.c \ @@ -12,6 +12,8 @@ include_HEADERS = decoder.h dvdnav.h dvdnav_events.h \ dvdnav_internal.h vm.h vmcmd.h read_cache.h dvd_types.h remap.h +EXTRA_DIST = README.MAP FELLOWSHIP.map + ### # Install header files (default=$includedir/xine) # diff -r ec2df154be56 -r b6834e6359cf decoder.c --- a/decoder.c Mon Jan 13 13:33:45 2003 +0000 +++ b/decoder.c Thu Feb 20 15:32:21 2003 +0000 @@ -41,13 +41,14 @@ uint64_t bit_mask=0xffffffffffffffff; /* I could put -1 instead */ uint64_t examining = 0; int32_t bits; + if (count == 0) return 0; if ( ((count+start) > 64) || (count > 32) || (start > 63) || (count < 0) || - (start < 0) ){ + (start < 0) ) { fprintf(MSG_OUT, "libdvdnav: Bad call to vm_getbits. Parameter out of range\n"); assert(0); } @@ -101,7 +102,7 @@ if(reg & 0x80) { if ((reg & 0x1f) == 20) { fprintf(MSG_OUT, "libdvdnav: Suspected RCE Region Protection!!!"); - } + } return command->registers->SPRM[reg & 0x1f]; /* FIXME max 24 not 32 */ } else { return get_GPRM(command->registers, reg & 0x0f) ; @@ -570,8 +571,7 @@ return res; } -/* Evaluate a set of commands in the given register set (which is - * modified */ +/* Evaluate a set of commands in the given register set (which is modified) */ int32_t vmEval_CMD(vm_cmd_t commands[], int32_t num_commands, registers_t *registers, link_t *return_values) { int32_t i = 0; @@ -587,6 +587,7 @@ fprintf(MSG_OUT, "libdvdnav: --------------------------------------------\n"); fprintf(MSG_OUT, "libdvdnav: Single stepping commands\n"); #endif + i = 0; while(i < num_commands && total < 100000) { int32_t line; @@ -594,6 +595,7 @@ #ifdef TRACE vmPrint_CMD(i, &commands[i]); #endif + line = eval_command(&commands[i].bytes[0], registers, return_values); if (line < 0) { /* Link command */ @@ -621,6 +623,8 @@ return 0; } +#ifdef TRACE + static char *linkcmd2str(link_cmd_t cmd) { switch(cmd) { case LinkNoLink: @@ -762,3 +766,6 @@ fprintf(MSG_OUT, "%04lx|", registers->GPRM_time[i].tv_sec & 0xffff); fprintf(MSG_OUT, "\n"); } + +#endif + diff -r ec2df154be56 -r b6834e6359cf decoder.h --- a/decoder.h Mon Jan 13 13:33:45 2003 +0000 +++ b/decoder.h Thu Feb 20 15:32:21 2003 +0000 @@ -30,9 +30,7 @@ #include /* vm_cmd_t */ -/* Uncomment for tracing */ -#define TRACE - +/* link command types */ typedef enum { LinkNoLink = 0, @@ -76,6 +74,7 @@ PlayThis } link_cmd_t; +/* a link's data set */ typedef struct { link_cmd_t command; uint16_t data1; @@ -83,6 +82,7 @@ uint16_t data3; } link_t; +/* the VM registers */ typedef struct { uint16_t SPRM[24]; uint16_t GPRM[16]; @@ -90,18 +90,28 @@ struct timeval GPRM_time[16]; /* For counter mode */ } registers_t; -typedef struct -{ +/* a VM command data set */ +typedef struct { uint64_t instruction; uint64_t examined; registers_t *registers; } command_t; +/* the big VM function, executing the given commands and writing + * the link where to continue, the return value indicates if a jump + * has been performed */ int vmEval_CMD(vm_cmd_t commands[], int num_commands, registers_t *registers, link_t *return_values); -void vmPrint_LINK(link_t value); -void vmPrint_registers( registers_t *registers ); +/* extracts some bits from the command */ uint32_t vm_getbits(command_t* command, int start, int count); +#ifdef TRACE +/* for debugging: prints a link in readable form */ +void vmPrint_LINK(link_t value); + +/* for debugging: dumps VM registers */ +void vmPrint_registers( registers_t *registers ); +#endif + #endif /* DECODER_H_INCLUDED */ diff -r ec2df154be56 -r b6834e6359cf dvdnav.c --- a/dvdnav.c Mon Jan 13 13:33:45 2003 +0000 +++ b/dvdnav.c Thu Feb 20 15:32:21 2003 +0000 @@ -30,7 +30,6 @@ */ #include -#include #include "dvdnav_internal.h" #include "read_cache.h" @@ -42,119 +41,9 @@ #include "remap.h" -/* - * NOTE: - * All NLCK_*() function are not mutex locked, this made them reusable in - * a locked context. Take care. - * - */ - -/* Current domain (backend to dvdnav_is_domain_() funcs) */ -static int8_t NLCK_dvdnav_is_domain(dvdnav_t *this, domain_t domain) { - dvd_state_t *state; - - if((!this) || (!this->started) || (!this->vm)) - return -1; - - state = &(this->vm->state); - - if(!state) - return -1; - - return (state->domain == domain) ? 1 : 0; -} - -static int8_t _dvdnav_is_domain(dvdnav_t *this, domain_t domain) { - int8_t retval; - - pthread_mutex_lock(&this->vm_lock); - retval = NLCK_dvdnav_is_domain(this, domain); - pthread_mutex_unlock(&this->vm_lock); - - return retval; -} - -static int8_t NCLK_dvdnav_get_audio_logical_stream(dvdnav_t *this, uint8_t audio_num) { - dvd_state_t *state; - int8_t logical = -1; - - if(!NLCK_dvdnav_is_domain(this, VTS_DOMAIN)) - audio_num = 0; - - state = &(this->vm->state); - - if(audio_num < 8) { - if(state->pgc->audio_control[audio_num] & (1 << 15)) { - logical = (state->pgc->audio_control[audio_num] >> 8) & 0x07; - } - } - - return logical; -} +static dvdnav_status_t dvdnav_clear(dvdnav_t * this) { + /* clear everything except file, vm, mutex, readahead */ -static int8_t NCLK_dvdnav_get_spu_logical_stream(dvdnav_t *this, uint8_t subp_num) { - dvd_state_t *state; - ifo_handle_t *vtsi; - - if(!this) - return -1; - - state = &(this->vm->state); - vtsi = this->vm->vtsi; - - if(subp_num >= vtsi->vtsi_mat->nr_of_vts_subp_streams) - return -1; - - return vm_get_subp_stream(this->vm, subp_num, 0); -} - -static int8_t NLCK_dvdnav_get_active_spu_stream(dvdnav_t *this) { - dvd_state_t *state; - int8_t subp_num; - int stream_num; - - state = &(this->vm->state); - subp_num = state->SPST_REG & ~0x40; - stream_num = NCLK_dvdnav_get_spu_logical_stream(this, subp_num); - - if(stream_num == -1) - for(subp_num = 0; subp_num < 32; subp_num++) - if(state->pgc->subp_control[subp_num] & (1 << 31)) { - stream_num = NCLK_dvdnav_get_spu_logical_stream(this, subp_num); - break; - } - - return stream_num; -} - -uint8_t dvdnav_get_video_aspect(dvdnav_t *this) { - uint8_t retval; - - pthread_mutex_lock(&this->vm_lock); - retval = (uint8_t) vm_get_video_aspect(this->vm); - pthread_mutex_unlock(&this->vm_lock); - - return retval; -} - -uint8_t dvdnav_get_video_scale_permission(dvdnav_t *this) { - uint8_t retval; - - pthread_mutex_lock(&this->vm_lock); - retval = (uint8_t) vm_get_video_scale_permission(this->vm); - pthread_mutex_unlock(&this->vm_lock); - - return retval; -} - -dvdnav_status_t dvdnav_clear(dvdnav_t * this) { - if (!this) { - printerr("Passed a NULL pointer"); - return S_ERR; - } - /* clear everything except path, file, vm, mutex, readahead */ - - /* path */ if (this->file) DVDCloseFile(this->file); this->file = NULL; this->open_vtsN = -1; @@ -166,17 +55,15 @@ /* Set initial values of flags */ this->position_current.still = 0; this->skip_still = 0; - this->stop = 0; this->spu_clut_changed = 0; - this->started=0; - /* this->use_read_ahead */ + this->started = 0; dvdnav_read_cache_clear(this->cache); return S_OK; } -dvdnav_status_t dvdnav_open(dvdnav_t** dest, char *path) { +dvdnav_status_t dvdnav_open(dvdnav_t** dest, const char *path) { dvdnav_t *this; struct timeval time; @@ -186,10 +73,9 @@ (*dest) = NULL; this = (dvdnav_t*)malloc(sizeof(dvdnav_t)); if(!this) - return S_ERR; + return S_ERR; memset(this, 0, (sizeof(dvdnav_t) ) ); /* Make sure this structure is clean */ - (*dest) = this; - + pthread_mutex_init(&this->vm_lock, NULL); /* Initialise the error string */ printerr(""); @@ -197,13 +83,13 @@ /* Initialise the VM */ this->vm = vm_new_vm(); if(!this->vm) { - printerr("Error initialising the DVD VM"); + printerr("Error initialising the DVD VM."); pthread_mutex_destroy(&this->vm_lock); free(this); return S_ERR; } - if(vm_reset(this->vm, path) == -1) { - printerr("Error starting the VM / opening the DVD device"); + if(!vm_reset(this->vm, path)) { + printerr("Error starting the VM / opening the DVD device."); pthread_mutex_destroy(&this->vm_lock); vm_free_vm(this->vm); free(this); @@ -213,34 +99,34 @@ /* Set the path. FIXME: Is a deep copy 'right' */ strncpy(this->path, path, MAX_PATH_LEN); - dvdnav_clear(this); - /* Pre-open and close a file so that the CSS-keys are cached. */ this->file = DVDOpenFile(vm_get_dvd_reader(this->vm), 0, DVD_READ_MENU_VOBS); - if (this->file) DVDCloseFile(this->file); - this->file = NULL; /* Start the read-ahead cache. */ this->cache = dvdnav_read_cache_new(this); - /* Seed the random numbers. So that the DVD VM Command rand()i - * gives a different start value each time a DVD is played. - */ - gettimeofday(&time,NULL); + /* Seed the random numbers. So that the DVD VM Command rand() + * gives a different start value each time a DVD is played. */ + gettimeofday(&time, NULL); srand(time.tv_usec); + dvdnav_clear(this); + + (*dest) = this; return S_OK; } dvdnav_status_t dvdnav_close(dvdnav_t *this) { - if(!this) { - printerr("Passed a NULL pointer"); - return S_ERR; - } + #ifdef LOG_DEBUG fprintf(MSG_OUT, "libdvdnav: close:called\n"); #endif + if(!this) { + printerr("Passed a NULL pointer."); + return S_ERR; + } + if (this->file) { DVDCloseFile(this->file); #ifdef LOG_DEBUG @@ -250,24 +136,18 @@ } /* Free the VM */ - if(this->vm) { + if(this->vm) vm_free_vm(this->vm); - } - if (this->file) { - DVDCloseFile(this->file); -#ifdef LOG_DEBUG - fprintf(MSG_OUT, "libdvdnav: close2:file closing\n"); -#endif - this->file = NULL; - } + pthread_mutex_destroy(&this->vm_lock); /* We leave the final freeing of the entire structure to the cache, * because we don't know, if there are still buffers out in the wild, * that must return first. */ - if(this->cache) { + if(this->cache) dvdnav_read_cache_free(this->cache); - } else free(this); + else + free(this); return S_OK; } @@ -278,101 +158,83 @@ #ifdef LOG_DEBUG fprintf(MSG_OUT, "libdvdnav: reset:called\n"); #endif + if(!this) { - printerr("Passed a NULL pointer"); + printerr("Passed a NULL pointer."); return S_ERR; } -#ifdef LOG_DEBUG - fprintf(MSG_OUT, "libdvdnav: getting lock\n"); -#endif + pthread_mutex_lock(&this->vm_lock); + #ifdef LOG_DEBUG fprintf(MSG_OUT, "libdvdnav: reseting vm\n"); #endif - if(vm_reset(this->vm, NULL) == -1) { - printerr("Error restarting the VM"); + if(!vm_reset(this->vm, NULL)) { + printerr("Error restarting the VM."); pthread_mutex_unlock(&this->vm_lock); return S_ERR; } #ifdef LOG_DEBUG fprintf(MSG_OUT, "libdvdnav: clearing dvdnav\n"); #endif - result=dvdnav_clear(this); -#ifdef LOG_DEBUG - fprintf(MSG_OUT, "libdvdnav: unlocking\n"); -#endif + result = dvdnav_clear(this); + pthread_mutex_unlock(&this->vm_lock); return result; } -dvdnav_status_t dvdnav_path(dvdnav_t *this, char** path) { - if(!this || !path || !(*path)) { +dvdnav_status_t dvdnav_path(dvdnav_t *this, const char** path) { + + if(!this || !path) { + printerr("Passed a NULL pointer."); return S_ERR; } - /* FIXME: Is shallow copy 'right'? */ (*path) = this->path; return S_OK; } -char* dvdnav_err_to_string(dvdnav_t *this) { - if(!this) { - /* Shold this be "passed a NULL pointer?" */ +const char* dvdnav_err_to_string(dvdnav_t *this) { + + if(!this) return "Hey! You gave me a NULL pointer you naughty person!"; - } return this->err_str; } -/** +/* * Returns 1 if block contains NAV packet, 0 otherwise. * Precesses said NAV packet if present. * * Most of the code in here is copied from xine's MPEG demuxer * so any bugs which are found in that should be corrected here also. */ -int dvdnav_decode_packet(dvdnav_t *this, uint8_t *p, dsi_t* nav_dsi, pci_t* nav_pci) { - int bMpeg1=0; +static int dvdnav_decode_packet(dvdnav_t *this, uint8_t *p, dsi_t *nav_dsi, pci_t *nav_pci) { + int bMpeg1 = 0; uint32_t nHeaderLen; uint32_t nPacketLen; uint32_t nStreamID; -/* uint8_t *p_start=p; */ - - - if (p==NULL) { - fprintf(MSG_OUT, "libdvdnav: Passed a NULL pointer.\n"); - return 0; - } - - /* dprint("Checking packet...\n"); */ if (p[3] == 0xBA) { /* program stream pack header */ - int nStuffingBytes; - /* xprintf (VERBOSE|DEMUX, "program stream pack header\n"); */ - bMpeg1 = (p[4] & 0x40) == 0; if (bMpeg1) { - p += 12; + p += 12; } else { /* mpeg2 */ nStuffingBytes = p[0xD] & 0x07; p += 14 + nStuffingBytes; } } - if (p[3] == 0xbb) { /* program stream system header */ - int nHeaderLen; - nHeaderLen = (p[4] << 8) | p[5]; p += 6 + nHeaderLen; } /* we should now have a PES packet here */ - if (p[0] || p[1] || (p[2] != 1)) { fprintf(MSG_OUT, "libdvdnav: demux error! %02x %02x %02x (should be 0x000001) \n",p[0],p[1],p[2]); return 0; @@ -385,14 +247,14 @@ p += nHeaderLen; if (nStreamID == 0xbf) { /* Private stream 2 */ -/* - * int i; - * fprintf(MSG_OUT, "libdvdnav: nav packet=%u\n",p-p_start-6); - * for(i=0;i<80;i++) { - * fprintf(MSG_OUT, "%02x ",p[i-6]); - * } - * fprintf(MSG_OUT, "\n"); - */ +#if 0 + int i; + fprintf(MSG_OUT, "libdvdnav: nav packet=%u\n",p-p_start-6); + for(i=0;i<80;i++) + fprintf(MSG_OUT, "%02x ",p[i-6]); + fprintf(MSG_OUT, "\n"); +#endif + if(p[0] == 0x00) { navRead_PCI(nav_pci, p+1); } @@ -403,9 +265,7 @@ if(p[6] == 0x01) { nPacketLen = p[4] << 8 | p[5]; p += 6; - /* dprint("NAV DSI packet\n"); */ navRead_DSI(nav_dsi, p+1); - } return 1; } @@ -415,7 +275,7 @@ /* DSI is used for most angle stuff. * PCI is used for only non-seemless angle stuff */ -int dvdnav_get_vobu(dvdnav_t *self, dsi_t* nav_dsi, pci_t* nav_pci, dvdnav_vobu_t* vobu) { +static int dvdnav_get_vobu(dvdnav_t *this, dsi_t *nav_dsi, pci_t *nav_pci, dvdnav_vobu_t *vobu) { uint32_t next; int angle, num_angle; @@ -434,70 +294,72 @@ * Should really assert if bit 31 != 1 */ - /* Relative offset from vobu_start */ - vobu->vobu_next = ( nav_dsi->vobu_sri.next_vobu & 0x3fffffff ); - - /* Old code -- may still be sueful one day +#if 0 + /* Old code -- may still be useful one day */ if(nav_dsi->vobu_sri.next_vobu != SRI_END_OF_CELL ) { vobu->vobu_next = ( nav_dsi->vobu_sri.next_vobu & 0x3fffffff ); } else { vobu->vobu_next = vobu->vobu_length; - } */ + } +#else + /* Relative offset from vobu_start */ + vobu->vobu_next = ( nav_dsi->vobu_sri.next_vobu & 0x3fffffff ); +#endif - dvdnav_get_angle_info(self, &angle, &num_angle); + vm_get_angle_info(this->vm, &angle, &num_angle); + + /* FIMXE: The angle reset doesn't work for some reason for the moment */ #if 0 - /* FIMXE: The angle reset doesn't work for some reason for the moment */ - if((num_angle < angle) && (angle != 1)) { - printf("OOOOOOO angle ends!\n"); + fprintf(MSG_OUT, "libdvdnav: angle ends!\n"); /* This is to switch back to angle one when we * finish with angles. */ - dvdnav_angle_change(self, 1); + dvdnav_angle_change(this, 1); } #endif if(num_angle != 0) { - next = nav_pci->nsml_agli.nsml_agl_dsta[angle-1]; - if(next != 0) { + if((next = nav_pci->nsml_agli.nsml_agl_dsta[angle-1]) != 0) { if((next & 0x3fffffff) != 0) { - if(next & 0x80000000) { + if(next & 0x80000000) vobu->vobu_next = - (int32_t)(next & 0x3fffffff); - } else { + else vobu->vobu_next = + (int32_t)(next & 0x3fffffff); - } } - - } else if( nav_dsi->sml_agli.data[angle-1].address != 0 ) { - next = nav_dsi->sml_agli.data[angle-1].address; + } else if((next = nav_dsi->sml_agli.data[angle-1].address) != 0) { vobu->vobu_length = nav_dsi->sml_pbi.ilvu_ea; - if((next & 0x80000000) && (next != 0x7fffffff)) { + if((next & 0x80000000) && (next != 0x7fffffff)) vobu->vobu_next = - (int32_t)(next & 0x3fffffff); - } else { + else vobu->vobu_next = + (int32_t)(next & 0x3fffffff); - } } } return 1; } -/* This is the main get_next_block function which actually gets the media stream video and audio etc. - * The use of this function is optional, with the application programmer - * free to implement their own version of this function - * FIXME: Make the function calls from here public API calls. +/* + * These are the main get_next_block function which actually get the media stream video and audio etc. + * + * There are two versions: The second one is using the zero-copy read ahead cache and therefore + * hands out pointers targetting directly into the cache. + * The first one uses a memcopy to fill this cache block into the application provided memory. + * The benefit of this first one is that no special memory management is needed. The application is + * the only one responsible of allocating and freeing the memory associated with the pointer. + * The drawback is the additional memcopy. */ dvdnav_status_t dvdnav_get_next_block(dvdnav_t *this, unsigned char *buf, - int *event, int *len) { + int *event, int *len) { unsigned char *block; dvdnav_status_t status; block = buf; status = dvdnav_get_next_cache_block(this, &block, event, len); - if (block != buf) { + if (status == S_OK && block != buf) { /* we received a block from the cache, copy it, so we can give it back */ memcpy(buf, block, DVD_VIDEO_LB_LEN); dvdnav_free_cache_block(this, block); @@ -506,14 +368,16 @@ } dvdnav_status_t dvdnav_get_next_cache_block(dvdnav_t *this, unsigned char **buf, - int *event, int *len) { + int *event, int *len) { dvd_state_t *state; int result; + if(!this || !event || !len || !buf || !*buf) { - printerr("Passed a NULL pointer"); + printerr("Passed a NULL pointer."); return S_ERR; } - pthread_mutex_lock(&this->vm_lock); + + pthread_mutex_lock(&this->vm_lock); if(!this->started) { /* Start the VM */ @@ -526,118 +390,64 @@ (*len) = 0; /* Check the STOP flag */ - if(this->stop) { + if(this->vm->stopped) { (*event) = DVDNAV_STOP; - pthread_mutex_unlock(&this->vm_lock); - return S_OK; - } - - /* Check the STILLFRAME flag */ - /* FIXME: Still cell, not still frame */ - if(this->position_current.still != 0) { - dvdnav_still_event_t still_event; - - still_event.length = this->position_current.still; - - (*event) = DVDNAV_STILL_FRAME; - (*len) = sizeof(dvdnav_still_event_t); - memcpy(*buf, &(still_event), sizeof(dvdnav_still_event_t)); - - pthread_mutex_unlock(&this->vm_lock); - return S_OK; - } - - vm_position_get(this->vm,&this->position_next); - /********** - fprintf(MSG_OUT, "libdvdnav: POS-NEXT "); - vm_position_print(this->vm, &this->position_next); - fprintf(MSG_OUT, "libdvdnav: POS-CUR "); - vm_position_print(this->vm, &this->position_current); - **********/ - - if(this->position_current.hop_channel != this->position_next.hop_channel) { - this->position_current.hop_channel = this->position_next.hop_channel; - (*event) = DVDNAV_HOP_CHANNEL; - (*len) = 0; + this->started = 0; pthread_mutex_unlock(&this->vm_lock); return S_OK; } - - - if(this->spu_clut_changed) { - (*event) = DVDNAV_SPU_CLUT_CHANGE; -#ifdef LOG_DEBUG - fprintf(MSG_OUT, "libdvdnav: SPU_CLUT_CHANGE\n"); -#endif - (*len) = 16 * sizeof(uint32_t); - memcpy(*buf, &(state->pgc->palette), 16 * sizeof(uint32_t)); - this->spu_clut_changed = 0; -#ifdef LOG_DEBUG - fprintf(MSG_OUT, "libdvdnav: SPU_CLUT_CHANGE returning S_OK\n"); -#endif - pthread_mutex_unlock(&this->vm_lock); - return S_OK; - } + vm_position_get(this->vm, &this->position_next); - if(this->position_current.spu_channel != this->position_next.spu_channel) { - dvdnav_spu_stream_change_event_t stream_change; - (*event) = DVDNAV_SPU_STREAM_CHANGE; -#ifdef LOG_DEBUG - fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE\n"); +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: POS-NEXT "); + vm_position_print(this->vm, &this->position_next); + fprintf(MSG_OUT, "libdvdnav: POS-CUR "); + vm_position_print(this->vm, &this->position_current); #endif - (*len) = sizeof(dvdnav_spu_stream_change_event_t); - stream_change.physical_wide = vm_get_subp_active_stream(this->vm, 0); - stream_change.physical_letterbox = vm_get_subp_active_stream(this->vm, 1); - stream_change.physical_pan_scan = vm_get_subp_active_stream(this->vm, 2); - memcpy(*buf, &(stream_change), sizeof( dvdnav_spu_stream_change_event_t)); - this->position_current.spu_channel = this->position_next.spu_channel; -#ifdef LOG_DEBUG - fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE stream_id_wide=%d\n",stream_change.physical_wide); - fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE stream_id_letterbox=%d\n",stream_change.physical_letterbox); - fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE stream_id_pan_scan=%d\n",stream_change.physical_pan_scan); -#endif - if (stream_change.physical_wide != -1 && - stream_change.physical_letterbox != -1 && - stream_change.physical_pan_scan != -1) { + + /* did we hop? */ + if(this->position_current.hop_channel != this->position_next.hop_channel) { + (*event) = DVDNAV_HOP_CHANNEL; #ifdef LOG_DEBUG - fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE returning S_OK\n"); -#endif - pthread_mutex_unlock(&this->vm_lock); - return S_OK; - } - } - - if(this->position_current.audio_channel != this->position_next.audio_channel) { - dvdnav_audio_stream_change_event_t stream_change; - (*event) = DVDNAV_AUDIO_STREAM_CHANGE; -#ifdef LOG_DEBUG - fprintf(MSG_OUT, "libdvdnav: AUDIO_STREAM_CHANGE\n"); + fprintf(MSG_OUT, "libdvdnav: HOP_CHANNEL\n"); #endif - (*len) = sizeof(dvdnav_audio_stream_change_event_t); - stream_change.physical= vm_get_audio_active_stream( this->vm ); - memcpy(*buf, &(stream_change), sizeof( dvdnav_audio_stream_change_event_t)); - this->position_current.audio_channel = this->position_next.audio_channel; -#ifdef LOG_DEBUG - fprintf(MSG_OUT, "libdvdnav: AUDIO_STREAM_CHANGE stream_id=%d returning S_OK\n",stream_change.physical); -#endif - pthread_mutex_unlock(&this->vm_lock); - return S_OK; - } - - /* Check the HIGHLIGHT flag */ - /* FIXME: Use BUTTON instead of HIGHLIGHT. */ - if(this->position_current.button != this->position_next.button) { - dvdnav_highlight_event_t hevent; - - hevent.display = 1; - hevent.buttonN = this->position_next.button; - - this->position_current.button = this->position_next.button; - - (*event) = DVDNAV_HIGHLIGHT; - (*len) = sizeof(hevent); - memcpy(*buf, &(hevent), sizeof(hevent)); + if (this->position_next.hop_channel > HOP_SEEK) { + int num_angles = 0, current; + + /* we seeked -> check for multiple angles */ + vm_get_angle_info(this->vm, ¤t, &num_angles); + if (num_angles > 1) { + int result, block; + /* we have to skip the first VOBU when seeking in a multiangle feature, + * because it might belong to the wrong angle */ + block = this->position_next.cell_start + this->position_next.block; + result = dvdnav_read_cache_block(this->cache, block, 1, buf); + if(result <= 0) { + printerr("Error reading NAV packet."); + pthread_mutex_unlock(&this->vm_lock); + return S_ERR; + } + /* Decode nav into pci and dsi. Then get next VOBU info. */ + if(!dvdnav_decode_packet(this, *buf, &this->dsi, &this->pci)) { + printerr("Expected NAV packet but none found."); + pthread_mutex_unlock(&this->vm_lock); + return S_ERR; + } + dvdnav_get_vobu(this, &this->dsi, &this->pci, &this->vobu); + /* skip to next, if there is a next */ + if (this->vobu.vobu_next != SRI_END_OF_CELL) { + this->vobu.vobu_start += this->vobu.vobu_next; + this->vobu.vobu_next = 0; + } + /* update VM state */ + this->vm->state.blockN = this->vobu.vobu_start - this->position_next.cell_start; + } + } + this->position_current.hop_channel = this->position_next.hop_channel; + /* Make blockN > vobu_length to do expected_nav */ + this->vobu.vobu_length = 0; + this->vobu.blockN = 1; pthread_mutex_unlock(&this->vm_lock); return S_OK; } @@ -650,7 +460,6 @@ dvdnav_vts_change_event_t vts_event; if(this->file) { - dvdnav_read_cache_clear(this->cache); DVDCloseFile(this->file); this->file = NULL; } @@ -658,22 +467,22 @@ vts_event.old_vtsN = this->open_vtsN; vts_event.old_domain = this->open_domain; - /* Use the current DOMAIN to find whether to open menu or title VOBs */ + /* Use the DOMAIN to find whether to open menu or title VOBs */ switch(this->position_next.domain) { - case FP_DOMAIN: - case VMGM_DOMAIN: + case FP_DOMAIN: + case VMGM_DOMAIN: domain = DVD_READ_MENU_VOBS; vtsN = 0; break; - case VTSM_DOMAIN: + case VTSM_DOMAIN: domain = DVD_READ_MENU_VOBS; vtsN = this->position_next.vts; break; - case VTS_DOMAIN: + case VTS_DOMAIN: domain = DVD_READ_TITLE_VOBS; vtsN = this->position_next.vts; break; - default: + default: printerr("Unknown domain when changing VTS."); pthread_mutex_unlock(&this->vm_lock); return S_ERR; @@ -695,11 +504,12 @@ /* File opened successfully so return a VTS change event */ (*event) = DVDNAV_VTS_CHANGE; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: VTS_CHANGE\n"); +#endif + (*len) = sizeof(vts_event); memcpy(*buf, &(vts_event), sizeof(vts_event)); - (*len) = sizeof(vts_event); - /* On a VTS change, we want to disable any highlights which - * may have been shown (FIXME: is this valid?) */ this->spu_clut_changed = 1; this->position_current.cell = -1; /* Force an update */ this->position_current.spu_channel = -1; /* Force an update */ @@ -708,33 +518,137 @@ pthread_mutex_unlock(&this->vm_lock); return S_OK; } - /* FIXME: Don't really need "cell", we only need vobu_start */ + + /* Check if the cell changed */ if( (this->position_current.cell != this->position_next.cell) || (this->position_current.cell_restart != this->position_next.cell_restart) || - (this->position_current.vobu_start != this->position_next.vobu_start) || - (this->position_current.vobu_next != this->position_next.vobu_next) ) { - this->position_current.cell = this->position_next.cell; + (this->position_current.cell_start != this->position_next.cell_start) ) { + + (*event) = DVDNAV_CELL_CHANGE; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: CELL_CHANGE\n"); +#endif + (*len) = 0; + + this->position_current.cell = this->position_next.cell; this->position_current.cell_restart = this->position_next.cell_restart; - /* vobu_start changes when PGC or PG changes. */ - this->position_current.vobu_start = this->position_next.vobu_start; - this->position_current.vobu_next = this->position_next.vobu_next; - /* FIXME: Need to set vobu_start, vobu_next */ - this->vobu.vobu_start = this->position_next.vobu_start; - /* vobu_next is use for mid cell resumes */ - this->vobu.vobu_next = this->position_next.vobu_next; - this->vobu.vobu_length = 0; - this->vobu.blockN = this->vobu.vobu_length + 1; - /* Make blockN > vobu_lenght to do expected_nav */ - (*event) = DVDNAV_CELL_CHANGE; - (*len) = 0; + this->position_current.cell_start = this->position_next.cell_start; + this->position_current.block = this->position_next.block; + + /* vobu info is used for mid cell resumes */ + this->vobu.vobu_start = this->position_next.cell_start + this->position_next.block; + this->vobu.vobu_next = 0; + /* Make blockN > vobu_length to do expected_nav */ + this->vobu.vobu_length = 0; + this->vobu.blockN = 1; + + /* update the spu palette at least on PGC changes */ + this->spu_clut_changed = 1; + this->position_current.spu_channel = -1; /* Force an update */ + this->position_current.audio_channel = -1; /* Force an update */ + pthread_mutex_unlock(&this->vm_lock); return S_OK; } - - if (this->vobu.blockN > this->vobu.vobu_length) { - /* End of VOBU */ + /* has the CLUT changed? */ + if(this->spu_clut_changed) { + (*event) = DVDNAV_SPU_CLUT_CHANGE; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: SPU_CLUT_CHANGE\n"); +#endif + (*len) = 16 * sizeof(uint32_t); + memcpy(*buf, &(state->pgc->palette), 16 * sizeof(uint32_t)); + this->spu_clut_changed = 0; + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } + + /* has the SPU channel changed? */ + if(this->position_current.spu_channel != this->position_next.spu_channel) { + dvdnav_spu_stream_change_event_t stream_change; + + (*event) = DVDNAV_SPU_STREAM_CHANGE; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE\n"); +#endif + (*len) = sizeof(dvdnav_spu_stream_change_event_t); + stream_change.physical_wide = vm_get_subp_active_stream(this->vm, 0); + stream_change.physical_letterbox = vm_get_subp_active_stream(this->vm, 1); + stream_change.physical_pan_scan = vm_get_subp_active_stream(this->vm, 2); + memcpy(*buf, &(stream_change), sizeof(dvdnav_spu_stream_change_event_t)); + this->position_current.spu_channel = this->position_next.spu_channel; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE stream_id_wide=%d\n",stream_change.physical_wide); + fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE stream_id_letterbox=%d\n",stream_change.physical_letterbox); + fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE stream_id_pan_scan=%d\n",stream_change.physical_pan_scan); +#endif + if (stream_change.physical_wide != -1 && + stream_change.physical_letterbox != -1 && + stream_change.physical_pan_scan != -1) { +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: SPU_STREAM_CHANGE returning S_OK\n"); +#endif + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } + } + /* has the audio channel changed? */ + if(this->position_current.audio_channel != this->position_next.audio_channel) { + dvdnav_audio_stream_change_event_t stream_change; + + (*event) = DVDNAV_AUDIO_STREAM_CHANGE; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: AUDIO_STREAM_CHANGE\n"); +#endif + (*len) = sizeof(dvdnav_audio_stream_change_event_t); + stream_change.physical = vm_get_audio_active_stream( this->vm ); + memcpy(*buf, &(stream_change), sizeof( dvdnav_audio_stream_change_event_t)); + this->position_current.audio_channel = this->position_next.audio_channel; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: AUDIO_STREAM_CHANGE stream_id=%d returning S_OK\n",stream_change.physical); +#endif + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } + + /* Check the HIGHLIGHT flag */ + if(this->position_current.button != this->position_next.button) { + dvdnav_highlight_event_t hevent; + + (*event) = DVDNAV_HIGHLIGHT; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: HIGHLIGHT\n"); +#endif + (*len) = sizeof(hevent); + hevent.display = 1; + hevent.buttonN = this->position_next.button; + memcpy(*buf, &(hevent), sizeof(hevent)); + this->position_current.button = this->position_next.button; + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } + + /* Check the STILLFRAME flag */ + if(this->position_current.still != 0) { + dvdnav_still_event_t still_event; + + (*event) = DVDNAV_STILL_FRAME; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: STILL_FRAME\n"); +#endif + (*len) = sizeof(dvdnav_still_event_t); + still_event.length = this->position_current.still; + memcpy(*buf, &(still_event), sizeof(dvdnav_still_event_t)); + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } + + /* Have we reached the end of a VOBU? */ + if (this->vobu.blockN >= this->vobu.vobu_length) { + + /* Have we reached the end of a cell? */ if(this->vobu.vobu_next == SRI_END_OF_CELL) { /* End of Cell from NAV DSI info */ #ifdef LOG_DEBUG @@ -743,58 +657,28 @@ this->position_current.still = this->position_next.still; if( this->position_current.still == 0 || this->skip_still ) { - vm_get_next_cell(this->vm); - vm_position_get(this->vm,&this->position_next); - /* FIXME: Need to set vobu_start, vobu_next */ - this->position_current.still = 0; /* still gets activated at end of cell */ - this->skip_still = 0; - this->position_current.cell = this->position_next.cell; - this->position_current.vobu_start = this->position_next.vobu_start; - this->position_current.vobu_next = this->position_next.vobu_next; - this->vobu.vobu_start = this->position_next.vobu_start; - /* vobu_next is use for mid cell resumes */ - this->vobu.vobu_next = this->position_next.vobu_next; - this->vobu.vobu_length = 0; - this->vobu.blockN = this->vobu.vobu_length + 1; - /* Make blockN > vobu_next to do expected_nav */ - /* update the spu palette on PGC changes */ - this->spu_clut_changed = 1; - this->position_current.spu_channel = -1; /* Force an update */ - this->position_current.audio_channel = -1; /* Force an update */; - (*event) = DVDNAV_CELL_CHANGE; - (*len) = 0; - pthread_mutex_unlock(&this->vm_lock); - return S_OK; - } else { - dvdnav_still_event_t still_event; - still_event.length = this->position_current.still; - (*event) = DVDNAV_STILL_FRAME; - (*len) = sizeof(dvdnav_still_event_t); - memcpy(*buf, &(still_event), sizeof(dvdnav_still_event_t)); - pthread_mutex_unlock(&this->vm_lock); - return S_OK; + /* no active cell still -> get us to the next cell */ + vm_get_next_cell(this->vm); + this->position_current.still = 0; /* still gets activated at end of cell */ + this->skip_still = 0; } + /* handle related state changes in next iteration */ + (*event) = DVDNAV_NOP; + (*len) = 0; + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } - /* Only set still after whole VOBU has been output. */ - /* - if(this->position_next.still != 0) { - this->position_current.still = this->position_next.still; - } - */ - - } - /* Perform the jump if necessary (this is always a + /* Perform remapping jump if necessary (this is always a * VOBU boundary). */ + if (this->vm->map) { + this->vobu.vobu_next = remap_block( this->vm->map, + this->vm->state.domain, this->vm->state.TTN_REG, + this->vm->state.pgN, + this->vobu.vobu_start, this->vobu.vobu_next); + } - if (this->vm->map) { - this->vobu.vobu_next = remap_block( this->vm->map, - this->vm->state.domain, this->vm->state.TTN_REG, - this->vm->state.pgN, - this->vobu.vobu_start, this->vobu.vobu_next); - } - - - /* result = DVDReadBlocks(this->file, this->vobu.vobu_start + this->vobu.vobu_next, 1, buf); */ + /* at the start of the next VOBU -> expecting NAV packet */ result = dvdnav_read_cache_block(this->cache, this->vobu.vobu_start + this->vobu.vobu_next, 1, buf); if(result <= 0) { @@ -802,31 +686,29 @@ pthread_mutex_unlock(&this->vm_lock); return S_ERR; } - /* Decode nav into pci and dsi. */ - /* Then get next VOBU info. */ - if(dvdnav_decode_packet(this, *buf, &this->dsi, &this->pci) == 0) { + /* Decode nav into pci and dsi. Then get next VOBU info. */ + if(!dvdnav_decode_packet(this, *buf, &this->dsi, &this->pci)) { printerr("Expected NAV packet but none found."); pthread_mutex_unlock(&this->vm_lock); return S_ERR; } - dvdnav_get_vobu(this, &this->dsi,&this->pci, &this->vobu); - this->vobu.blockN=1; - /* FIXME: We need to update the vm state->blockN with which VOBU we are in. - * This is so RSM resumes to the VOBU level and not just the CELL level. - * This should be implemented with a new Public API call. + /* We need to update the vm state->blockN with which VOBU we are in. + * This is so RSM resumes to the VOBU level and not just the CELL level. */ - /* We cache one past the end of the VOBU, - * in the hope it might catch the next NAV packet as well. - * This reduces the amount of read commands sent to the DVD device. - * A cache miss will only happen for 3 reasons. - * 1) Seeking - * 2) Menu change - * 3) The next VOBU does not immeadiately follow the current one. E.g. Multi Angles, ILVU. + this->vm->state.blockN = this->vobu.vobu_start - this->position_current.cell_start; + + dvdnav_get_vobu(this, &this->dsi, &this->pci, &this->vobu); + this->vobu.blockN = 0; + /* Give the cache a hint about the size of next VOBU. + * This improves pre-caching, because the VOBU will almost certainly be read entirely. */ dvdnav_pre_cache_blocks(this->cache, this->vobu.vobu_start+1, this->vobu.vobu_length+1); /* Successfully got a NAV packet */ (*event) = DVDNAV_NAV_PACKET; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: NAV_PACKET\n"); +#endif (*len) = 2048; pthread_mutex_unlock(&this->vm_lock); return S_OK; @@ -834,25 +716,62 @@ /* If we've got here, it must just be a normal block. */ if(!this->file) { - printerr("Attempting to read without opening file"); + printerr("Attempting to read without opening file."); pthread_mutex_unlock(&this->vm_lock); return S_ERR; } + this->vobu.blockN++; result = dvdnav_read_cache_block(this->cache, this->vobu.vobu_start + this->vobu.blockN, 1, buf); if(result <= 0) { printerr("Error reading from DVD."); pthread_mutex_unlock(&this->vm_lock); return S_ERR; } - this->vobu.blockN++; + (*event) = DVDNAV_BLOCK_OK; (*len) = 2048; - (*event) = DVDNAV_BLOCK_OK; pthread_mutex_unlock(&this->vm_lock); return S_OK; } +dvdnav_status_t dvdnav_get_title_string(dvdnav_t *this, const char **title_str) { + + if(!this || !title_str) { + printerr("Passed a NULL pointer."); + return S_ERR; + } + + (*title_str) = this->vm->dvd_name; + return S_OK; +} + +uint8_t dvdnav_get_video_aspect(dvdnav_t *this) { + uint8_t retval; + + if(!this) + return -1; + + pthread_mutex_lock(&this->vm_lock); + retval = (uint8_t)vm_get_video_aspect(this->vm); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} + +uint8_t dvdnav_get_video_scale_permission(dvdnav_t *this) { + uint8_t retval; + + if(!this) + return -1; + + pthread_mutex_lock(&this->vm_lock); + retval = (uint8_t)vm_get_video_scale_permission(this->vm); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} + uint16_t dvdnav_audio_stream_to_lang(dvdnav_t *this, uint8_t stream) { audio_attr_t attr; @@ -869,19 +788,6 @@ return attr.lang_code; } -int8_t dvdnav_get_audio_logical_stream(dvdnav_t *this, uint8_t audio_num) { - int8_t retval; - - if(!this) - return -1; - - pthread_mutex_lock(&this->vm_lock); - retval = NCLK_dvdnav_get_audio_logical_stream(this, audio_num); - pthread_mutex_unlock(&this->vm_lock); - - return retval; -} - uint16_t dvdnav_spu_stream_to_lang(dvdnav_t *this, uint8_t stream) { subp_attr_t attr; @@ -898,16 +804,54 @@ return attr.lang_code; } +int8_t dvdnav_get_audio_logical_stream(dvdnav_t *this, uint8_t audio_num) { + int8_t retval; + + if(!this) + return -1; + + pthread_mutex_lock(&this->vm_lock); + if (!this->vm->state.pgc) { + pthread_mutex_unlock(&this->vm_lock); + return -1; + } + retval = vm_get_audio_stream(this->vm, audio_num); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} + int8_t dvdnav_get_spu_logical_stream(dvdnav_t *this, uint8_t subp_num) { int8_t retval; if(!this) return -1; + pthread_mutex_lock(&this->vm_lock); + if (!this->vm->state.pgc) { + pthread_mutex_unlock(&this->vm_lock); + return -1; + } + retval = vm_get_subp_stream(this->vm, subp_num, 0); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} + +int8_t dvdnav_get_active_audio_stream(dvdnav_t *this) { + int8_t retval; + + if(!this) + return -1; + pthread_mutex_lock(&this->vm_lock); - retval = NCLK_dvdnav_get_spu_logical_stream(this, subp_num); + if (!this->vm->state.pgc) { + pthread_mutex_unlock(&this->vm_lock); + return -1; + } + retval = vm_get_audio_active_stream(this->vm); pthread_mutex_unlock(&this->vm_lock); - + return retval; } @@ -918,98 +862,110 @@ return -1; pthread_mutex_lock(&this->vm_lock); - retval = NLCK_dvdnav_get_active_spu_stream(this); + if (!this->vm->state.pgc) { + pthread_mutex_unlock(&this->vm_lock); + return -1; + } + retval = vm_get_subp_active_stream(this->vm, 0); pthread_mutex_unlock(&this->vm_lock); return retval; } +static int8_t dvdnav_is_domain(dvdnav_t *this, domain_t domain) { + int8_t retval; + + if (!this || !this->started) + return -1; + + pthread_mutex_lock(&this->vm_lock); + retval = (this->vm->state.domain == domain); + pthread_mutex_unlock(&this->vm_lock); + + return retval; +} + /* First Play domain. (Menu) */ int8_t dvdnav_is_domain_fp(dvdnav_t *this) { - return _dvdnav_is_domain(this, FP_DOMAIN); + return dvdnav_is_domain(this, FP_DOMAIN); } /* Video management Menu domain. (Menu) */ int8_t dvdnav_is_domain_vmgm(dvdnav_t *this) { - return _dvdnav_is_domain(this, VMGM_DOMAIN); + return dvdnav_is_domain(this, VMGM_DOMAIN); } /* Video Title Menu domain (Menu) */ int8_t dvdnav_is_domain_vtsm(dvdnav_t *this) { - return _dvdnav_is_domain(this, VTSM_DOMAIN); + return dvdnav_is_domain(this, VTSM_DOMAIN); } /* Video Title domain (playing movie). */ int8_t dvdnav_is_domain_vts(dvdnav_t *this) { - return _dvdnav_is_domain(this, VTS_DOMAIN); + return dvdnav_is_domain(this, VTS_DOMAIN); } -/* Generally delegate angle information handling to - * VM */ +/* Generally delegate angle information handling to VM */ dvdnav_status_t dvdnav_angle_change(dvdnav_t *this, int angle) { int num, current; if(!this) { + printerr("Passed a NULL pointer."); return S_ERR; } - if(dvdnav_get_angle_info(this, ¤t, &num) != S_OK) { - printerr("Error getting angle info"); - return S_ERR; - } - + pthread_mutex_lock(&this->vm_lock); + vm_get_angle_info(this->vm, ¤t, &num); /* Set angle SPRM if valid */ if((angle > 0) && (angle <= num)) { this->vm->state.AGL_REG = angle; } else { - printerr("Passed an invalid angle number"); + printerr("Passed an invalid angle number."); + pthread_mutex_unlock(&this->vm_lock); return S_ERR; } - - return S_OK; -} -/* FIXME: change order of current_angle, number_of_angles */ -dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *this, int* current_angle, - int *number_of_angles) { - if(!this || !this->vm) { - return S_ERR; - } - - if(!current_angle || !number_of_angles) { - printerr("Passed a NULL pointer"); - return S_ERR; - } - - vm_get_angle_info(this->vm, number_of_angles, current_angle); + pthread_mutex_unlock(&this->vm_lock); return S_OK; } -dvdnav_status_t dvdnav_get_cell_info(dvdnav_t *this, int* current_angle, - int *number_of_angles) { - if(!this || !this->vm) { +dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *this, int *current_angle, + int *number_of_angles) { + if(!this || !current_angle || !number_of_angles) { + printerr("Passed a NULL pointer."); return S_ERR; } - *current_angle=this->position_next.cell; + + pthread_mutex_lock(&this->vm_lock); + vm_get_angle_info(this->vm, current_angle, number_of_angles); + pthread_mutex_unlock(&this->vm_lock); + return S_OK; -} +} pci_t* dvdnav_get_current_nav_pci(dvdnav_t *this) { - if(!this || !this->vm) return 0; + if(!this) return 0; return &this->pci; } dsi_t* dvdnav_get_current_nav_dsi(dvdnav_t *this) { - if(!this || !this->vm) return 0; + if(!this) return 0; return &this->dsi; } uint32_t dvdnav_get_next_still_flag(dvdnav_t *this) { - if(!this || !this->vm) { - return S_ERR; - } + if(!this) return -1; return this->position_next.still; } /* * $Log$ + * Revision 1.40 2003/02/20 15:32:15 mroi + * big libdvdnav cleanup, quoting the ChangeLog: + * * some bugfixes + * * code cleanup + * * build process polishing + * * more sensible event order in get_next_block to ensure useful event delivery + * * VOBU level resume + * * fixed: seeking in a multiangle feature briefly showed the wrong angle + * * Revision 1.39 2002/10/23 11:38:09 mroi * port Stephen's comment fixing to avoid problems when syncing xine-lib's copy of * libdvdnav diff -r ec2df154be56 -r b6834e6359cf dvdnav.h --- a/dvdnav.h Mon Jan 13 13:33:45 2003 +0000 +++ b/dvdnav.h Thu Feb 20 15:32:21 2003 +0000 @@ -56,9 +56,6 @@ #define DVDNAV_STATUS_ERR 0 #define DVDNAV_STATUS_OK 1 -/** - * NOTE: */ - /** * \defgroup init Initialisation & housekeeping functions * These functions allow you to open a DVD device and associate it @@ -84,7 +81,7 @@ * \param dest Pointer to a dvdnav_t pointer to fill in. * \param path Any libdvdread acceptable path */ -dvdnav_status_t dvdnav_open(dvdnav_t** dest, char *path); +dvdnav_status_t dvdnav_open(dvdnav_t** dest, const char *path); /** * Closes a dvdnav_t previously opened with dvdnav_open(), freeing any @@ -109,7 +106,7 @@ * \param self Pointer to dvdnav_t associated with this operation. * \param path Pointer to char* to fill in. */ -dvdnav_status_t dvdnav_path(dvdnav_t *self, char** path); +dvdnav_status_t dvdnav_path(dvdnav_t *self, const char** path); /** * Returns a human-readable string describing the last error. @@ -117,7 +114,7 @@ * \param self Pointer to dvdnav_t associated with this operation. * \returns A pointer to said string. */ -char* dvdnav_err_to_string(dvdnav_t *self); +const char* dvdnav_err_to_string(dvdnav_t *self); /** \@} */ @@ -156,8 +153,7 @@ /** * Specify whether read-ahead caching should be used. You may not want this if your - * decoding engine does its own buffering or if you don't like the fact that this is - * implemented in a multithreaded manner. + * decoding engine does its own buffering. * * \param self Pointer to dvdnav_t associated with this operation. * \param use_readahead 0 - no, 1 - yes @@ -214,7 +210,7 @@ * I such a case (cache hit) this function will return a different pointer than * the one handed in, pointing directly into the relevant block in the cache. * Those pointer must _never_ be freed but instead returned to the library via - + dvdnav_free_cache_block. + * dvdnav_free_cache_block(). * * \param self Pointer to dvdnav_t associated with this operation. * \param buf Buffer (at least 2048 octets) to fill with next block/event structure. @@ -226,7 +222,7 @@ int *event, int *len); /** - * All buffers which came from the internal cache (when dvdnav_get_next_cache_block + * All buffers which came from the internal cache (when dvdnav_get_next_cache_block() * returned a buffer different from the one handed in) have to be freed with this * function. Although handing in other buffers not from the cache doesn't cause any harm. * @@ -254,6 +250,17 @@ uint8_t dvdnav_get_video_scale_permission(dvdnav_t *self); /** + * Return a string describing the title. This is an ID string encoded on the + * disc byt the author. In many cases this is a descriptive string such as + * `THE_MATRIX' but sometimes is sigularly uninformative such as + * `PDVD-011421'. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param title_str Pointer to C-style string to receive a string describing the title. + */ +dvdnav_status_t dvdnav_get_title_string(dvdnav_t *self, const char **title_str); + +/** * @} */ @@ -290,6 +297,13 @@ dvdnav_status_t dvdnav_still_skip(dvdnav_t *self); /** + * Returns the still time status from the next cell + * + * \param self Pointer to dvdnav_t associated with this operation. + */ +uint32_t dvdnav_get_next_still_flag(dvdnav_t *self); + +/** * Plays a specified title of the DVD. * * \param self Pointer to dvdnav_t associated with this operation. @@ -298,8 +312,7 @@ dvdnav_status_t dvdnav_title_play(dvdnav_t *self, int title); /** - * Plays the specifiec title, starting from the specified - * part (chapter). + * Plays the specifiec title, starting from the specified part. * * \param self Pointer to dvdnav_t associated with this operation. * \param title 1..99 -- Title number to play. @@ -320,7 +333,7 @@ * \param parts_to_play 1..999 -- Number of parts to play. */ dvdnav_status_t dvdnav_part_play_auto_stop(dvdnav_t *self, int title, - int part, int parts_to_play); + int part, int parts_to_play); /** * Play the specified title starting from the specified time @@ -333,7 +346,7 @@ * \param time Timecode to start from (hours, minutes, seconds + frames). */ dvdnav_status_t dvdnav_time_play(dvdnav_t *self, int title, - unsigned long int time); + unsigned long int time); /** * Stops playing the current title (causes a STOP action in @@ -354,6 +367,27 @@ dvdnav_status_t dvdnav_go_up(dvdnav_t *self); /** + * Stop playing the current title and jump to the specified menu. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param menu Which menu to call (see DVDMenuID_t). + * + * \sa DVDMenuID_t (from libdvdread) + */ +dvdnav_status_t dvdnav_menu_call(dvdnav_t *self, DVDMenuID_t menu); + +/** + * Return the title number and part currently being played or + * -1 if in a menu. + * + * \param self Pointer to dvdnav_t associated with this operation. + * \param title Pointer to into which will receive the current title number. + * \param part Pointer to into which will receive the current part number. + */ +dvdnav_status_t dvdnav_current_title_info(dvdnav_t *self, int *title, + int *part); + +/** * @} */ @@ -374,7 +408,7 @@ * \param time Timecode to start from. */ dvdnav_status_t dvdnav_time_search(dvdnav_t *self, - unsigned long int time); + unsigned long int time); /** * Stop playing the current title and start playback of the title @@ -385,7 +419,7 @@ * \param origin Start from here, start or end. */ dvdnav_status_t dvdnav_sector_search(dvdnav_t *self, - unsigned long int offset, int origin); + unsigned long int offset, int origin); /** * Stop playing the current title and start playback of the title @@ -421,38 +455,6 @@ dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *self); /** - * Stop playing the current title and jump to the specified menu. - * - * \param self Pointer to dvdnav_t associated with this operation. - * \param menu Which menu to call (see DVDMenuID_t). - * - * \sa DVDMenuID_t (from libdvdread) - */ -dvdnav_status_t dvdnav_menu_call(dvdnav_t *self, DVDMenuID_t menu); - -/** - * Return the title number and chapter currently being played or - * -1 if in a menu. - * - * \param self Pointer to dvdnav_t associated with this operation. - * \param title Pointer to into which will receive the current title number. - * \param part Pointer to into which will receive the current part number. - */ -dvdnav_status_t dvdnav_current_title_info(dvdnav_t *self, int *title, - int *part); - -/** - * Return a string describing the title. This is an ID string encoded on the - * disc byt the author. In many cases this is a descriptive string such as - * `THE_MATRIX' but sometimes is sigularly uninformative such as - * `PDVD-011421'. - * - * \param self Pointer to dvdnav_t associated with this operation. - * \param title_str Pointer to C-style string to receive a string describing the title. - */ -dvdnav_status_t dvdnav_get_title_string(dvdnav_t *self, char **title_str); - -/** * Return the current position (in blocks) within the current * part and the length (in blocks) of said part. * @@ -460,7 +462,7 @@ * \param pos Pointer to unsigned int to get the current position. * \param len Pointer to unsinged int to hold the length of the current part. */ -dvdnav_status_t dvdnav_get_position(dvdnav_t *self, unsigned int* pos, +dvdnav_status_t dvdnav_get_position(dvdnav_t *self, unsigned int *pos, unsigned int *len); /** @@ -472,7 +474,7 @@ * \param len Pointer to unsinged int to hold the length of the current title. */ dvdnav_status_t dvdnav_get_position_in_title(dvdnav_t *self, - unsigned int* pos, + unsigned int *pos, unsigned int *len); /** @@ -482,6 +484,14 @@ /** * \defgroup highlight Highlights * + * Most functions related to highlights take a NAV PCI packet as a parameter. + * While you can get the such a packet from libdvdnav, for players with internal + * FIFOs, this will result in errors, because due to the FIFO length, libdvdnav will + * be ahead in the stream compared to what the user is seeing on screen. + * Therefore, player applications who have a NAV packet available, which is + * better in sync with the actual playback should always pass this one to these + * functions. + * * @{ */ @@ -527,7 +537,7 @@ * \sa dvdnav_highlight_area_t */ dvdnav_status_t dvdnav_get_highlight_area(pci_t* nav_pci , int32_t button, int32_t mode, - dvdnav_highlight_area_t* highlight); + dvdnav_highlight_area_t* highlight); /** * Move button highlight around as suggested by function name (e.g. with arrow keys). @@ -535,18 +545,22 @@ * \param self Pointer to dvdnav_t associated with this operation. */ dvdnav_status_t dvdnav_upper_button_select(dvdnav_t *self, pci_t *pci); + /** * Move button highlight around as suggested by function name (e.g. with arrow keys). * * \param self Pointer to dvdnav_t associated with this operation. */ + dvdnav_status_t dvdnav_lower_button_select(dvdnav_t *self, pci_t *pci); + /** * Move button highlight around as suggested by function name (e.g. with arrow keys). * * \param self Pointer to dvdnav_t associated with this operation. */ dvdnav_status_t dvdnav_right_button_select(dvdnav_t *self, pci_t *pci); + /** * Move button highlight around as suggested by function name (e.g. with arrow keys). * @@ -664,7 +678,7 @@ * \param audio 0..7 -- Stream number. */ dvdnav_status_t dvdnav_physical_audio_stream_change(dvdnav_t *self, - int audio); + int audio); /** * Set a specific logical audio stream. @@ -673,7 +687,7 @@ * \param audio 0..7 -- Stream number. */ dvdnav_status_t dvdnav_logical_audio_stream_change(dvdnav_t *self, - int audio); + int audio); /** * Set the int pointed to to the current PHYSICAL audio @@ -735,7 +749,7 @@ * \param display Pointer which will receive value. */ dvdnav_status_t dvdnav_get_logical_spu_stream(dvdnav_t *self, - int* stream, int* disply); + int* stream, int* disply); /** * Converts a *logical* audio stream id into country code @@ -772,6 +786,13 @@ int8_t dvdnav_get_spu_logical_stream(dvdnav_t *self, uint8_t subp_num); /** + * Get active audio stream. + * + * \param self Pointer to dvdnav_t associated with this operation. + */ +int8_t dvdnav_get_active_audio_stream(dvdnav_t *self); + +/** * Get active spu stream. * * \param self Pointer to dvdnav_t associated with this operation. @@ -815,20 +836,7 @@ * \param number_of_angles Pointer to int which will get the number of angles. */ dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *self, int* current_angle, - int *number_of_angles); - -/** - * FIXME: WTF does this do? -- High qulaity documentation huh? - */ -dvdnav_status_t dvdnav_get_cell_info(dvdnav_t *self, int* current_angle, - int *number_of_angles); - -/** - * Returns the still time status from the next cell - * - * \param self Pointer to dvdnav_t associated with this operation. - */ -uint32_t dvdnav_get_next_still_flag(dvdnav_t *self); + int *number_of_angles); /** * @} @@ -849,18 +857,21 @@ * \returns -1 on failure, 1 if condition is true, 0 if condition is false */ int8_t dvdnav_is_domain_fp(dvdnav_t *self); + /** * Are we in the Video management Menu domain. (Menu) * \param self Pointer to dvdnav_t associated with this operation. * \returns -1 on failure, 1 if condition is true, 0 if condition is false */ int8_t dvdnav_is_domain_vmgm(dvdnav_t *self); + /** * Are we in the Video Title Menu domain (Menu) * \param self Pointer to dvdnav_t associated with this operation. * \returns -1 on failure, 1 if condition is true, 0 if condition is false */ int8_t dvdnav_is_domain_vtsm(dvdnav_t *self); + /** * Are we in the Video Title domain (playing movie)? * \param self Pointer to dvdnav_t associated with this operation. diff -r ec2df154be56 -r b6834e6359cf dvdnav_events.h --- a/dvdnav_events.h Mon Jan 13 13:33:45 2003 +0000 +++ b/dvdnav_events.h Thu Feb 20 15:32:21 2003 +0000 @@ -35,21 +35,19 @@ /*** EVENTS ***/ -#define DVDNAV_BLOCK_OK 0 /*!< The next black was returned */ -#define DVDNAV_NOP 1 /*!< No action should be taken */ -#define DVDNAV_STILL_FRAME 2 /*!< The preceeding block was the last in a - still frame. */ -#define DVDNAV_SPU_STREAM_CHANGE 3 /*!< The SPU stream was changed */ -#define DVDNAV_AUDIO_STREAM_CHANGE 4 /*!< The Audio stream was changed */ -#define DVDNAV_VTS_CHANGE 5 /*!< We have changed VTS */ -#define DVDNAV_CELL_CHANGE 6 /*!< We have jumped to a new cell */ -#define DVDNAV_NAV_PACKET 7 /*!< The packet just passed was the NAV packet */ -#define DVDNAV_STOP 8 /*!< The last block was final, no more are coming */ -#define DVDNAV_HIGHLIGHT 9 /*!< Change highlight region */ -#define DVDNAV_SPU_CLUT_CHANGE 10 /*!< SPU CLUT */ -#define DVDNAV_SEEK_DONE 11 /*!< Seek done, subtitles should be reset */ -#define DVDNAV_HOP_CHANNEL 12 /*!< Sent when non-seemless stream change has happed - E.g. Menu button pressed causing change in menu */ +#define DVDNAV_BLOCK_OK 0 /*!< The next block was returned */ +#define DVDNAV_NOP 1 /*!< No action should be taken */ +#define DVDNAV_STILL_FRAME 2 /*!< The preceeding block was the last in a still frame */ +#define DVDNAV_SPU_STREAM_CHANGE 3 /*!< The SPU stream was changed */ +#define DVDNAV_AUDIO_STREAM_CHANGE 4 /*!< The Audio stream was changed */ +#define DVDNAV_VTS_CHANGE 5 /*!< We have changed VTS */ +#define DVDNAV_CELL_CHANGE 6 /*!< We have jumped to a new cell */ +#define DVDNAV_NAV_PACKET 7 /*!< The packet just passed was a NAV packet */ +#define DVDNAV_STOP 8 /*!< The last block was final, no more are coming */ +#define DVDNAV_HIGHLIGHT 9 /*!< Change highlight region */ +#define DVDNAV_SPU_CLUT_CHANGE 10 /*!< SPU CLUT changed */ +#define DVDNAV_HOP_CHANNEL 12 /*!< Sent when non-seemless stream change has happed */ + /*** EVENT TYPES ***/ @@ -60,17 +58,17 @@ int length; /*!< The length (in seconds) the still frame should be displayed for, or 0xff if - indefinate. */ + indefinite. */ } dvdnav_still_event_t; /** * Structure providing information on DVDNAV_SPU_STREAM_CHANGE events. */ typedef struct { - int physical_wide; /*!< The physical (MPEG) stream number for widescreen display. */ + int physical_wide; /*!< The physical (MPEG) stream number for widescreen display. */ int physical_letterbox; /*!< The physical (MPEG) stream number for letterboxed display. */ - int physical_pan_scan; /*!< The physical (MPEG) stream number for pan&scan display. */ - int logical; /*!< The logical (DVD) stream number. */ + int physical_pan_scan; /*!< The physical (MPEG) stream number for pan&scan display. */ + int logical; /*!< The logical (DVD) stream number. */ } dvdnav_spu_stream_change_event_t; /** @@ -85,18 +83,20 @@ * Structure providing information on DVDNAV_VTS_CHANGE events. */ typedef struct { - int old_vtsN; /*!< The old VTS number */ + int old_vtsN; /*!< The old VTS number */ dvd_read_domain_t old_domain; /*!< The old domain */ - int new_vtsN; /*!< The new VTS number */ + int new_vtsN; /*!< The new VTS number */ dvd_read_domain_t new_domain; /*!< The new domain */ } dvdnav_vts_change_event_t; +/* FIXME: These are unused. */ +#if 0 /** * Structure providing information on DVDNAV_CELL_CHANGE events. */ typedef struct { cell_playback_t *old_cell; /*!< The old cell (or NULL if this is - the first cell) */ + the first cell) */ cell_playback_t *new_cell; /*!< The cell_playback_t for the new cell */ } dvdnav_cell_change_event_t; @@ -107,15 +107,18 @@ pci_t *pci; dsi_t *dsi; } dvdnav_nav_packet_event_t; +#endif /** * Structure providing information on DVDNAV_HIGHLIGHT events. + * The event only fills in display and buttonN. + * The rest can be get with dvdnav_get_highlight_area(). */ typedef struct { - int display; /*!< 0 - hide, 1 - show, entries below only guaranteed useful - if this is '1' */ - uint32_t palette; /*!< The CLUT entries for the highlight palette - (4-bits per entry -> 4 entries) */ + int display; /*!< 0 - hide, 1 - show, entries below only guaranteed useful + if this is '1' */ + uint32_t palette; /*!< The CLUT entries for the highlight palette + (4-bits per entry -> 4 entries) */ uint16_t sx,sy,ex,ey; /*!< The start/end x,y positions */ uint32_t pts; /*!< Highlight PTS to match with SPU */ uint32_t buttonN; /*!< Button number for the SPU decoder. */ diff -r ec2df154be56 -r b6834e6359cf dvdnav_internal.h.in --- a/dvdnav_internal.h.in Mon Jan 13 13:33:45 2003 +0000 +++ b/dvdnav_internal.h.in Thu Feb 20 15:32:21 2003 +0000 @@ -45,6 +45,12 @@ #include +/* Uncomment for VM command tracing */ +/* #define TRACE */ + +/* where should libdvdnav write its messages (stdout/stderr) */ +#define MSG_OUT stdout + /* Maximum length of an error string */ #define MAX_ERR_LEN 255 @@ -52,16 +58,13 @@ #ifdef PATH_MAX #define MAX_PATH_LEN PATH_MAX #else -#define MAX_PATH_LEN 255 /* Arbitary */ +#define MAX_PATH_LEN 255 /* Arbitrary */ #endif #ifndef DVD_VIDEO_LB_LEN #define DVD_VIDEO_LB_LEN 2048 #endif -/* where should libdvdnav write its messages (stdout/stderr) */ -#define MSG_OUT stdout - typedef struct read_cache_s read_cache_t; /* @@ -112,20 +115,20 @@ #endif typedef struct dvdnav_vobu_s { - int32_t vobu_start; /* Logical Absolute. MAX needed is 0x300000 */ - int32_t vobu_length; /* Relative offset */ - int32_t blockN; /* Relative offset */ - int32_t vobu_next; /* Relative offset */ + int32_t vobu_start; /* Logical Absolute. MAX needed is 0x300000 */ + int32_t vobu_length; + int32_t blockN; /* Relative offset */ + int32_t vobu_next; /* Relative offset */ } dvdnav_vobu_t; -/* The main DVDNAV type */ +/** The main DVDNAV type **/ struct dvdnav_s { /* General data */ - char path[MAX_PATH_LEN]; /* Path to DVD device/dir */ + char path[MAX_PATH_LEN]; /* Path to DVD device/dir */ dvd_file_t *file; /* Currently opened file */ - int open_vtsN; /* The domain and number of the... */ - int open_domain; /* ..currently opened VOB */ + int open_vtsN; /* The domain and number of the... */ + int open_domain; /* ..currently opened VOB */ /* Position data */ vm_position_t position_next; @@ -137,20 +140,15 @@ dsi_t dsi; /* Flags */ - int skip_still; /* Set when skipping a still */ - int stop; /* Are we stopped? (note not paused, actually stopped) */ - int spu_clut_changed; /* The SPU CLUT changed */ - int started; /* vm_start has been called? */ - int use_read_ahead; /* 1 - use read-ahead cache, 0 - don't */ + int skip_still; /* Set when skipping a still */ + int spu_clut_changed; /* The SPU CLUT changed */ + int started; /* vm_start has been called? */ + int use_read_ahead; /* 1 - use read-ahead cache, 0 - don't */ + /* VM */ - vm_t* vm; + vm_t *vm; pthread_mutex_t vm_lock; - /* Highlight */ - int hli_state; /* State of highlight: 0 - disabled, - 1 - selected, - 2 - activated */ - /* Read-ahead cache */ read_cache_t *cache; @@ -166,8 +164,8 @@ #define printerrf(...) snprintf(this->err_str, MAX_ERR_LEN, __VA_ARGS__); #endif #define printerr(str) strncpy(this->err_str, str, MAX_ERR_LEN); + /* Save my typing */ - #define S_ERR DVDNAV_STATUS_ERR #define S_OK DVDNAV_STATUS_OK diff -r ec2df154be56 -r b6834e6359cf highlight.c --- a/highlight.c Mon Jan 13 13:33:45 2003 +0000 +++ b/highlight.c Thu Feb 20 15:32:21 2003 +0000 @@ -25,23 +25,25 @@ #include "config.h" #endif - -#define BUTTON_TESTING - #include -#include #include "dvdnav_internal.h" #include "vm.h" #include +/* +#define BUTTON_TESTING +*/ + #ifdef BUTTON_TESTING + #include #include "vmcmd.h" static void print_time(dvd_time_t *dtime) { const char *rate; + assert((dtime->hour>>4) < 0xa && (dtime->hour&0xf) < 0xa); assert((dtime->minute>>4) < 0x7 && (dtime->minute&0xf) < 0xa); assert((dtime->second>>4) < 0x7 && (dtime->second&0xf) < 0xa); @@ -69,18 +71,18 @@ static void nav_print_PCI_GI(pci_gi_t *pci_gi) { int i; - fprintf(MSG_OUT,"pci_gi:\n"); - fprintf(MSG_OUT,"nv_pck_lbn 0x%08x\n", pci_gi->nv_pck_lbn); - fprintf(MSG_OUT,"vobu_cat 0x%04x\n", pci_gi->vobu_cat); - fprintf(MSG_OUT,"vobu_uop_ctl 0x%08x\n", *(uint32_t*)&pci_gi->vobu_uop_ctl); - fprintf(MSG_OUT,"vobu_s_ptm 0x%08x\n", pci_gi->vobu_s_ptm); - fprintf(MSG_OUT,"vobu_e_ptm 0x%08x\n", pci_gi->vobu_e_ptm); - fprintf(MSG_OUT,"vobu_se_e_ptm 0x%08x\n", pci_gi->vobu_se_e_ptm); - fprintf(MSG_OUT,"e_eltm "); + fprintf(MSG_OUT,"libdvdnav: pci_gi:\n"); + fprintf(MSG_OUT,"libdvdnav: nv_pck_lbn 0x%08x\n", pci_gi->nv_pck_lbn); + fprintf(MSG_OUT,"libdvdnav: vobu_cat 0x%04x\n", pci_gi->vobu_cat); + fprintf(MSG_OUT,"libdvdnav: vobu_uop_ctl 0x%08x\n", *(uint32_t*)&pci_gi->vobu_uop_ctl); + fprintf(MSG_OUT,"libdvdnav: vobu_s_ptm 0x%08x\n", pci_gi->vobu_s_ptm); + fprintf(MSG_OUT,"libdvdnav: vobu_e_ptm 0x%08x\n", pci_gi->vobu_e_ptm); + fprintf(MSG_OUT,"libdvdnav: vobu_se_e_ptm 0x%08x\n", pci_gi->vobu_se_e_ptm); + fprintf(MSG_OUT,"libdvdnav: e_eltm "); print_time(&pci_gi->e_eltm); fprintf(MSG_OUT,"\n"); - fprintf(MSG_OUT,"vobu_isrc \""); + fprintf(MSG_OUT,"libdvdnav: vobu_isrc \""); for(i = 0; i < 32; i++) { char c = pci_gi->vobu_isrc[i]; if((c >= ' ') && (c <= '~')) @@ -99,10 +101,10 @@ if(j == 0) return; - fprintf(MSG_OUT,"nsml_agli:\n"); + fprintf(MSG_OUT,"libdvdnav: nsml_agli:\n"); for(i = 0; i < 9; i++) if(nsml_agli->nsml_agl_dsta[i]) - fprintf(MSG_OUT,"nsml_agl_c%d_dsta 0x%08x\n", i + 1, + fprintf(MSG_OUT,"libdvdnav: nsml_agl_c%d_dsta 0x%08x\n", i + 1, nsml_agli->nsml_agl_dsta[i]); } @@ -111,24 +113,24 @@ if((hl_gi->hli_ss & 0x03) == 0) return; - fprintf(MSG_OUT,"hl_gi:\n"); - fprintf(MSG_OUT,"hli_ss 0x%01x\n", hl_gi->hli_ss & 0x03); - fprintf(MSG_OUT,"hli_s_ptm 0x%08x\n", hl_gi->hli_s_ptm); - fprintf(MSG_OUT,"hli_e_ptm 0x%08x\n", hl_gi->hli_e_ptm); - fprintf(MSG_OUT,"btn_se_e_ptm 0x%08x\n", hl_gi->btn_se_e_ptm); + fprintf(MSG_OUT,"libdvdnav: hl_gi:\n"); + fprintf(MSG_OUT,"libdvdnav: hli_ss 0x%01x\n", hl_gi->hli_ss & 0x03); + fprintf(MSG_OUT,"libdvdnav: hli_s_ptm 0x%08x\n", hl_gi->hli_s_ptm); + fprintf(MSG_OUT,"libdvdnav: hli_e_ptm 0x%08x\n", hl_gi->hli_e_ptm); + fprintf(MSG_OUT,"libdvdnav: btn_se_e_ptm 0x%08x\n", hl_gi->btn_se_e_ptm); *btngr_ns = hl_gi->btngr_ns; - fprintf(MSG_OUT,"btngr_ns %d\n", hl_gi->btngr_ns); - fprintf(MSG_OUT,"btngr%d_dsp_ty 0x%02x\n", 1, hl_gi->btngr1_dsp_ty); - fprintf(MSG_OUT,"btngr%d_dsp_ty 0x%02x\n", 2, hl_gi->btngr2_dsp_ty); - fprintf(MSG_OUT,"btngr%d_dsp_ty 0x%02x\n", 3, hl_gi->btngr3_dsp_ty); + fprintf(MSG_OUT,"libdvdnav: btngr_ns %d\n", hl_gi->btngr_ns); + fprintf(MSG_OUT,"libdvdnav: btngr%d_dsp_ty 0x%02x\n", 1, hl_gi->btngr1_dsp_ty); + fprintf(MSG_OUT,"libdvdnav: btngr%d_dsp_ty 0x%02x\n", 2, hl_gi->btngr2_dsp_ty); + fprintf(MSG_OUT,"libdvdnav: btngr%d_dsp_ty 0x%02x\n", 3, hl_gi->btngr3_dsp_ty); - fprintf(MSG_OUT,"btn_ofn %d\n", hl_gi->btn_ofn); + fprintf(MSG_OUT,"libdvdnav: btn_ofn %d\n", hl_gi->btn_ofn); *btn_ns = hl_gi->btn_ns; - fprintf(MSG_OUT,"btn_ns %d\n", hl_gi->btn_ns); - fprintf(MSG_OUT,"nsl_btn_ns %d\n", hl_gi->nsl_btn_ns); - fprintf(MSG_OUT,"fosl_btnn %d\n", hl_gi->fosl_btnn); - fprintf(MSG_OUT,"foac_btnn %d\n", hl_gi->foac_btnn); + fprintf(MSG_OUT,"libdvdnav: btn_ns %d\n", hl_gi->btn_ns); + fprintf(MSG_OUT,"libdvdnav: nsl_btn_ns %d\n", hl_gi->nsl_btn_ns); + fprintf(MSG_OUT,"libdvdnav: fosl_btnn %d\n", hl_gi->fosl_btnn); + fprintf(MSG_OUT,"libdvdnav: foac_btnn %d\n", hl_gi->foac_btnn); } static void nav_print_BTN_COLIT(btn_colit_t *btn_colit) { @@ -140,10 +142,10 @@ if(j == 0) return; - fprintf(MSG_OUT,"btn_colit:\n"); + fprintf(MSG_OUT,"libdvdnav: btn_colit:\n"); for(i = 0; i < 3; i++) for(j = 0; j < 2; j++) - fprintf(MSG_OUT,"btn_cqoli %d %s_coli: %08x\n", + fprintf(MSG_OUT,"libdvdnav: btn_cqoli %d %s_coli: %08x\n", i, (j == 0) ? "sl" : "ac", btn_colit->btn_coli[i][j]); } @@ -151,9 +153,9 @@ static void nav_print_BTNIT(btni_t *btni_table, int btngr_ns, int btn_ns) { int i, j, k; - fprintf(MSG_OUT,"btnit:\n"); - fprintf(MSG_OUT,"btngr_ns: %i\n", btngr_ns); - fprintf(MSG_OUT,"btn_ns: %i\n", btn_ns); + fprintf(MSG_OUT,"libdvdnav: btnit:\n"); + fprintf(MSG_OUT,"libdvdnav: btngr_ns: %i\n", btngr_ns); + fprintf(MSG_OUT,"libdvdnav: btn_ns: %i\n", btn_ns); if(btngr_ns == 0) return; @@ -163,22 +165,22 @@ if(j < btn_ns) { btni_t *btni = &btni_table[(36 / btngr_ns) * i + j]; - fprintf(MSG_OUT,"group %d btni %d: ", i+1, j+1); + fprintf(MSG_OUT,"libdvdnav: group %d btni %d: ", i+1, j+1); fprintf(MSG_OUT,"btn_coln %d, auto_action_mode %d\n", btni->btn_coln, btni->auto_action_mode); - fprintf(MSG_OUT,"coords (%d, %d) .. (%d, %d)\n", + fprintf(MSG_OUT,"libdvdnav: coords (%d, %d) .. (%d, %d)\n", btni->x_start, btni->y_start, btni->x_end, btni->y_end); - fprintf(MSG_OUT,"up %d, ", btni->up); + fprintf(MSG_OUT,"libdvdnav: up %d, ", btni->up); fprintf(MSG_OUT,"down %d, ", btni->down); fprintf(MSG_OUT,"left %d, ", btni->left); fprintf(MSG_OUT,"right %d\n", btni->right); for(k = 0; k < 8; k++) { - fprintf(MSG_OUT, "%02x ", btni->cmd.bytes[k]); + fprintf(MSG_OUT, "libdvdnav: %02x ", btni->cmd.bytes[k]); } fprintf(MSG_OUT, "| "); vmPrint_mnemonic(&btni->cmd); - fprintf(MSG_OUT, "\n\n"); + fprintf(MSG_OUT, "\n"); } } } @@ -187,29 +189,30 @@ static void nav_print_HLI(hli_t *hli) { int btngr_ns = 0, btn_ns = 0; - fprintf(MSG_OUT,"hli:\n"); + fprintf(MSG_OUT,"libdvdnav: hli:\n"); nav_print_HL_GI(&hli->hl_gi, & btngr_ns, & btn_ns); nav_print_BTN_COLIT(&hli->btn_colit); nav_print_BTNIT(hli->btnit, btngr_ns, btn_ns); } void nav_print_PCI(pci_t *pci) { - fprintf(MSG_OUT,"pci packet:\n"); + fprintf(MSG_OUT,"libdvdnav: pci packet:\n"); nav_print_PCI_GI(&pci->pci_gi); nav_print_NSML_AGLI(&pci->nsml_agli); nav_print_HLI(&pci->hli); } +#endif -#endif /* Highlighting API calls */ - - -dvdnav_status_t dvdnav_get_current_highlight(dvdnav_t *this, int* button) { - if(!this) - return S_ERR; +dvdnav_status_t dvdnav_get_current_highlight(dvdnav_t *this, int *button) { + + if(!this) { + printerr("Passed a NULL pointer."); + return S_ERR; + } /* Simply return the appropriate value based on the SPRM */ (*button) = (this->vm->state.HL_BTNN_REG) >> 10; @@ -217,7 +220,7 @@ return S_OK; } -btni_t *__get_current_button(dvdnav_t *this, pci_t *pci) { +static btni_t *get_current_button(dvdnav_t *this, pci_t *pci) { int button = 0; if(dvdnav_get_current_highlight(this, &button) != S_OK) { @@ -231,36 +234,24 @@ return &(pci->hli.btnit[button-1]); } -dvdnav_status_t dvdnav_button_auto_action(dvdnav_t *this, pci_t *pci) { - btni_t *button_ptr; - - if(!this) - return S_ERR; - - if((button_ptr = __get_current_button(this, pci)) == NULL) { - return S_ERR; - } - if (button_ptr->auto_action_mode == 1) { - return S_OK; - } - return S_ERR; +static dvdnav_status_t button_auto_action(dvdnav_t *this, pci_t *pci) { + if (get_current_button(this, pci)->auto_action_mode) + return dvdnav_button_activate(this, pci); } - dvdnav_status_t dvdnav_upper_button_select(dvdnav_t *this, pci_t *pci) { btni_t *button_ptr; - if(!this) - return S_ERR; - - if((button_ptr = __get_current_button(this, pci)) == NULL) { + if(!this) { + printerr("Passed a NULL pointer."); return S_ERR; } + if(!(button_ptr = get_current_button(this, pci))) + return S_ERR; + dvdnav_button_select(this, pci, button_ptr->up); - if (dvdnav_button_auto_action(this, pci) ) { - dvdnav_button_activate(this, pci); - } + button_auto_action(this, pci); return S_OK; } @@ -268,17 +259,16 @@ dvdnav_status_t dvdnav_lower_button_select(dvdnav_t *this, pci_t *pci) { btni_t *button_ptr; - if(!this) - return S_ERR; - - if((button_ptr = __get_current_button(this, pci)) == NULL) { + if(!this) { + printerr("Passed a NULL pointer."); return S_ERR; } + if(!(button_ptr = get_current_button(this, pci))) + return S_ERR; + dvdnav_button_select(this, pci, button_ptr->down); - if (dvdnav_button_auto_action(this, pci) ) { - dvdnav_button_activate(this, pci); - } + button_auto_action(this, pci); return S_OK; } @@ -286,18 +276,16 @@ dvdnav_status_t dvdnav_right_button_select(dvdnav_t *this, pci_t *pci) { btni_t *button_ptr; - if(!this) - return S_ERR; - - if((button_ptr = __get_current_button(this, pci)) == NULL) { - printerr("Error fetching information on current button."); + if(!this) { + printerr("Passed a NULL pointer."); return S_ERR; } + if(!(button_ptr = get_current_button(this, pci))) + return S_ERR; + dvdnav_button_select(this, pci, button_ptr->right); - if (dvdnav_button_auto_action(this, pci) ) { - dvdnav_button_activate(this, pci); - } + button_auto_action(this, pci); return S_OK; } @@ -305,34 +293,31 @@ dvdnav_status_t dvdnav_left_button_select(dvdnav_t *this, pci_t *pci) { btni_t *button_ptr; - if(!this) - return S_ERR; - - if((button_ptr = __get_current_button(this, pci)) == NULL) { + if(!this) { + printerr("Passed a NULL pointer."); return S_ERR; } + if(!(button_ptr = get_current_button(this, pci))) + return S_ERR; + dvdnav_button_select(this, pci, button_ptr->left); - if (dvdnav_button_auto_action(this, pci) ) { - dvdnav_button_activate(this, pci); - } + button_auto_action(this, pci); return S_OK; } -dvdnav_status_t dvdnav_get_highlight_area(pci_t* nav_pci , int32_t button, int32_t mode, - dvdnav_highlight_area_t* highlight) { +dvdnav_status_t dvdnav_get_highlight_area(pci_t *nav_pci , int32_t button, int32_t mode, + dvdnav_highlight_area_t *highlight) { btni_t *button_ptr; + #ifdef BUTTON_TESTING fprintf(MSG_OUT, "libdvdnav: Button get_highlight_area %i\n", button); #endif - /* Set the highlight SPRM if the passed button was valid*/ - if((button <= 0) || (button > nav_pci->hli.hl_gi.btn_ns)) { - fprintf(MSG_OUT, "libdvdnav: Unable to select button number %i as it doesn't exist\n", - button); + if((button <= 0) || (button > nav_pci->hli.hl_gi.btn_ns)) return S_ERR; - } + button_ptr = &nav_pci->hli.btnit[button-1]; highlight->sx = button_ptr->x_start; @@ -347,7 +332,7 @@ highlight->pts = nav_pci->hli.hl_gi.hli_s_ptm; highlight->buttonN = button; #ifdef BUTTON_TESTING - fprintf(MSG_OUT, "libdvdnav: highlight.c:Highlight area is (%u,%u)-(%u,%u), display = %i, button = %u\n", + fprintf(MSG_OUT, "libdvdnav: highlight: Highlight area is (%u,%u)-(%u,%u), display = %i, button = %u\n", button_ptr->x_start, button_ptr->y_start, button_ptr->x_end, button_ptr->y_end, 1, @@ -360,21 +345,20 @@ dvdnav_status_t dvdnav_button_activate(dvdnav_t *this, pci_t *pci) { int button; btni_t *button_ptr = NULL; - - if(!this) - return S_ERR; + + if(!this) { + printerr("Passed a NULL pointer."); + return S_ERR; + } + pthread_mutex_lock(&this->vm_lock); - /* Precisely the same as selecting a button except we want - * a different palette */ if(dvdnav_get_current_highlight(this, &button) != S_OK) { - pthread_mutex_unlock(&this->vm_lock); + pthread_mutex_unlock(&this->vm_lock); return S_ERR; } -/* FIXME: dvdnav_button_select should really return a - * special case for explicit NO-BUTTONS. - */ - if(dvdnav_button_select(this, pci, button) != S_OK) { + + if((button <= 0) || (button > pci->hli.hl_gi.btn_ns)) { /* Special code to handle still menus with no buttons. * the navigation is expected to report to the appicatino that a STILL is * underway. In turn, the application is supposed to report to the user @@ -389,42 +373,46 @@ vm_get_next_cell(this->vm); this->position_current.still = 0; pthread_mutex_unlock(&this->vm_lock); + /* clear error message */ + printerr(""); return S_OK; } pthread_mutex_unlock(&this->vm_lock); return S_ERR; } - /* FIXME: The button command should really be passed in the API instead. */ - button_ptr = __get_current_button(this, pci); - /* Finally, make the VM execute the appropriate code and + + button_ptr = get_current_button(this, pci); + /* Finally, make the VM execute the appropriate code and probably * scedule a jump */ #ifdef BUTTON_TESTING fprintf(MSG_OUT, "libdvdnav: Evaluating Button Activation commands.\n"); #endif - if(vm_eval_cmd(this->vm, &(button_ptr->cmd)) == 1) { + if(vm_exec_cmd(this->vm, &(button_ptr->cmd)) == 1) { /* Command caused a jump */ this->vm->hop_channel++; this->position_current.still = 0; } + pthread_mutex_unlock(&this->vm_lock); return S_OK; } dvdnav_status_t dvdnav_button_activate_cmd(dvdnav_t *this, int32_t button, vm_cmd_t *cmd) { - if(!this || !this->vm) + if(!this) { + printerr("Passed a NULL pointer."); return S_ERR; - pthread_mutex_lock(&this->vm_lock); - /* make the VM execute the appropriate code and + } + + pthread_mutex_lock(&this->vm_lock); + /* make the VM execute the appropriate code and probably * schedule a jump */ #ifdef BUTTON_TESTING - fprintf(MSG_OUT, "libdvdnav:dvdnav_button_activate_cmd: Evaluating Button Activation commands.\n"); + fprintf(MSG_OUT, "libdvdnav: dvdnav_button_activate_cmd: Evaluating Button Activation commands.\n"); #endif if(button > 0) { - printerrf("Select button number %i\n ", - button); this->vm->state.HL_BTNN_REG = (button << 10); - if( (vm_eval_cmd(this->vm, cmd)) == 1) { + if(vm_exec_cmd(this->vm, cmd) == 1) { /* Command caused a jump */ this->vm->hop_channel++; } @@ -438,26 +426,20 @@ dvdnav_status_t dvdnav_button_select(dvdnav_t *this, pci_t *pci, int button) { if(!this) { - printerrf("Unable to select button number %i as this state bad", - button); - return S_ERR; + printerr("Passed a NULL pointer."); + return S_ERR; } #ifdef BUTTON_TESTING fprintf(MSG_OUT, "libdvdnav: Button select %i\n", button); #endif - /* Set the highlight SPRM if the passed button was valid*/ - /* FIXME: this->pci should be provided by the application. */ if((button <= 0) || (button > pci->hli.hl_gi.btn_ns)) { - printerrf("Unable to select button number %i as it doesn't exist", - button); + printerr("Button does not exist."); return S_ERR; } + this->vm->state.HL_BTNN_REG = (button << 10); - - this->hli_state = 1; /* Selected */ - this->position_current.button = -1; /* Force Highligh change */ return S_OK; @@ -466,11 +448,8 @@ dvdnav_status_t dvdnav_button_select_and_activate(dvdnav_t *this, pci_t *pci, int button) { /* A trivial function */ - if(dvdnav_button_select(this, pci, button) != S_ERR) { + if(dvdnav_button_select(this, pci, button) != S_ERR) return dvdnav_button_activate(this, pci); - } - - /* Should never get here without an error */ return S_ERR; } @@ -479,55 +458,49 @@ uint32_t best,dist; int mx,my,dx,dy,d; - /* FIXME: At the moment, the case of no button matchin (x,y) is - * silently ignored, is this OK? */ - if(!this) - return S_ERR; - - if(dvdnav_get_current_highlight(this, &cur_button) != S_OK) { + if(!this) { + printerr("Passed a NULL pointer."); return S_ERR; } - best = 0; + if(dvdnav_get_current_highlight(this, &cur_button) != S_OK) + return S_ERR; + + best = 0; dist = 0x08000000; /* >> than (720*720)+(567*567); */ - /* Loop through each button */ - for(button=1; button <= pci->hli.hl_gi.btn_ns; button++) { - btni_t *button_ptr = NULL; - button_ptr = &(this->pci.hli.btnit[button-1]); + /* Loop through all buttons */ + for(button = 1; button <= pci->hli.hl_gi.btn_ns; button++) { + btni_t *button_ptr = &(this->pci.hli.btnit[button-1]); + if((x >= button_ptr->x_start) && (x <= button_ptr->x_end) && (y >= button_ptr->y_start) && (y <= button_ptr->y_end)) { mx = (button_ptr->x_start + button_ptr->x_end)/2; - my = (button_ptr->y_start + button_ptr->y_end)/2; + my = (button_ptr->y_start + button_ptr->y_end)/2; dx = mx - x; dy = my - y; d = (dx*dx) + (dy*dy); /* If the mouse is within the button and the mouse is closer * to the center of this button then it is the best choice. */ if(d < dist) { - dist = d; best=button; + dist = d; + best = button; } } } - - if (best!=0) { - /* As an efficiency measure, only re-select the button - * if it is different to the previously selected one. */ - if(best != cur_button) { - dvdnav_button_select(this, pci, best); - } - } - - return S_OK; + + /* As an efficiency measure, only re-select the button + * if it is different to the previously selected one. */ + if (best != 0 && best != cur_button) + dvdnav_button_select(this, pci, best); + + /* return S_OK only if we actually found a matching button */ + return best ? S_OK : S_ERR; } dvdnav_status_t dvdnav_mouse_activate(dvdnav_t *this, pci_t *pci, int x, int y) { /* A trivial function */ - if(dvdnav_mouse_select(this, pci, x,y) != S_ERR) { + if(dvdnav_mouse_select(this, pci, x,y) != S_ERR) return dvdnav_button_activate(this, pci); - } - - /* Should never get here without an error */ return S_ERR; } - diff -r ec2df154be56 -r b6834e6359cf navigation.c --- a/navigation.c Mon Jan 13 13:33:45 2003 +0000 +++ b/navigation.c Thu Feb 20 15:32:21 2003 +0000 @@ -25,7 +25,6 @@ #include "config.h" #endif -#include #include "dvdnav_internal.h" #include "vm.h" @@ -33,8 +32,10 @@ /* Navigation API calls */ dvdnav_status_t dvdnav_still_skip(dvdnav_t *this) { - if(!this) - return S_ERR; + if(!this) { + printerr("Passed a NULL pointer."); + return S_ERR; + } this->position_current.still = 0; this->skip_still = 1; @@ -43,11 +44,8 @@ } dvdnav_status_t dvdnav_get_number_of_titles(dvdnav_t *this, int *titles) { - if(!this) - return S_ERR; - - if(!titles) { - printerr("Passed a NULL pointer"); + if(!this || !titles) { + printerr("Passed a NULL pointer."); return S_ERR; } @@ -63,11 +61,8 @@ } dvdnav_status_t dvdnav_get_number_of_parts(dvdnav_t *this, int title, int *parts) { - if(!this) - return S_ERR; - - if(!parts) { - printerr("Passed a NULL pointer"); + if(!this || !parts) { + printerr("Passed a NULL pointer."); return S_ERR; } if(!this->started) { @@ -75,81 +70,125 @@ return S_ERR; } if ((title < 1) || (title > vm_get_vmgi(this->vm)->tt_srpt->nr_of_srpts) ) { - printerr("Passed a title number out of range"); + printerr("Passed a title number out of range."); return S_ERR; } + (*parts) = vm_get_vmgi(this->vm)->tt_srpt->title[title-1].nr_of_ptts; + return S_OK; } dvdnav_status_t dvdnav_current_title_info(dvdnav_t *this, int *title, int *part) { - if(!this || !this->vm) - return S_ERR; - - if(!title || !part) { - printerr("Passed a NULL pointer"); + int retval; + + if(!this || !title || !part) { + printerr("Passed a NULL pointer."); + return S_ERR; + } + + pthread_mutex_lock(&this->vm_lock); + if (!this->vm->vtsi || !this->vm->vmgi) { + printerr("Bad VM state."); + pthread_mutex_unlock(&this->vm_lock); return S_ERR; } - - return vm_get_current_title_part(this->vm, title, part); + if (!this->vm->state.pgc) { + printerr("No current PGC."); + pthread_mutex_unlock(&this->vm_lock); + return S_ERR; + } + if (this->vm->state.domain != VTS_DOMAIN) { + printerr("Not in VTS domain."); + pthread_mutex_unlock(&this->vm_lock); + return S_ERR; + } + retval = vm_get_current_title_part(this->vm, title, part); + pthread_mutex_unlock(&this->vm_lock); + + return retval ? S_OK : S_ERR; } dvdnav_status_t dvdnav_title_play(dvdnav_t *this, int title) { - if(!this) { + printerr("Passed a NULL pointer."); return S_ERR; } - return dvdnav_part_play(this, title, 1); } dvdnav_status_t dvdnav_part_play(dvdnav_t *this, int title, int part) { + int retval; if(!this) { + printerr("Passed a NULL pointer."); + return S_ERR; + } + + pthread_mutex_lock(&this->vm_lock); + if (!this->vm->vtsi || !this->vm->vmgi) { + printerr("Bad VM state."); + pthread_mutex_unlock(&this->vm_lock); return S_ERR; } + if (!this->vm->state.pgc) { + printerr("No current PGC."); + pthread_mutex_unlock(&this->vm_lock); + return S_ERR; + } + if((title < 1) || (title > this->vm->vmgi->tt_srpt->nr_of_srpts)) { + printerr("Title out of range."); + pthread_mutex_unlock(&this->vm_lock); + return S_ERR; + } + retval = vm_jump_title_part(this->vm, title, part); + pthread_mutex_unlock(&this->vm_lock); - return vm_jump_title_part(this->vm, title, part); + return retval ? S_OK : S_ERR; } dvdnav_status_t dvdnav_part_play_auto_stop(dvdnav_t *this, int title, - int part, int parts_to_play) { - /* Perform jump as per usual */ - - return dvdnav_part_play(this, title, part); - - /* FIXME: Impement auto-stop */ - - /* return S_OK;*/ + int part, int parts_to_play) { + /* FIXME: Implement auto-stop */ + if (dvdnav_part_play(this, title, part) == S_OK) + printerr("Not implemented yet."); + return S_ERR; } dvdnav_status_t dvdnav_time_play(dvdnav_t *this, int title, - unsigned long int time) { + unsigned long int time) { + if(!this) { + printerr("Passed a NULL pointer."); + return S_ERR; + } + /* FIXME: Implement */ - - return S_OK; + printerr("Not implemented yet."); + return S_ERR; } dvdnav_status_t dvdnav_stop(dvdnav_t *this) { - if(!this) - return S_ERR; - - /* Set the STOP flag */ + if(!this) { + printerr("Passed a NULL pointer."); + return S_ERR; + } - this->stop = 1; - + pthread_mutex_lock(&this->vm_lock); + vm_stop(this->vm); + pthread_mutex_unlock(&this->vm_lock); return S_OK; } dvdnav_status_t dvdnav_go_up(dvdnav_t *this) { - if(!this) - return S_ERR; + if(!this) { + printerr("Passed a NULL pointer."); + return S_ERR; + } /* A nice easy function... delegate to the VM */ - vm_go_up(this->vm); + pthread_mutex_lock(&this->vm_lock); + vm_jump_up(this->vm); + pthread_mutex_unlock(&this->vm_lock); return S_OK; } - - - diff -r ec2df154be56 -r b6834e6359cf read_cache.c --- a/read_cache.c Mon Jan 13 13:33:45 2003 +0000 +++ b/read_cache.c Thu Feb 20 15:32:21 2003 +0000 @@ -106,13 +106,13 @@ #ifdef __GNUC__ # if READ_CACHE_TRACE -# define dprintf(fmt, args...) fprintf(MSG_OUT, "libdvdnav: %s: "fmt, __func__ , ## arg +# define dprintf(fmt, args...) fprintf(MSG_OUT, "libdvdnav: %s: "fmt, __func__ , ## args) # else # define dprintf(fmt, args...) /* Nowt */ # endif #else # if READ_CACHE_TRACE -# define dprintf(fmt, ...) fprintf(MSG_OUT, "libdvdnav: %s: "fmt, __func__ , __VA_ARGS_ +# define dprintf(fmt, ...) fprintf(MSG_OUT, "libdvdnav: %s: "fmt, __func__ , __VA_ARGS__) # else # define dprintf(fmt, ...) /* Nowt */ # endif @@ -284,7 +284,7 @@ pthread_mutex_unlock(&self->cache_lock); } -/* This function will do the cache read once implemented */ +/* This function will do the cache read */ int dvdnav_read_cache_block( read_cache_t *self, int sector, size_t block_count, uint8_t **buf) { int result, diff; diff -r ec2df154be56 -r b6834e6359cf remap.c --- a/remap.c Mon Jan 13 13:33:45 2003 +0000 +++ b/remap.c Thu Feb 20 15:32:21 2003 +0000 @@ -1,3 +1,23 @@ +/* + * This file is part of libdvdnav, a DVD navigation library. + * + * 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$ + */ + #include #include #include @@ -5,6 +25,7 @@ #include #include #include "remap.h" +#include "dvdnav_internal.h" struct block_s { int domain; @@ -164,15 +185,14 @@ /* Build the map filename */ home = getenv("HOME"); assert(home); strncpy(fname, home, sizeof(fname)); - strncat(fname, "/.xine/", sizeof(fname)); + strncat(fname, "/.dvdnav/", sizeof(fname)); strncat(fname, title, sizeof(fname)); strncat(fname, ".map", sizeof(fname)); - printf("Loading %s.\n", fname); /* Open the map file */ fp = fopen( fname, "r"); if (!fp) { - printf("Unable to find map file '%s'\n", fname); + fprintf(MSG_OUT, "libdvdnav: Unable to find map file '%s'\n", fname); return NULL; } @@ -186,7 +206,7 @@ res = parseblock( buf, &tmp.domain, &tmp.title, &tmp.program, &tmp.start_block, &tmp.end_block); if (res != 5) { - printf("Ignoring map line (%d): %s\n", res, buf); + fprintf(MSG_OUT, "libdvdnav: Ignoring map line (%d): %s\n", res, buf); continue; } remap_add_node( map, tmp); @@ -205,7 +225,7 @@ block_t *b; if (map->debug) { - printf("%s: domain %d, title %d, program %d, start %lx, next %lx\n", + fprintf(MSG_OUT, "libdvdnav: %s: domain %d, title %d, program %d, start %lx, next %lx\n", map->title, domain, title, program, cblock, cblock+offset); } @@ -217,7 +237,7 @@ if (b) { if (map->debug) { - printf("Redirected to %lx\n", b->end_block); + fprintf(MSG_OUT, "libdvdnav: Redirected to %lx\n", b->end_block); } return b->end_block - cblock; } diff -r ec2df154be56 -r b6834e6359cf remap.h --- a/remap.h Mon Jan 13 13:33:45 2003 +0000 +++ b/remap.h Thu Feb 20 15:32:21 2003 +0000 @@ -1,3 +1,23 @@ +/* + * This file is part of libdvdnav, a DVD navigation library. + * + * 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$ + */ + #ifndef __REMAP__H #define __REMAP__H typedef struct block_s block_t; diff -r ec2df154be56 -r b6834e6359cf searching.c --- a/searching.c Mon Jan 13 13:33:45 2003 +0000 +++ b/searching.c Thu Feb 20 15:32:21 2003 +0000 @@ -25,46 +25,52 @@ #include "config.h" #endif -#include +#include + #include "dvdnav_internal.h" #include "vm.h" #include +/* +#define LOG_DEBUG +*/ + /* Searching API calls */ dvdnav_status_t dvdnav_time_search(dvdnav_t *this, unsigned long int time) { -/* Time search the current PGC based on the xxx table */ + /* FIXME: Time search the current PGC based on the xxx table */ return S_OK; } /* Scan the ADMAP for a particular block number. */ /* Return placed in vobu. */ /* Returns error status */ - -dvdnav_status_t dvdnav_scan_admap(dvdnav_t *this, int32_t domain, int32_t seekto_block, int32_t *vobu) { - /* FIXME:Need to handle seeking outside current cell. */ +/* FIXME: Maybe need to handle seeking outside current cell. */ +static dvdnav_status_t dvdnav_scan_admap(dvdnav_t *this, int32_t domain, int32_t seekto_block, int32_t *vobu) { vobu_admap_t *admap = NULL; + +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: Seeking to target %u ...\n", seekto_block); +#endif *vobu = -1; - fprintf(MSG_OUT, "libdvdnav: Seeking to target %u ...\n", - seekto_block); /* Search through the VOBU_ADMAP for the nearest VOBU * to the target block */ switch(domain) { - case FP_DOMAIN: - case VMGM_DOMAIN: - admap = this->vm->vmgi->menu_vobu_admap; - break; - case VTSM_DOMAIN: - admap = this->vm->vtsi->menu_vobu_admap; - break; - case VTS_DOMAIN: - admap = this->vm->vtsi->vts_vobu_admap; - break; - default: - fprintf(MSG_OUT, "libdvdnav: Error: Unknown domain for seeking seek.\n"); + case FP_DOMAIN: + case VMGM_DOMAIN: + admap = this->vm->vmgi->menu_vobu_admap; + break; + case VTSM_DOMAIN: + admap = this->vm->vtsi->menu_vobu_admap; + break; + case VTS_DOMAIN: + admap = this->vm->vtsi->vts_vobu_admap; + break; + default: + fprintf(MSG_OUT, "libdvdnav: Error: Unknown domain for seeking.\n"); } if(admap) { int32_t address = 0; @@ -85,7 +91,6 @@ } else { vobu_start = next_vobu; } - address ++; } if(found) { @@ -101,65 +106,67 @@ } dvdnav_status_t dvdnav_sector_search(dvdnav_t *this, - unsigned long int offset, int origin) { -/* FIXME: Implement */ - + unsigned long int offset, int origin) { uint32_t target = 0; uint32_t length = 0; - uint32_t first_cell_nr, last_cell_nr, cell_nr, fnd_cell_nr; + uint32_t first_cell_nr, last_cell_nr, cell_nr; int found; - cell_playback_t *cell, *fnd_cell; + cell_playback_t *cell; dvd_state_t *state; dvdnav_status_t result; - if((!this) || (!this->vm) || (!this->started)) - return -1; + if(this->position_current.still != 0) { + printerr("Cannot seek in a still frame."); + return S_ERR; + } - state = &(this->vm->state); - if((!state) || (!state->pgc) ) - return -1; - - if(this->position_current.still != 0) - /* Cannot do seeking in a still frame. */ - return -1; - - pthread_mutex_lock(&this->vm_lock); result = dvdnav_get_position(this, &target, &length); - fprintf(MSG_OUT, "libdvdnav: FIXME: seeking to offset=%lu pos=%u length=%u\n", offset, target, length); - fprintf(MSG_OUT, "libdvdnav: FIXME: Before cellN=%u blockN=%u\n" , - state->cellN, - state->blockN); +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: seeking to offset=%lu pos=%u length=%u\n", offset, target, length); + fprintf(MSG_OUT, "libdvdnav: Before cellN=%u blockN=%u\n", state->cellN, state->blockN); +#endif if(!result) { - pthread_mutex_unlock(&this->vm_lock); - return -1; + return S_ERR; } + pthread_mutex_lock(&this->vm_lock); + state = &(this->vm->state); + if(!state->pgc) { + printerr("No current PGC."); + pthread_mutex_unlock(&this->vm_lock); + return S_ERR; + } + switch(origin) { case SEEK_SET: if(offset > length) { + printerr("Request to seek behind end."); pthread_mutex_unlock(&this->vm_lock); - return -1; + return S_ERR; } target = offset; break; case SEEK_CUR: if(target + offset > length) { + printerr("Request to seek behind end."); pthread_mutex_unlock(&this->vm_lock); - return -1; + return S_ERR; } target += offset; break; case SEEK_END: if(length - offset < 0) { + printerr("Request to seek before start."); pthread_mutex_unlock(&this->vm_lock); - return -1; + return S_ERR; } target = length - offset; break; default: /* Error occured */ + printerr("Illegal seek mode."); pthread_mutex_unlock(&this->vm_lock); - return -1; + return S_ERR; } /* First find closest cell number in program */ @@ -170,83 +177,89 @@ last_cell_nr = state->pgc->nr_of_cells; } - found = 0; target += state->pgc->cell_playback[first_cell_nr-1].first_sector; - fnd_cell_nr = last_cell_nr + 1; + found = 0; + target += state->pgc->cell_playback[first_cell_nr-1].first_sector; for(cell_nr = first_cell_nr; (cell_nr <= last_cell_nr) && !found; cell_nr ++) { cell = &(state->pgc->cell_playback[cell_nr-1]); if((cell->first_sector <= target) && (cell->last_sector >= target)) { + found = 1; state->cellN = cell_nr; state->blockN = 0; - state->cell_restart++; - found = 1; - fnd_cell_nr = cell_nr; - fnd_cell = cell; + state->cell_restart++; } } - if(fnd_cell_nr <= last_cell_nr) { + if(found) { int32_t vobu, start; - dvdnav_status_t status; +#ifdef LOG_DEBUG fprintf(MSG_OUT, "libdvdnav: Seeking to cell %i from choice of %i to %i\n", - fnd_cell_nr, first_cell_nr, last_cell_nr); - status = dvdnav_scan_admap(this, state->domain, target, &vobu); - /* - * Clut does not actually change, - * but as the decoders have been closed then opened, - * A new clut has to be sent. - */ - start =(state->pgc->cell_playback[state->cellN - 1].first_sector); - fprintf(MSG_OUT, "libdvdnav: FIXME: After cellN=%u blockN=%u target=%x vobu=%x start=%x\n" , + state->cellN, first_cell_nr, last_cell_nr); +#endif + dvdnav_scan_admap(this, state->domain, target, &vobu); + start = state->pgc->cell_playback[state->cellN - 1].first_sector; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: After cellN=%u blockN=%u target=%x vobu=%x start=%x\n" , state->cellN, state->blockN, target, vobu, start); - state->blockN = vobu - start; - fprintf(MSG_OUT, "libdvdnav: FIXME: After vobu=%x start=%x blockN=%x\n" , +#endif + state->blockN = vobu - start; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: After vobu=%x start=%x blockN=%x\n" , vobu, start, state->blockN); +#endif + this->vm->hop_channel += HOP_SEEK; pthread_mutex_unlock(&this->vm_lock); - return target; - } else { - fprintf(MSG_OUT, "libdvdnav: Error when seeking, asked to seek outside program\n"); + return S_OK; } - + + fprintf(MSG_OUT, "libdvdnav: Error when seeking, asked to seek outside program\n"); fprintf(MSG_OUT, "libdvdnav: FIXME: Implement seeking to location %u\n", target); - + printerr("Error when seeking."); pthread_mutex_unlock(&this->vm_lock); - return -1; + return S_ERR; } dvdnav_status_t dvdnav_part_search(dvdnav_t *this, int part) { - - if((!this) || (!this->vm) ) - return S_ERR; - - return S_OK; + int title, old_part; + + if (dvdnav_current_title_info(this, &title, &old_part) == S_OK) + return dvdnav_part_play(this, title, part); + return S_ERR; } dvdnav_status_t dvdnav_prev_pg_search(dvdnav_t *this) { - dvd_state_t *state; - - if((!this) || (!this->vm) ) - return S_ERR; - state = &(this->vm->state); - if((!state) || (!state->pgc) ) + if(!this) { + printerr("Passed a NULL pointer."); return S_ERR; + } + + pthread_mutex_lock(&this->vm_lock); + if(!this->vm->state.pgc) { + printerr("No current PGC."); + pthread_mutex_unlock(&this->vm_lock); + return S_ERR; + } - pthread_mutex_lock(&this->vm_lock); +#ifdef LOG_DEBUG fprintf(MSG_OUT, "libdvdnav: previous chapter\n"); - if (!vm_prev_pg(this->vm)) { - fprintf(MSG_OUT, "libdvdnav: prev chapter failed.\n"); +#endif + if (!vm_jump_prev_pg(this->vm)) { + fprintf(MSG_OUT, "libdvdnav: previous chapter failed.\n"); + printerr("Skip to previous chapter failed."); pthread_mutex_unlock(&this->vm_lock); return S_ERR; } this->position_current.still = 0; this->vm->hop_channel++; +#ifdef LOG_DEBUG fprintf(MSG_OUT, "libdvdnav: previous chapter done\n"); +#endif pthread_mutex_unlock(&this->vm_lock); return S_OK; @@ -254,70 +267,96 @@ dvdnav_status_t dvdnav_top_pg_search(dvdnav_t *this) { - if((!this) || (!this->vm) ) + if(!this) { + printerr("Passed a NULL pointer."); return S_ERR; + } - fprintf(MSG_OUT, "libdvdnav: top chapter. NOP.\n"); - - return S_OK; -} - -dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *this) { - dvd_state_t *state; + pthread_mutex_lock(&this->vm_lock); + if(!this->vm->state.pgc) { + printerr("No current PGC."); + pthread_mutex_unlock(&this->vm_lock); + return S_ERR; + } - if((!this) || (!this->vm) ) - return S_ERR; - - state = &(this->vm->state); - if((!state) || (!state->pgc) ) - return S_ERR; - - pthread_mutex_lock(&this->vm_lock); - fprintf(MSG_OUT, "libdvdnav: next chapter\n"); - if (!vm_next_pg(this->vm)) { - fprintf(MSG_OUT, "libdvdnav: next chapter failed.\n"); +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: top chapter\n"); +#endif + if (!vm_jump_top_pg(this->vm)) { + fprintf(MSG_OUT, "libdvdnav: top chapter failed.\n"); + printerr("Skip to top chapter failed."); pthread_mutex_unlock(&this->vm_lock); return S_ERR; } this->position_current.still = 0; this->vm->hop_channel++; +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: top chapter done\n"); +#endif + pthread_mutex_unlock(&this->vm_lock); + + return S_OK; +} + +dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *this) { + + if(!this) { + printerr("Passed a NULL pointer."); + return S_ERR; + } + + pthread_mutex_lock(&this->vm_lock); + if(!this->vm->state.pgc) { + printerr("No current PGC."); + pthread_mutex_unlock(&this->vm_lock); + return S_ERR; + } + +#ifdef LOG_DEBUG + fprintf(MSG_OUT, "libdvdnav: next chapter\n"); +#endif + if (!vm_jump_next_pg(this->vm)) { + fprintf(MSG_OUT, "libdvdnav: next chapter failed.\n"); + printerr("Skip to next chapter failed."); + pthread_mutex_unlock(&this->vm_lock); + return S_ERR; + } + this->position_current.still = 0; + this->vm->hop_channel++; +#ifdef LOG_DEBUG fprintf(MSG_OUT, "libdvdnav: next chapter done\n"); +#endif pthread_mutex_unlock(&this->vm_lock); return S_OK; } dvdnav_status_t dvdnav_menu_call(dvdnav_t *this, DVDMenuID_t menu) { - dvd_state_t *state; - - if((!this) || (!this->vm) ) - return S_ERR; - - pthread_mutex_lock(&this->vm_lock); - state = &(this->vm->state); - if (vm_menu_call(this->vm, menu, 0)) - this->vm->hop_channel++; - pthread_mutex_unlock(&this->vm_lock); - return S_OK; -} - -static char __title_str[] = "DVDNAV"; - -dvdnav_status_t dvdnav_get_title_string(dvdnav_t *this, char **title_str) { - if(!this) - return S_ERR; - - if(!title_str) { - printerr("Passed a NULL pointer"); + + if(!this) { + printerr("Passed a NULL pointer."); return S_ERR; } - (*title_str) = __title_str; - - return S_OK; + pthread_mutex_lock(&this->vm_lock); + if(!this->vm->state.pgc) { + printerr("No current PGC."); + pthread_mutex_unlock(&this->vm_lock); + return S_ERR; + } + + if (vm_jump_menu(this->vm, menu)) { + this->vm->hop_channel++; + pthread_mutex_unlock(&this->vm_lock); + return S_OK; + } else { + printerr("No such menu."); + pthread_mutex_unlock(&this->vm_lock); + return S_ERR; + } } -dvdnav_status_t dvdnav_get_position(dvdnav_t *this, unsigned int* pos, +dvdnav_status_t dvdnav_get_position(dvdnav_t *this, unsigned int *pos, unsigned int *len) { uint32_t cur_sector; uint32_t first_cell_nr; @@ -325,24 +364,32 @@ cell_playback_t *first_cell; cell_playback_t *last_cell; dvd_state_t *state; - if((!this) || (!this->vm) ) - return 0; - + + if(!this || !pos || !len) { + printerr("Passed a NULL pointer."); + return S_ERR; + } + if(!this->started) { + printerr("Virtual DVD machine not started."); + return S_ERR; + } + + pthread_mutex_lock(&this->vm_lock); state = &(this->vm->state); - if((!state) || (!state->pgc) ) - return 0; - - /* Sanity check */ - if(state->pgN > state->pgc->nr_of_programs) { - return 0; + if(!state->pgc) { + printerr("No current PGC."); + pthread_mutex_unlock(&this->vm_lock); + return S_ERR; } - + /* Get current sector */ cur_sector = this->vobu.vobu_start + this->vobu.blockN; /* Find start cell of program. */ first_cell_nr = state->pgc->program_map[state->pgN-1]; first_cell = &(state->pgc->cell_playback[first_cell_nr-1]); + + /* Find end cell of program */ if(state->pgN < state->pgc->nr_of_programs) { last_cell_nr = state->pgc->program_map[state->pgN] - 1; } else { @@ -352,8 +399,7 @@ *pos= cur_sector - first_cell->first_sector; *len= last_cell->last_sector - first_cell->first_sector; - /* fprintf(MSG_OUT, "libdvdnav: searching:current pos=%u length=%u\n",*pos,*len); */ - + pthread_mutex_unlock(&this->vm_lock); return S_OK; } @@ -367,18 +413,18 @@ cell_playback_t *first_cell; cell_playback_t *last_cell; dvd_state_t *state; - if((!this) || (!this->vm) ) - return S_ERR; - - state = &(this->vm->state); - if((!state) || (!state->pgc) ) - return S_ERR; - - /* Sanity check */ - if(state->pgN > state->pgc->nr_of_programs) { + + if(!this || !pos || !len) { + printerr("Passed a NULL pointer."); return S_ERR; } - + + state = &(this->vm->state); + if(!state->pgc) { + printerr("No current PGC."); + return S_ERR; + } + /* Get current sector */ cur_sector = this->vobu.vobu_start + this->vobu.blockN; @@ -393,5 +439,3 @@ return S_OK; } - - diff -r ec2df154be56 -r b6834e6359cf settings.c --- a/settings.c Mon Jan 13 13:33:45 2003 +0000 +++ b/settings.c Thu Feb 20 15:32:21 2003 +0000 @@ -25,7 +25,6 @@ #include "config.h" #endif -#include #include "dvdnav_internal.h" #include "vm.h" @@ -33,57 +32,40 @@ /* Characteristics/setting API calls */ dvdnav_status_t dvdnav_get_region_mask(dvdnav_t *this, int *region) { - if(!this) { - printerr("Passed a NULL this pointer"); - return S_ERR; - } - - if(!region) { - printerr("Passed a NULL region pointer"); - return S_ERR; - } - - if(!this->vm) { - printerr("VM not yet initialised"); + if(!this || !region) { + printerr("Passed a NULL this pointer."); return S_ERR; } (*region) = this->vm->state.registers.SPRM[20]; - return S_OK; } dvdnav_status_t dvdnav_set_region_mask(dvdnav_t *this, int mask) { - if(!this) - return S_ERR; - - if(!this->vm) { - printerr("VM not yet initialised"); + if(!this) { + printerr("Passed a NULL this pointer."); return S_ERR; } + pthread_mutex_lock(&this->vm_lock); this->vm->state.registers.SPRM[20] = (mask & 0xff); - + pthread_mutex_unlock(&this->vm_lock); return S_OK; } dvdnav_status_t dvdnav_set_readahead_flag(dvdnav_t *this, int use_readahead) { - if(!this) - return S_ERR; + if(!this) { + printerr("Passed a NULL this pointer."); + return S_ERR; + } this->use_read_ahead = use_readahead; - return S_OK; } -dvdnav_status_t dvdnav_get_readahead_flag(dvdnav_t *this, int* flag) { - if(!this) { - printerr("Passed a NULL this pointer"); - return S_ERR; - } - - if(!flag) { - printerr("Passed a NULL flag pointer"); +dvdnav_status_t dvdnav_get_readahead_flag(dvdnav_t *this, int *flag) { + if(!this || !flag) { + printerr("Passed a NULL this pointer."); return S_ERR; } @@ -92,23 +74,13 @@ } static dvdnav_status_t set_language_register(dvdnav_t *this, char *code, int reg) { - if(!this ) { - printerr("Passed a NULL this pointer"); + if(!this || !code) { + printerr("Passed a NULL this pointer."); return S_ERR; } - if(!code) { - printerr("Passed a NULL code pointer"); - return S_ERR; - } - if(!code[0] || !code[1]) { - printerr("Passed illegal language code"); - return S_ERR; - } - - if(!this->vm) { - printerr("VM not yet initialised"); + printerr("Passed illegal language code."); return S_ERR; } diff -r ec2df154be56 -r b6834e6359cf vm.c --- a/vm.c Mon Jan 13 13:33:45 2003 +0000 +++ b/vm.c Thu Feb 20 15:32:21 2003 +0000 @@ -45,52 +45,50 @@ #include "vm.h" #include "dvdnav_internal.h" - +/* #define STRICT - +*/ /* Local prototypes */ -static void saveRSMinfo(vm_t *vm,int cellN, int blockN); +/* 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); -static link_t process_command(vm_t *vm,link_t link_values); + +/* Process link - returns 1 if a hop has been performed */ +static int process_command(vm_t *vm,link_t link_values); -static void ifoOpenNewVTSI(vm_t *vm,dvd_reader_t *dvd, int vtsN); +/* Set */ +static int set_TT(vm_t *vm, int tt); +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); -/* get_XYZ returns a value. - * ser_XYZ sets state using passed parameters. - * returns success/failure. - */ - -/* Can only be called when in VTS_DOMAIN */ -static int set_FP_PGC(vm_t *vm); /* FP */ -static int set_TT(vm_t *vm,int tt); -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_MENU(vm_t *vm,int menu); /* VTSM & VMGM */ -/* Called in any domain */ -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 int set_PGN(vm_t *vm); /* Set PGN based on (vm->state).CellN */ -static int set_PGC(vm_t *vm,int pgcN); +/* Helper functions */ -/* Initialisation */ - -vm_t* vm_new_vm() { - vm_t *vm = (vm_t*)calloc(sizeof(vm_t), sizeof(char)); - - return vm; -} - +#ifdef TRACE static void vm_print_current_domain_state(vm_t *vm) { switch((vm->state).domain) { case VTS_DOMAIN: @@ -123,62 +121,15 @@ (vm->state).TTN_REG, (vm->state).TT_PGCN_REG); } - -void vm_stop(vm_t *vm) { - if(!vm) - return; - - if(vm->vmgi) { - ifoClose(vm->vmgi); - vm->vmgi=NULL; - } - - if(vm->vtsi) { - ifoClose(vm->vtsi); - vm->vmgi=NULL; - } - - if(vm->dvd) { - DVDClose(vm->dvd); - vm->dvd=NULL; - } -} - -void vm_free_vm(vm_t *vm) { - if(vm) { - vm_stop(vm); - free(vm); - } -} +#endif -/* IFO Access */ - -ifo_handle_t *vm_get_vmgi(vm_t *vm) { - if(!vm) - return NULL; - - return vm->vmgi; -} - -ifo_handle_t *vm_get_vtsi(vm_t *vm) { - if(!vm) - return NULL; - - return vm->vtsi; -} - -/* Reader Access */ - -dvd_reader_t *vm_get_dvd_reader(vm_t *vm) { - if(!vm) - return NULL; - - return vm->dvd; -} - -void dvd_read_name( vm_t *this, char *devname) { +void dvd_read_name( vm_t *this, const char *devname) { int fd, i; +#ifndef __FreeBSD__ off64_t off; +#else + off_t off; +#endif uint8_t data[DVD_VIDEO_LB_LEN]; /* Read DVD name */ @@ -189,1767 +140,49 @@ off = read( fd, data, DVD_VIDEO_LB_LEN ); close(fd); if (off == ( (int64_t) DVD_VIDEO_LB_LEN )) { - fprintf( stderr, "VM DVD Title: "); + 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(stderr, "%c", data[i]); + fprintf(MSG_OUT, "%c", data[i]); } else { - fprintf(stderr, " "); + fprintf(MSG_OUT, " "); } } strncpy(&this->dvd_name[0], &data[25], 48); - /* fprintf(stderr, "TITLE:%s\n",&this->dvd_name[0]); */ this->dvd_name[48]=0; - this->dvd_name_length=strlen(&this->dvd_name[0]); - fprintf( stderr, "\nVM DVD Serial Number: "); + 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(stderr, "%c", data[i]); + fprintf(MSG_OUT, "%c", data[i]); } else { - fprintf(stderr, " "); + fprintf(MSG_OUT, " "); } } - fprintf( stderr, "\nVM DVD Title (Alternative): "); + 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(stderr, "%c", data[i]); + fprintf(MSG_OUT, "%c", data[i]); } else { - fprintf(stderr, " "); + fprintf(MSG_OUT, " "); } } - fprintf( stderr, "\n"); + fprintf(MSG_OUT, "\n"); } else { - fprintf( stderr, "libdvdread: Can't read name block. Probably not a DVD-ROM device.\n"); + fprintf(MSG_OUT, "libdvdnav: Can't read name block. Probably not a DVD-ROM device.\n"); } } else { - fprintf( stderr, "libdvdread: Can't seek to block %u\n", 32 ); + fprintf(MSG_OUT, "libdvdnav: Can't seek to block %u\n", 32 ); } close(fd); } else { - fprintf(stderr,"NAME OPEN FAILED\n"); - } -} - -int vm_reset(vm_t *vm, char *dvdroot) /* , register_t regs) */ { - /* Setup State */ - memset((vm->state).registers.SPRM, 0, sizeof(uint16_t)*24); - 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 */ - /* Player Regional Code Mask. - * bit0 = Region 1 - * bit1 = Region 2 - */ - (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 -1; - } - dvd_read_name(vm, 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 -1; - } - if(!ifoRead_FP_PGC(vm->vmgi)) { - fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_FP_PGC failed\n"); - return -1; - } - if(!ifoRead_TT_SRPT(vm->vmgi)) { - fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_TT_SRPT failed\n"); - return -1; - } - if(!ifoRead_PGCI_UT(vm->vmgi)) { - fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_PGCI_UT failed\n"); - return -1; - } - if(!ifoRead_PTL_MAIT(vm->vmgi)) { - fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_PTL_MAIT failed\n"); - ; /* return -1; Not really used for now.. */ - } - if(!ifoRead_VTS_ATRT(vm->vmgi)) { - fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_VTS_ATRT failed\n"); - ; /* return -1; Not really used for now.. */ - } - if(!ifoRead_VOBU_ADMAP(vm->vmgi)) { - fprintf(MSG_OUT, "libdvdnav: vm: ifoRead_VOBU_ADMAP vgmi failed\n"); - ; /* return -1; Not really used for now.. */ - } - /* ifoRead_TXTDT_MGI(vmgi); Not implemented yet */ - } - else fprintf(MSG_OUT, "libdvdnav: vm: reset\n"); - if (vm->vmgi) { - fprintf(MSG_OUT, "libdvdnav: DVD disk reports itself with Region mask 0x%08x. Maybe region %u.\n", - vm->vmgi->vmgi_mat->vmg_category, - (((vm->vmgi->vmgi_mat->vmg_category >> 16) ^ 0xff) & 0xff) ); - } - return 0; -} - -/* FIXME TODO XXX $$$ Handle error condition too... */ -int vm_start(vm_t *vm) -{ - link_t link_values; - - /* Set pgc to FP(First Play) pgc */ - set_FP_PGC(vm); - link_values = play_PGC(vm); - link_values = process_command(vm,link_values); - assert(link_values.command == PlayThis); - (vm->state).blockN = link_values.data1; -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: vm_start: blockN set to 0x%x\n", (vm->state).blockN); -#endif - assert( (vm->state).blockN == 0 ); - - - return 0; /* ?? */ -} - -int vm_position_get(vm_t *vm, vm_position_t *position) { - position->button = (vm->state).HL_BTNN_REG >> 10; - 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->vts = (vm->state).vtsN; - position->domain = (vm->state).domain; - position->cell = (vm->state).cellN; - position->cell_restart = (vm->state).cell_restart; - position->still = (vm->state).pgc->cell_playback[(vm->state).cellN - 1].still_time; - position->vobu_start = (vm->state).pgc->cell_playback[(vm->state).cellN - 1].first_sector; - position->vobu_next = (vm->state).blockN; - - /* still already detrmined or not at PGC end */ - if (position->still || (vm->state).cellN < (vm->state).pgc->nr_of_cells) - return 1; - /* handle PGC stills */ - if ((vm->state).pgc->still_time) { - position->still = (vm->state).pgc->still_time; - return 1; - } - /* 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 < 250)) { - int time; - time = ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.hour & 0xf0) * 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 & 0xf0) * 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 & 0xf0) * 10; - time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.second & 0x0f) * 1; - if (time > 0xff) time = 0xff; - position->still = time; - } - - return 1; -} - -int vm_position_print(vm_t *vm, vm_position_t *position) { - fprintf(MSG_OUT, "libdvdnav: But=%x Spu=%x Aud=%x Ang=%x Hop=%x vts=%x dom=%x cell=%x cell_restart=%x still=%x start=%x next=%x\n", - position->button, - position->spu_channel, - position->audio_channel, - position->angle_channel, - position->hop_channel, - position->vts, - position->domain, - position->cell, - position->cell_restart, - position->still, - position->vobu_start, - position->vobu_next); - return 1; -} - - -int vm_start_title(vm_t *vm, int tt) { - link_t link_values; - - set_TT(vm, tt); - link_values = play_PGC(vm); - link_values = process_command(vm, link_values); - assert(link_values.command == PlayThis); - (vm->state).blockN = link_values.data1; -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: vm_start_title: blockN set to 0x%x\n", (vm->state).blockN); -#endif - assert( (vm->state).blockN == 0 ); - - return 0; /* ?? */ -} - -int vm_jump_prog(vm_t *vm, int pr) { - link_t link_values; - - - set_PGC(vm, get_PGCN(vm)); - (vm->state).pgN = pr; /* ?? set_PGC() clobbers pgN */ - link_values = play_PG(vm); - link_values = process_command(vm, link_values); - assert(link_values.command == PlayThis); - (vm->state).blockN = link_values.data1; -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: vm_jump_prog: blockN set to 0x%x\n", (vm->state).blockN); -#endif - assert( (vm->state).blockN == 0 ); - - return 0; /* ?? */ -} - -int vm_eval_cmd(vm_t *vm, vm_cmd_t *cmd) -{ - link_t link_values; - - if(vmEval_CMD(cmd, 1, &(vm->state).registers, &link_values)) { - link_values = process_command(vm, link_values); - assert(link_values.command == PlayThis); - (vm->state).blockN = link_values.data1; -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: vm_eval_cmd: blockN set to 0x%x\n", (vm->state).blockN); -#endif - return link_values.data2; /* return if there acutally was a jump */ - } else { - return 0; /* It updated some state thats all... */ - } -} - -int vm_get_next_cell(vm_t *vm) -{ - link_t link_values; - link_values = play_Cell_post(vm); - link_values = process_command(vm,link_values); - assert(link_values.command == PlayThis); - (vm->state).blockN = link_values.data1; -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: vm_get_next_cell: blockN set to 0x%x\n", (vm->state).blockN); -#endif - assert( (vm->state).blockN == 0 ); - - return 0; /* ?? */ -} - -int vm_top_pg(vm_t *vm) -{ - link_t link_values; - link_values = play_PG(vm); - link_values = process_command(vm,link_values); - assert(link_values.command == PlayThis); - (vm->state).blockN = link_values.data1; -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: vm_top_pg: blockN set to 0x%x\n", (vm->state).blockN); -#endif - assert( (vm->state).blockN == 0 ); - - return 1; /* Jump */ -} - -int vm_go_up(vm_t *vm) -{ - link_t link_values; - - if(set_PGC(vm, (vm->state).pgc->goup_pgc_nr)) - assert(0); - - link_values = play_PGC(vm); - link_values = process_command(vm,link_values); - assert(link_values.command == PlayThis); - (vm->state).blockN = link_values.data1; -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: vm_go_up: blockN set to 0x%x\n", (vm->state).blockN); -#endif - assert( (vm->state).blockN == 0 ); - - return 1; /* Jump */ -} - -int vm_next_pg(vm_t *vm) -{ - if((vm->state).pgN >= (vm->state).pgc->nr_of_programs) { - /* last program -> move to first program of next PGC */ - if ((vm->state).pgc->next_pgc_nr != 0 && set_PGC(vm, (vm->state).pgc->next_pgc_nr) == 0) { - vm_jump_prog(vm, 1); - return 1; - } - /* something failed, try to move to the cell after the last */ - (vm->state).cellN = (vm->state).pgc->nr_of_cells; - vm_get_next_cell(vm); - return 1; - } else { - vm_jump_prog(vm, (vm->state).pgN + 1); - return 1; - } -} - -int vm_prev_pg(vm_t *vm) -{ - if ((vm->state).pgN <= 1) { - /* first program -> move to last program of previous PGC */ - if ((vm->state).pgc->prev_pgc_nr != 0 && set_PGC(vm, (vm->state).pgc->prev_pgc_nr) == 0) { - vm_jump_prog(vm, (vm->state).pgc->nr_of_programs); - return 1; - } - return 0; - } else { - vm_jump_prog(vm, (vm->state).pgN - 1); - return 1; - } -} - -/* Get the current title and part from the current playing position. */ -/* returns S_ERR if not in the VTS_DOMAIN */ -/* FIXME: Should we do some locking here ? */ -int vm_get_current_title_part(vm_t *vm, int *title_result, int *part_result) -{ - vts_ptt_srpt_t *vts_ptt_srpt; - int title=0, part=0, ttn=0; - int found = 0; - int16_t pgcN, pgN; - - if((!vm) || (!vm->vtsi) ) - return S_ERR; - - if(!title_result || !part_result) { - fprintf(MSG_OUT, "libdvdnav:vm_get_current_title_part: Passed a NULL pointer"); - return S_ERR; - } - - if(!(vm->state.pgc) ) - return S_ERR; - if (vm->state.domain != VTS_DOMAIN) - return S_ERR; - vts_ptt_srpt = vm->vtsi->vts_ptt_srpt; - pgcN = get_PGCN(vm); - pgN = vm->state.pgN; - printf("VTS_PTT_SRPT - PGC: %3i PG: %3i\n", - pgcN, pgN); - - for(ttn=0;( (ttn < vts_ptt_srpt->nr_of_srpts) && (found == 0) );ttn++) { - for(part=0;((part < vts_ptt_srpt->title[ttn].nr_of_ptts) && (found == 0));part++) { - if ( (vts_ptt_srpt->title[ttn].ptt[part].pgcn == pgcN) && - (vts_ptt_srpt->title[ttn].ptt[part].pgn == pgN ) ) { - found = 1; - break; - } - } - if (found != 0) break; - } - ttn++; - part++; - for(title=0; title < vm->vmgi->tt_srpt->nr_of_srpts; title++){ - if( (vm->vmgi->tt_srpt->title[title].vts_ttn == ttn) && - (vm->vmgi->tt_srpt->title[title].title_set_nr == vm->state.vtsN)){ - found = 1; - break; - } - } - title++; - - if (found == 1) { - fprintf(MSG_OUT, "libdvdnav: ************ this chapter FOUND!\n"); - fprintf(MSG_OUT, "libdvdnav: VTS_PTT_SRPT - Title %3i part %3i: PGC: %3i PG: %3i\n", - title, part, - vts_ptt_srpt->title[ttn-1].ptt[part-1].pgcn , - vts_ptt_srpt->title[ttn-1].ptt[part-1].pgn ); - } else { - fprintf(MSG_OUT, "libdvdnav: ************ this chapter NOT FOUND!\n"); - return S_ERR; - } - *title_result = title; - *part_result = part; - return 1; -} - -/* Jump to a particlar part of a particlar title on this vts */ -/* returns S_ERR if not in the VTS_DOMAIN */ -/* FIXME: Should we do some locking here ? */ -int vm_jump_title_part(vm_t *vm, int title, int part) { - link_t link_values; - int vtsN; - - if((!vm) || (!vm->vtsi) || (!vm->vmgi) ) - return S_ERR; - - if(!(vm->state.pgc) ) - return S_ERR; -/* if ( (title < 1) || (title > vm->vtsi->vts_ptt_srpt->nr_of_srpts) || - (part < 1) || (part > vm->vtsi->vts_ptt_srpt->title[title].nr_of_ptts) ) { - return S_ERR; - } - */ - if( (title < 1) || (title > vm->vmgi->tt_srpt->nr_of_srpts) ) { - return S_ERR; - } - vtsN = vm->vmgi->tt_srpt->title[title - 1].title_set_nr; - - if(set_VTS_PTT(vm, vtsN, title, part) == -1) { - return S_ERR; - } - link_values = play_PGC_PG( vm, (vm->state).pgN ); - link_values = process_command(vm,link_values); - assert(link_values.command == PlayThis); - (vm->state).blockN = link_values.data1; - assert( (vm->state).blockN == 0 ); - vm->hop_channel++; - - fprintf(MSG_OUT, "libdvdnav: previous chapter done\n"); - - return 1; -} - -static domain_t menuid2domain(DVDMenuID_t menuid) -{ - domain_t result = VTSM_DOMAIN; /* Really shouldn't have to.. */ - - switch(menuid) { - case DVD_MENU_Title: - result = VMGM_DOMAIN; - break; - case DVD_MENU_Root: - case DVD_MENU_Subpicture: - case DVD_MENU_Audio: - case DVD_MENU_Angle: - case DVD_MENU_Part: - result = VTSM_DOMAIN; - break; - } - - return result; -} - -int vm_menu_call(vm_t *vm, DVDMenuID_t menuid, int block) -{ - domain_t old_domain; - link_t link_values; - - /* Should check if we are allowed/can acces this menu */ - - - /* FIXME XXX $$$ How much state needs to be restored - * when we fail to find a menu? */ - - old_domain = (vm->state).domain; - - switch((vm->state).domain) { - case VTS_DOMAIN: - saveRSMinfo(vm, 0, block); - /* FALL THROUGH */ - case VTSM_DOMAIN: - case VMGM_DOMAIN: - (vm->state).domain = menuid2domain(menuid); - if(get_PGCIT(vm) != NULL && set_MENU(vm, menuid) != -1) { - link_values = play_PGC(vm); - link_values = process_command(vm, link_values); - assert(link_values.command == PlayThis); - (vm->state).blockN = link_values.data1; -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: vm_menu_call: blockN set to 0x%x\n", (vm->state).blockN); -#endif - assert( (vm->state).blockN == 0 ); - return 1; /* Jump */ - } else { - (vm->state).domain = old_domain; - } - break; - case FP_DOMAIN: /* FIXME XXX $$$ What should we do here? */ - break; - } - - return 0; -} - - -int vm_resume(vm_t *vm) -{ - int i; - link_t link_values; - - /* Check and see if there is any rsm info!! */ - if((vm->state).rsm_vtsN == 0) { - return 0; - } - - (vm->state).domain = VTS_DOMAIN; - ifoOpenNewVTSI(vm, vm->dvd, (vm->state).rsm_vtsN); - set_PGC(vm, (vm->state).rsm_pgcN); - - /* These should never be set in SystemSpace and/or MenuSpace */ - /* (vm->state).TTN_REG = (vm->state).rsm_tt; */ - /* (vm->state).TT_PGCN_REG = (vm->state).rsm_pgcN; */ - /* (vm->state).HL_BTNN_REG = (vm->state).rsm_btnn; */ - for(i = 0; i < 5; i++) { - (vm->state).registers.SPRM[4 + i] = (vm->state).rsm_regs[i]; - } - - if((vm->state).rsm_cellN == 0) { - assert((vm->state).cellN); /* Checking if this ever happens */ - (vm->state).pgN = 1; - link_values = play_PG(vm); - link_values = process_command(vm, link_values); - assert(link_values.command == PlayThis); - (vm->state).blockN = link_values.data1; -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: vm_resume1: blockN set to 0x%x\n", (vm->state).blockN); -#endif - assert( (vm->state).blockN == 0 ); - } else { - (vm->state).cellN = (vm->state).rsm_cellN; - (vm->state).blockN = (vm->state).rsm_blockN; -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: vm_resume2: blockN set to 0x%x\n", (vm->state).blockN); -#endif - /* (vm->state).pgN = ?? does this gets the righ value in play_Cell, no! */ - if(set_PGN(vm)) { - /* Were at or past the end of the PGC, should not happen for a RSM */ - assert(0); - play_PGC_post(vm); - } - } - - return 1; /* Jump */ -} - -/** - * Return the substream id for 'logical' audio stream audioN. - * 0 <= audioN < 8 - */ -int vm_get_audio_stream(vm_t *vm, int audioN) -{ - int streamN = -1; -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: vm.c:get_audio_stream audioN=%d\n",audioN); -#endif - if((vm->state).domain == VTSM_DOMAIN - || (vm->state).domain == VMGM_DOMAIN - || (vm->state).domain == FP_DOMAIN) { - audioN = 0; - } - - if(audioN < 8) { - /* Is there any contol info for this logical stream */ - if((vm->state).pgc->audio_control[audioN] & (1<<15)) { - streamN = ((vm->state).pgc->audio_control[audioN] >> 8) & 0x07; - } - } - - if((vm->state).domain == VTSM_DOMAIN - || (vm->state).domain == VMGM_DOMAIN - || (vm->state).domain == FP_DOMAIN) { - if(streamN == -1) - streamN = 0; - } - - /* Should also check in vtsi/vmgi status that what kind of stream - * it is (ac3/lpcm/dts/sdds...) to find the right (sub)stream id */ - return streamN; -} - -/** - * Return the substream id for 'logical' subpicture stream subpN and given mode. - * 0 <= subpN < 32 - * mode == 0 - widescreen - * mode == 1 - letterbox - * mode == 2 - pan&scan - */ -int vm_get_subp_stream(vm_t *vm, int subpN, int mode) -{ - int streamN = -1; - int source_aspect = vm_get_video_aspect(vm); - - if((vm->state).domain == VTSM_DOMAIN - || (vm->state).domain == VMGM_DOMAIN - || (vm->state).domain == FP_DOMAIN) { - subpN = 0; - } - - if(subpN < 32) { /* a valid logical stream */ - /* Is this logical stream present */ - if((vm->state).pgc->subp_control[subpN] & (1<<31)) { - if(source_aspect == 0) /* 4:3 */ - streamN = ((vm->state).pgc->subp_control[subpN] >> 24) & 0x1f; - if(source_aspect == 3) /* 16:9 */ - switch (mode) { - case 0: - streamN = ((vm->state).pgc->subp_control[subpN] >> 16) & 0x1f; - break; - case 1: - streamN = ((vm->state).pgc->subp_control[subpN] >> 8) & 0x1f; - break; - case 2: - streamN = (vm->state).pgc->subp_control[subpN] & 0x1f; - } - } - } - - /* Paranoia.. if no stream select 0 anyway */ -/* I am not paranoid */ -/* if((vm->state).domain == VTSM_DOMAIN - || (vm->state).domain == VMGM_DOMAIN - || (vm->state).domain == FP_DOMAIN) { - if(streamN == -1) - streamN = 0; - } -*/ - /* Should also check in vtsi/vmgi status that what kind of stream it is. */ - return streamN; -} - -int vm_get_subp_active_stream(vm_t *vm, int mode) -{ - int subpN; - int streamN; - subpN = (vm->state).SPST_REG & ~0x40; - streamN = vm_get_subp_stream(vm, subpN, mode); - - /* If no such stream, then select the first one that exists. */ - if(streamN == -1) { - for(subpN = 0; subpN < 32; subpN++) { - if((vm->state).pgc->subp_control[subpN] & (1<<31)) { - - streamN = vm_get_subp_stream(vm, subpN, mode); - break; - } - } - } - - /* We should instead send the on/off status to the spudecoder / mixer */ - /* If we are in the title domain see if the spu mixing is on */ - if((vm->state).domain == VTS_DOMAIN && !((vm->state).SPST_REG & 0x40)) { - /* Bit 7 set means hide, and only let Forced display show */ - return (streamN | 0x80); - } else { - return streamN; - } -} - -int vm_get_audio_active_stream(vm_t *vm) -{ - int audioN; - int streamN; - audioN = (vm->state).AST_REG ; - streamN = vm_get_audio_stream(vm, audioN); - - /* If no such stream, then select the first one that exists. */ - if(streamN == -1) { - for(audioN = 0; audioN < 8; audioN++) { - if((vm->state).pgc->audio_control[audioN] & (1<<15)) { - streamN = vm_get_audio_stream(vm, audioN); - break; - } - } - } - - return streamN; -} - - -void vm_get_angle_info(vm_t *vm, int *num_avail, int *current) -{ - *num_avail = 1; - *current = 1; - - if((vm->state).domain == VTS_DOMAIN) { - /* TTN_REG does not allways point to the correct title.. */ - title_info_t *title; - if((vm->state).TTN_REG > vm->vmgi->tt_srpt->nr_of_srpts) - return; - title = &vm->vmgi->tt_srpt->title[(vm->state).TTN_REG - 1]; - if(title->title_set_nr != (vm->state).vtsN || - title->vts_ttn != (vm->state).VTS_TTN_REG) - return; - *num_avail = title->nr_of_angles; - *current = (vm->state).AGL_REG; - if(*current > *num_avail) /* Is this really a good idea? */ - *current = *num_avail; - } -} - - -void vm_get_audio_info(vm_t *vm, int *num_avail, int *current) -{ - if((vm->state).domain == VTS_DOMAIN) { - *num_avail = vm->vtsi->vtsi_mat->nr_of_vts_audio_streams; - *current = (vm->state).AST_REG; - } else if((vm->state).domain == VTSM_DOMAIN) { - *num_avail = vm->vtsi->vtsi_mat->nr_of_vtsm_audio_streams; /* 1 */ - *current = 1; - } else if((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN) { - *num_avail = vm->vmgi->vmgi_mat->nr_of_vmgm_audio_streams; /* 1 */ - *current = 1; - } -} - -void vm_get_subp_info(vm_t *vm, int *num_avail, int *current) -{ - if((vm->state).domain == VTS_DOMAIN) { - *num_avail = vm->vtsi->vtsi_mat->nr_of_vts_subp_streams; - *current = (vm->state).SPST_REG; - } else if((vm->state).domain == VTSM_DOMAIN) { - *num_avail = vm->vtsi->vtsi_mat->nr_of_vtsm_subp_streams; /* 1 */ - *current = 0x41; - } else if((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN) { - *num_avail = vm->vmgi->vmgi_mat->nr_of_vmgm_subp_streams; /* 1 */ - *current = 0x41; - } -} - -subp_attr_t vm_get_subp_attr(vm_t *vm, int streamN) -{ - subp_attr_t attr; - - if((vm->state).domain == VTS_DOMAIN) { - attr = vm->vtsi->vtsi_mat->vts_subp_attr[streamN]; - } else if((vm->state).domain == VTSM_DOMAIN) { - attr = vm->vtsi->vtsi_mat->vtsm_subp_attr; - } else if((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN) { - attr = vm->vmgi->vmgi_mat->vmgm_subp_attr; - } - return attr; -} - -audio_attr_t vm_get_audio_attr(vm_t *vm, int streamN) -{ - audio_attr_t attr; - - if((vm->state).domain == VTS_DOMAIN) { - attr = vm->vtsi->vtsi_mat->vts_audio_attr[streamN]; - } else if((vm->state).domain == VTSM_DOMAIN) { - attr = vm->vtsi->vtsi_mat->vtsm_audio_attr; - } else if((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN) { - attr = vm->vmgi->vmgi_mat->vmgm_audio_attr; - } - return attr; -} - -video_attr_t vm_get_video_attr(vm_t *vm) -{ - video_attr_t attr; - - if((vm->state).domain == VTS_DOMAIN) { - attr = vm->vtsi->vtsi_mat->vts_video_attr; - } else if((vm->state).domain == VTSM_DOMAIN) { - attr = vm->vtsi->vtsi_mat->vtsm_video_attr; - } else if((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN) { - attr = vm->vmgi->vmgi_mat->vmgm_video_attr; - } - return attr; -} - -void vm_get_video_res(vm_t *vm, int *width, int *height) -{ - video_attr_t attr; - - attr = vm_get_video_attr(vm); - - if(attr.video_format != 0) - *height = 576; - else - *height = 480; - switch(attr.picture_size) { - case 0: - *width = 720; - break; - case 1: - *width = 704; - break; - case 2: - *width = 352; - break; - case 3: - *width = 352; - *height /= 2; - break; - } -} - -/* Must be called before domain is changed (get_PGCN()) */ -static void saveRSMinfo(vm_t *vm, int cellN, int blockN) -{ - int i; - - if(cellN != 0) { - (vm->state).rsm_cellN = cellN; - (vm->state).rsm_blockN = 0; - } else { - (vm->state).rsm_cellN = (vm->state).cellN; - (vm->state).rsm_blockN = blockN; - } - (vm->state).rsm_vtsN = (vm->state).vtsN; - (vm->state).rsm_pgcN = get_PGCN(vm); - - /* assert((vm->state).rsm_pgcN == (vm->state).TT_PGCN_REG); for VTS_DOMAIN */ - - for(i = 0; i < 5; i++) { - (vm->state).rsm_regs[i] = (vm->state).registers.SPRM[4 + i]; + fprintf(MSG_OUT, "NAME OPEN FAILED\n"); } } - - -/* Figure out the correct pgN from the cell and update (vm->state). */ -static int set_PGN(vm_t *vm) { - int new_pgN = 0; - - while(new_pgN < (vm->state).pgc->nr_of_programs - && (vm->state).cellN >= (vm->state).pgc->program_map[new_pgN]) - new_pgN++; - - if(new_pgN == (vm->state).pgc->nr_of_programs) /* We are at the last program */ - if((vm->state).cellN > (vm->state).pgc->nr_of_cells) - return 1; /* We are past the last cell */ - - (vm->state).pgN = new_pgN; - - if((vm->state).domain == VTS_DOMAIN) { - playback_type_t *pb_ty; - if((vm->state).TTN_REG > vm->vmgi->tt_srpt->nr_of_srpts) - return 0; /* ?? */ - pb_ty = &vm->vmgi->tt_srpt->title[(vm->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((vm->state).VTS_TTN_REG <= ptt_srpt->nr_of_srpts); - assert(get_PGCN() == ptt_srpt->title[(vm->state).VTS_TTN_REG - 1].ptt[0].pgcn); - assert(1 == ptt_srpt->title[(vm->state).VTS_TTN_REG - 1].ptt[0].pgn); -#endif - (vm->state).PTTN_REG = (vm->state).pgN; - } else { - /* FIXME: Handle RANDOM or SHUFFLE titles. */ - fprintf(MSG_OUT, "libdvdnav: RANDOM or SHUFFLE titles are NOT handled yet.\n"); - } - - } - - return 0; -} - -static link_t play_PGC(vm_t *vm) -{ - link_t link_values; - -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: vm: play_PGC:"); - if((vm->state).domain != FP_DOMAIN) { - fprintf(MSG_OUT, " (vm->state).pgcN (%i)\n", get_PGCN(vm)); - } else { - fprintf(MSG_OUT, " first_play_pgc\n"); - } -#endif - - /* This must be set before the pre-commands are executed because they */ - /* might contain a CallSS that will save resume state */ - - /* FIXME: This may be only a temporary fix for something... */ - (vm->state).pgN = 1; - (vm->state).cellN = 0; - - /* 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((vm->state).pgc->command_tbl && (vm->state).pgc->command_tbl->nr_of_pre) { - if(vmEval_CMD((vm->state).pgc->command_tbl->pre_cmds, - (vm->state).pgc->command_tbl->nr_of_pre, - &(vm->state).registers, &link_values)) { - /* link_values contains the 'jump' return value */ - return link_values; - } else { -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: PGC pre commands didn't do a Jump, Link or Call\n"); -#endif - } - } - return play_PG(vm); -} - -static link_t play_PGC_PG(vm_t *vm, int pgN) -{ - link_t link_values; - -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: vm: play_PGC:"); - if((vm->state).domain != FP_DOMAIN) { - fprintf(MSG_OUT, " (vm->state).pgcN (%i)\n", get_PGCN(vm)); - } else { - fprintf(MSG_OUT, " first_play_pgc\n"); - } -#endif - - /* This must be set before the pre-commands are executed because they */ - /* might contain a CallSS that will save resume state */ - - /* FIXME: This may be only a temporary fix for something... */ - (vm->state).pgN = pgN; - (vm->state).cellN = 0; - - /* 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((vm->state).pgc->command_tbl && (vm->state).pgc->command_tbl->nr_of_pre) { - if(vmEval_CMD((vm->state).pgc->command_tbl->pre_cmds, - (vm->state).pgc->command_tbl->nr_of_pre, - &(vm->state).registers, &link_values)) { - /* link_values contains the 'jump' return value */ - return link_values; - } else { -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: PGC pre commands didn't do a Jump, Link or Call\n"); -#endif - } - } - return play_PG(vm); -} - -static link_t play_PG(vm_t *vm) -{ -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: play_PG: (vm->state).pgN (%i)\n", (vm->state).pgN); -#endif - - assert((vm->state).pgN > 0); - if((vm->state).pgN > (vm->state).pgc->nr_of_programs) { -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: play_PG: (vm->state).pgN (%i) > pgc->nr_of_programs (%i)\n", - (vm->state).pgN, (vm->state).pgc->nr_of_programs ); -#endif - assert((vm->state).pgN == (vm->state).pgc->nr_of_programs + 1); - return play_PGC_post(vm); - } - - (vm->state).cellN = (vm->state).pgc->program_map[(vm->state).pgN - 1]; - - return play_Cell(vm); -} - - -static link_t play_Cell(vm_t *vm) -{ -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: play_Cell: (vm->state).cellN (%i)\n", (vm->state).cellN); -#endif - - assert((vm->state).cellN > 0); - if((vm->state).cellN > (vm->state).pgc->nr_of_cells) { -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: (vm->state).cellN (%i) > pgc->nr_of_cells (%i)\n", - (vm->state).cellN, (vm->state).pgc->nr_of_cells ); -#endif - assert((vm->state).cellN == (vm->state).pgc->nr_of_cells + 1); - return play_PGC_post(vm); - } - - - /* Multi angle/Interleaved */ - switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode) { - case 0: /* Normal */ - assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 0); - break; - case 1: /* The first cell in the block */ - switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type) { - case 0: /* Not part of a block */ - assert(0); - case 1: /* Angle block */ - /* Loop and check each cell instead? So we don't get outsid the block. */ - (vm->state).cellN += (vm->state).AGL_REG - 1; -#ifdef STRICT - assert((vm->state).cellN <= (vm->state).pgc->nr_of_cells); - assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode != 0); - assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 1); -#endif - if (!((vm->state).cellN <= (vm->state).pgc->nr_of_cells) || - !((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode != 0) || - !((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 1)) { - fprintf(MSG_OUT, "libdvdnav: Invalid angle block\n"); - (vm->state).cellN -= (vm->state).AGL_REG - 1; - } - break; - case 2: /* ?? */ - case 3: /* ?? */ - default: - fprintf(MSG_OUT, "libdvdnav: Invalid? Cell block_mode (%d), block_type (%d)\n", - (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode, - (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type); - } - break; - case 2: /* Cell in the block */ - case 3: /* Last cell in the block */ - /* These might perhaps happen for RSM or LinkC commands? */ - default: - fprintf(MSG_OUT, "libdvdnav: Cell is in block but did not enter at first cell!\n"); - } - - /* Updates (vm->state).pgN and PTTN_REG */ - if(set_PGN(vm)) { - /* Should not happen */ - link_t tmp = {LinkTailPGC, /* No Button */ 0, 0, 0}; - assert(0); - return tmp; - } - (vm->state).cell_restart++; - fprintf(MSG_OUT, "libdvdnav: Cell should restart here\n"); - { - link_t tmp = {PlayThis, /* Block in Cell */ 0, 0, 0}; - return tmp; - } - -} - -static link_t play_Cell_post(vm_t *vm) -{ - cell_playback_t *cell; - -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: play_Cell_post: (vm->state).cellN (%i)\n", (vm->state).cellN); -#endif - - cell = &(vm->state).pgc->cell_playback[(vm->state).cellN - 1]; - - /* Still time is already taken care of before we get called. */ - - /* Deal with a Cell command, if any */ - if(cell->cell_cmd_nr != 0) { - link_t link_values; - -/* These asserts are now not needed. - * Some DVDs have no cell commands listed in the PGC, - * but the Cell itself points to a cell command that does not exist. - * For this situation, just ignore the cell command and continue. - * - * assert((vm->state).pgc->command_tbl != NULL); - * assert((vm->state).pgc->command_tbl->nr_of_cell >= cell->cell_cmd_nr); - */ - - if ((vm->state).pgc->command_tbl != NULL && - (vm->state).pgc->command_tbl->nr_of_cell >= cell->cell_cmd_nr) { -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: Cell command present, executing\n"); -#endif - if(vmEval_CMD(&(vm->state).pgc->command_tbl->cell_cmds[cell->cell_cmd_nr - 1], 1, - &(vm->state).registers, &link_values)) { - return link_values; - } else { - fprintf(MSG_OUT, "libdvdnav: Cell command didn't do a Jump, Link or Call\n"); - /* Error ?? goto tail? goto next PG? or what? just continue? */ - } - } else { - fprintf(MSG_OUT, "libdvdnav: Invalid Cell command\n"); - - } - } - - - /* Where to continue after playing the cell... */ - /* Multi angle/Interleaved */ - switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode) { - case 0: /* Normal */ - assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 0); - (vm->state).cellN++; - break; - case 1: /* The first cell in the block */ - case 2: /* A cell in the block */ - case 3: /* The last cell in the block */ - default: - switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type) { - case 0: /* Not part of a block */ - assert(0); - case 1: /* Angle block */ - /* Skip the 'other' angles */ - (vm->state).cellN++; - while((vm->state).cellN <= (vm->state).pgc->nr_of_cells - && (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode >= 2) { - (vm->state).cellN++; - } - break; - case 2: /* ?? */ - case 3: /* ?? */ - default: - fprintf(MSG_OUT, "libdvdnav: Invalid? Cell block_mode (%d), block_type (%d)\n", - (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode, - (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type); - } - break; - } - - - /* Figure out the correct pgN for the new cell */ - if(set_PGN(vm)) { -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: last cell in this PGC\n"); -#endif - return play_PGC_post(vm); - } - - return play_Cell(vm); -} - - -static link_t play_PGC_post(vm_t *vm) -{ - link_t link_values; - -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: play_PGC_post:\n"); -#endif - - /* FIXME Implement PGC Stills. Currently only Cell stills work */ - assert((vm->state).pgc->still_time == 0); - - /* 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((vm->state).pgc->command_tbl && - vmEval_CMD((vm->state).pgc->command_tbl->post_cmds, - (vm->state).pgc->command_tbl->nr_of_post, - &(vm->state).registers, &link_values)) { - return link_values; - } - - /* Or perhaps handle it here? */ - { - link_t link_next_pgc = {LinkNextPGC, 0, 0, 0}; - fprintf(MSG_OUT, "libdvdnav: ** Fell of the end of the pgc, continuing in NextPGC\n"); - assert((vm->state).pgc->next_pgc_nr != 0); - /* Should end up in the STOP_DOMAIN if next_pgc is 0. */ - return link_next_pgc; - } -} - - -static link_t process_command(vm_t *vm, link_t link_values) -{ - /* FIXME $$$ Move this to a separate function? */ - vm->badness_counter++; - if (vm->badness_counter > 1) fprintf(MSG_OUT, "libdvdnav: **** WARNING: process_command re-entered %d*****\n",vm->badness_counter); - while(link_values.command != PlayThis) { - -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: Before printout starts:\n"); - vmPrint_LINK(link_values); - - fprintf(MSG_OUT, "libdvdnav: Link values %i %i %i %i\n", link_values.command, - link_values.data1, link_values.data2, link_values.data3); - - vm_print_current_domain_state(vm); - fprintf(MSG_OUT, "libdvdnav: Before printout ends.\n"); -#endif - - switch(link_values.command) { - case LinkNoLink: - /* No Link => PlayThis */ - /* BUTTON number:data1 */ - if(link_values.data1 != 0) - (vm->state).HL_BTNN_REG = link_values.data1 << 10; - link_values.command = PlayThis; - link_values.data1 = (vm->state).blockN; - link_values.data2 = 0; /* no actual jump */ - return link_values; - - case LinkTopC: - /* Restart playing from the beginning of the current Cell. */ - /* BUTTON number:data1 */ - fprintf(MSG_OUT, "libdvdnav: FIXME: LinkTopC. Replay current Cell\n"); - if(link_values.data1 != 0) - (vm->state).HL_BTNN_REG = link_values.data1 << 10; - link_values = play_Cell(vm); - break; - case LinkNextC: - /* Link to Next Cell */ - /* BUTTON number:data1 */ - if(link_values.data1 != 0) - (vm->state).HL_BTNN_REG = link_values.data1 << 10; - (vm->state).cellN += 1; /* if cellN becomes > nr_of_cells? it is handled in play_Cell() */ - link_values = play_Cell(vm); - break; - case LinkPrevC: - /* Link to Previous Cell */ - /* BUTTON number:data1 */ - if(link_values.data1 != 0) - (vm->state).HL_BTNN_REG = link_values.data1 << 10; - (vm->state).cellN -= 1; /* If cellN becomes < 1? it is handled in play_Cell() */ - link_values = play_Cell(vm); - break; - - case LinkTopPG: - /* Link to Top Program */ - /* BUTTON number:data1 */ - fprintf(MSG_OUT, "libdvdnav: FIXME: LinkTopPG. This should start the current PG again.\n"); - if(link_values.data1 != 0) - (vm->state).HL_BTNN_REG = link_values.data1 << 10; - /* Does pgN always contain the current value? */ - link_values = play_PG(vm); - break; - case LinkNextPG: - /* Link to Next Program */ - /* BUTTON number:data1 */ - if(link_values.data1 != 0) - (vm->state).HL_BTNN_REG = link_values.data1 << 10; - /* Does pgN always contain the current value? */ - (vm->state).pgN += 1; /* FIXME: What if pgN becomes > pgc.nr_of_programs? */ - link_values = play_PG(vm); - break; - case LinkPrevPG: - /* Link to Previous Program */ - /* BUTTON number:data1 */ - if(link_values.data1 != 0) - (vm->state).HL_BTNN_REG = link_values.data1 << 10; - /* Does pgN always contain the current value? */ - assert((vm->state).pgN > 1); - (vm->state).pgN -= 1; /* FIXME: What if pgN becomes < 1? */ - link_values = play_PG(vm); - break; - - case LinkTopPGC: - /* Restart playing from beginning of current Program Chain */ - /* BUTTON number:data1 */ - fprintf(MSG_OUT, "libdvdnav: FIXME: LinkTopPGC. Restart from beginning of current Program Chain\n"); - if(link_values.data1 != 0) - (vm->state).HL_BTNN_REG = link_values.data1 << 10; - link_values = play_PGC(vm); - break; - case LinkNextPGC: - /* Link to Next Program Chain */ - /* BUTTON number:data1 */ - if(link_values.data1 != 0) - (vm->state).HL_BTNN_REG = link_values.data1 << 10; - assert((vm->state).pgc->next_pgc_nr != 0); - if(set_PGC(vm, (vm->state).pgc->next_pgc_nr)) - assert(0); - link_values = play_PGC(vm); - break; - case LinkPrevPGC: - /* Link to Previous Program Chain */ - /* BUTTON number:data1 */ - if(link_values.data1 != 0) - (vm->state).HL_BTNN_REG = link_values.data1 << 10; - assert((vm->state).pgc->prev_pgc_nr != 0); - if(set_PGC(vm, (vm->state).pgc->prev_pgc_nr)) - assert(0); - link_values = play_PGC(vm); - break; - case LinkGoUpPGC: - /* Link to GoUp??? Program Chain */ - /* BUTTON number:data1 */ - if(link_values.data1 != 0) - (vm->state).HL_BTNN_REG = link_values.data1 << 10; - assert((vm->state).pgc->goup_pgc_nr != 0); - if(set_PGC(vm, (vm->state).pgc->goup_pgc_nr)) - assert(0); - link_values = play_PGC(vm); - break; - case LinkTailPGC: - /* Link to Tail??? Program Chain */ - /* BUTTON number:data1 */ - /* fprintf(MSG_OUT, "libdvdnav: FIXME: LinkTailPGC. What is LinkTailPGC?\n"); */ - if(link_values.data1 != 0) - (vm->state).HL_BTNN_REG = link_values.data1 << 10; - link_values = play_PGC_post(vm); - break; - - case LinkRSM: - { - /* Link to Resume */ - int i; - /* Check and see if there is any rsm info!! */ - (vm->state).domain = VTS_DOMAIN; - ifoOpenNewVTSI(vm, vm->dvd, (vm->state).rsm_vtsN); - set_PGC(vm, (vm->state).rsm_pgcN); - - /* These should never be set in SystemSpace and/or MenuSpace */ - /* (vm->state).TTN_REG = rsm_tt; ?? */ - /* (vm->state).TT_PGCN_REG = (vm->state).rsm_pgcN; ?? */ - for(i = 0; i < 5; i++) { - (vm->state).registers.SPRM[4 + i] = (vm->state).rsm_regs[i]; - } - - if(link_values.data1 != 0) - (vm->state).HL_BTNN_REG = link_values.data1 << 10; - - if((vm->state).rsm_cellN == 0) { - assert((vm->state).cellN); /* Checking if this ever happens */ - /* assert( time/block/vobu is 0 ); */ - (vm->state).pgN = 1; - link_values = play_PG(vm); - } else { - /* assert( time/block/vobu is _not_ 0 ); */ - /* play_Cell_at_time */ - /* (vm->state).pgN = ?? this gets the righ value in play_Cell */ - (vm->state).cellN = (vm->state).rsm_cellN; - link_values.command = PlayThis; - link_values.data1 = (vm->state).rsm_blockN; - if(set_PGN(vm)) { - /* Were at the end of the PGC, should not happen for a RSM */ - assert(0); - link_values.command = LinkTailPGC; - link_values.data1 = 0; /* No button */ - } - } - } - break; - case LinkPGCN: - /* Link to Program Chain Number:data1 */ - if(set_PGC(vm, link_values.data1)) - assert(0); - link_values = play_PGC(vm); - break; - case LinkPTTN: - /* Link to Part of this Title Number:data1 */ - /* BUTTON number:data2 */ - assert((vm->state).domain == VTS_DOMAIN); - if(link_values.data2 != 0) - (vm->state).HL_BTNN_REG = link_values.data2 << 10; - if(set_VTS_PTT(vm, (vm->state).vtsN, (vm->state).VTS_TTN_REG, link_values.data1) == -1) - assert(0); - link_values = play_PG(vm); - break; - case LinkPGN: - /* Link to Program Number:data1 */ - /* BUTTON number:data2 */ - if(link_values.data2 != 0) - (vm->state).HL_BTNN_REG = link_values.data2 << 10; - /* Update any other state, PTTN perhaps? */ - (vm->state).pgN = link_values.data1; - link_values = play_PG(vm); - break; - case LinkCN: - /* Link to Cell Number:data1 */ - /* BUTTON number:data2 */ - if(link_values.data2 != 0) - (vm->state).HL_BTNN_REG = link_values.data2 << 10; - /* Update any other state, pgN, PTTN perhaps? */ - (vm->state).cellN = link_values.data1; - link_values = play_Cell(vm); - break; - - case Exit: - fprintf(MSG_OUT, "libdvdnav: FIXME:in trouble...Link Exit - CRASHING!!!\n"); - assert(0); /* What should we do here?? */ - - case JumpTT: - /* Jump to VTS Title Domain */ - /* Only allowed from the First Play domain(PGC) */ - /* or the Video Manager domain (VMG) */ - assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN); /* ?? */ - if(set_TT(vm,link_values.data1) == -1) - assert(0); - link_values = play_PGC(vm); - break; - case JumpVTS_TT: - /* Jump to Title:data1 in same VTS Title Domain */ - /* Only allowed from the VTS Menu Domain(VTSM) */ - /* or the Video Title Set Domain(VTS) */ - assert((vm->state).domain == VTSM_DOMAIN || (vm->state).domain == VTS_DOMAIN); /* ?? */ - fprintf(MSG_OUT, "libdvdnav: FIXME: Should be able to use get_VTS_PTT here.\n"); - if(set_VTS_TT(vm,(vm->state).vtsN, link_values.data1) == -1) - assert(0); - link_values = play_PGC(vm); - break; - case JumpVTS_PTT: - /* Jump to Part:data2 of Title:data1 in same VTS Title Domain */ - /* Only allowed from the VTS Menu Domain(VTSM) */ - /* or the Video Title Set Domain(VTS) */ - assert((vm->state).domain == VTSM_DOMAIN || (vm->state).domain == VTS_DOMAIN); /* ?? */ - if(set_VTS_PTT(vm,(vm->state).vtsN, link_values.data1, link_values.data2) == -1) - assert(0); - link_values = play_PGC_PG( vm, (vm->state).pgN ); - break; - - case JumpSS_FP: - /* Jump to First Play Domain */ - /* Only allowed from the VTS Menu Domain(VTSM) */ - /* or the Video Manager domain (VMG) */ - assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == VTSM_DOMAIN); /* ?? */ - set_FP_PGC(vm); - link_values = play_PGC(vm); - break; - case JumpSS_VMGM_MENU: - /* Jump to Video Manger domain - Title Menu:data1 or any PGC in VMG */ - /* Allowed from anywhere except the VTS Title domain */ - assert((vm->state).domain == VMGM_DOMAIN || - (vm->state).domain == VTSM_DOMAIN || - (vm->state).domain == FP_DOMAIN); /* ?? */ - (vm->state).domain = VMGM_DOMAIN; - if(set_MENU(vm,link_values.data1) == -1) - assert(0); - link_values = play_PGC(vm); - break; - case JumpSS_VTSM: - /* Jump to a menu in Video Title domain, */ - /* or to a Menu is the current VTS */ - /* FIXME: This goes badly wrong for some DVDs. */ - /* FIXME: Keep in touch with ogle people regarding what to do here */ - /* ifoOpenNewVTSI:data1 */ - /* VTS_TTN_REG:data2 */ - /* get_MENU:data3 */ -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: BUG TRACKING *******************************************************************\n"); - fprintf(MSG_OUT, "libdvdnav: data1=%u data2=%u data3=%u\n", - link_values.data1, - link_values.data2, - link_values.data3); - fprintf(MSG_OUT, "libdvdnav: *******************************************************************\n"); -#endif - - if(link_values.data1 !=0) { - assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN); /* ?? */ - (vm->state).domain = VTSM_DOMAIN; - ifoOpenNewVTSI(vm, vm->dvd, link_values.data1); /* Also sets (vm->state).vtsN */ - } else { - /* This happens on 'The Fifth Element' region 2. */ - assert((vm->state).domain == VTSM_DOMAIN); - } - /* I don't know what title is supposed to be used for. */ - /* Alien or Aliens has this != 1, I think. */ - /* assert(link_values.data2 == 1); */ - (vm->state).VTS_TTN_REG = link_values.data2; - if(set_MENU(vm, link_values.data3) == -1) - assert(0); - link_values = play_PGC(vm); - break; - case JumpSS_VMGM_PGC: - /* get_PGC:data1 */ - assert((vm->state).domain == VMGM_DOMAIN || - (vm->state).domain == VTSM_DOMAIN || - (vm->state).domain == FP_DOMAIN); /* ?? */ - (vm->state).domain = VMGM_DOMAIN; - if(set_PGC(vm,link_values.data1) == -1) - assert(0); - link_values = play_PGC(vm); - break; - - case CallSS_FP: - /* saveRSMinfo:data1 */ - assert((vm->state).domain == VTS_DOMAIN); /* ?? */ - /* Must be called before domain is changed */ - saveRSMinfo(vm, link_values.data1, /* We dont have block info */ 0); - set_FP_PGC(vm); - link_values = play_PGC(vm); - break; - case CallSS_VMGM_MENU: - /* get_MENU:data1 */ - /* saveRSMinfo:data2 */ - assert((vm->state).domain == VTS_DOMAIN); /* ?? */ - /* Must be called before domain is changed */ - saveRSMinfo(vm,link_values.data2, /* We dont have block info */ 0); - (vm->state).domain = VMGM_DOMAIN; - if(set_MENU(vm,link_values.data1) == -1) - assert(0); - link_values = play_PGC(vm); - break; - case CallSS_VTSM: - /* get_MENU:data1 */ - /* saveRSMinfo:data2 */ - assert((vm->state).domain == VTS_DOMAIN); /* ?? */ - /* Must be called before domain is changed */ - saveRSMinfo(vm,link_values.data2, /* We dont have block info */ 0); - (vm->state).domain = VTSM_DOMAIN; - if(set_MENU(vm,link_values.data1) == -1) - assert(0); - link_values = play_PGC(vm); - break; - case CallSS_VMGM_PGC: - /* get_PGC:data1 */ - /* saveRSMinfo:data2 */ - assert((vm->state).domain == VTS_DOMAIN); /* ?? */ - /* Must be called before domain is changed */ - saveRSMinfo(vm,link_values.data2, /* We dont have block info */ 0); - (vm->state).domain = VMGM_DOMAIN; - if(set_PGC(vm,link_values.data1) == -1) - assert(0); - link_values = play_PGC(vm); - break; - case PlayThis: - /* Should never happen. */ - assert(0); - break; - } - -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: After printout starts:\n"); - vm_print_current_domain_state(vm); - fprintf(MSG_OUT, "libdvdnav: After printout ends.\n"); -#endif - - } - link_values.data2 = 1; /* there was actually a jump */ - vm->badness_counter--; - return link_values; -} - -/* Searches the TT tables, to find the current TT. - * returns the current TT. - * returns 0 if not found. - */ -static int get_TT(vm_t *vm, int vtsN, int vts_ttn) { - int i; - int tt=0; - for(i = 1; i <= vm->vmgi->tt_srpt->nr_of_srpts; i++) { - if( vm->vmgi->tt_srpt->title[i - 1].title_set_nr == vtsN && - vm->vmgi->tt_srpt->title[i - 1].vts_ttn == vts_ttn) { - tt=i; - break; - } - } - return tt; -} - -static int set_TT(vm_t *vm, int tt) -{ - assert(tt <= vm->vmgi->tt_srpt->nr_of_srpts); - - (vm->state).TTN_REG = tt; - - return set_VTS_TT(vm, vm->vmgi->tt_srpt->title[tt - 1].title_set_nr, - vm->vmgi->tt_srpt->title[tt - 1].vts_ttn); -} - - -static int set_VTS_TT(vm_t *vm, int vtsN, int vts_ttn) -{ - fprintf(MSG_OUT, "libdvdnav: get_VTS_TT called, testing!!! vtsN=%d, vts_ttn=%d\n", vtsN, vts_ttn); - return set_VTS_PTT(vm, vtsN, vts_ttn, 1); - /* pgcN = get_ID(vm, vts_ttn); This might return -1 */ - /* - assert(pgcN != -1); - - (vm->state).TTN_REG = get_TT(*vm, vtsN, vts_ttn); - (vm->state).VTS_TTN_REG = vts_ttn; - (vm->state).vtsN = - */ - /* Any other registers? */ - - /* return set_PGC(vm, pgcN); */ -} - - -static int set_VTS_PTT(vm_t *vm, int vtsN, int /* is this really */ vts_ttn, int part) -{ - int pgcN, pgN, res; - - (vm->state).domain = VTS_DOMAIN; - if(vtsN != (vm->state).vtsN) - ifoOpenNewVTSI(vm, vm->dvd, vtsN); /* Also sets (vm->state).vtsN */ - - if ((vts_ttn < 1) || (vts_ttn > vm->vtsi->vts_ptt_srpt->nr_of_srpts) || - (part < 1) || (part > vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].nr_of_ptts) ) { - return S_ERR; - } - - pgcN = vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].ptt[part - 1].pgcn; - pgN = vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].ptt[part - 1].pgn; - - (vm->state).TT_PGCN_REG = pgcN; - (vm->state).PTTN_REG = pgN; - - (vm->state).TTN_REG = get_TT(vm, vtsN, vts_ttn); - assert( (vm->state.TTN_REG) != 0 ); - (vm->state).VTS_TTN_REG = vts_ttn; - (vm->state).vtsN = vtsN; /* Not sure about this one. We can get to it easily from TTN_REG */ - /* Any other registers? */ - - res = set_PGC(vm, pgcN); /* This clobber's state.pgN (sets it to 1), but we don't want clobbering here. */ - (vm->state).pgN = pgN; /* Part?? */ - return res; -} - - - -static int set_FP_PGC(vm_t *vm) -{ - (vm->state).domain = FP_DOMAIN; - - (vm->state).pgc = vm->vmgi->first_play_pgc; - - return 0; -} - - -static int set_MENU(vm_t *vm, int menu) -{ - assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == VTSM_DOMAIN); - return set_PGC(vm, get_ID(vm, menu)); -} - -/* Search for entry_id match of the PGC Category in the current VTS PGCIT table. - * Return pgcN based on entry_id match. - */ -static int get_ID(vm_t *vm, int id) -{ - int pgcN, i; - pgcit_t *pgcit; - - /* Relies on state to get the correct pgcit. */ - pgcit = get_PGCIT(vm); - assert(pgcit != NULL); - fprintf(MSG_OUT, "libdvdnav: ** Searching for menu (0x%x) entry PGC\n", id); - - /* Force high bit set. */ - id |=0x80; - /* Get menu/title */ - for(i = 0; i < pgcit->nr_of_pgci_srp; i++) { - if( (pgcit->pgci_srp[i].entry_id) == id) { - pgcN = i + 1; - fprintf(MSG_OUT, "libdvdnav: Found menu.\n"); - return pgcN; - } - } - fprintf(MSG_OUT, "libdvdnav: ** No such id/menu (0x%02x) entry PGC\n", id & 0x7f); - for(i = 0; i < pgcit->nr_of_pgci_srp; i++) { - if ( (pgcit->pgci_srp[i].entry_id & 0x80) == 0x80) { - fprintf(MSG_OUT, "libdvdnav: Available menus: 0x%x\n", - pgcit->pgci_srp[i].entry_id & 0x7f); - } - } - return -1; /* error */ -} - -/* Set the vm->state to pgcN. - * Returns success/failure. - */ -static int set_PGC(vm_t *vm, int pgcN) -{ - /* FIXME: Keep this up to date with the ogle people */ - pgcit_t *pgcit; - - pgcit = get_PGCIT(vm); - - assert(pgcit != NULL); /* ?? Make this return -1 instead */ - if(pgcN < 1 || pgcN > pgcit->nr_of_pgci_srp) { - fprintf(MSG_OUT, "libdvdnav: ** No such pgcN = %d\n", pgcN); - return -1; /* error */ - } - - /* (vm->state).pgcN = pgcN; */ - (vm->state).pgc = pgcit->pgci_srp[pgcN - 1].pgc; - (vm->state).pgN = 1; - - if((vm->state).domain == VTS_DOMAIN) - (vm->state).TT_PGCN_REG = pgcN; - - return 0; -} - -static int get_PGCN(vm_t *vm) -{ - pgcit_t *pgcit; - int pgcN = 1; - - pgcit = get_PGCIT(vm); - - assert(pgcit != NULL); - - while(pgcN <= pgcit->nr_of_pgci_srp) { - if(pgcit->pgci_srp[pgcN - 1].pgc == (vm->state).pgc) - return pgcN; - pgcN++; - } - fprintf(MSG_OUT, "libdvdnav: get_PGCN failed. Trying to find pgcN in domain %d \n", - (vm->state).domain); - /* assert(0);*/ - return -1; /* error */ -} - -int vm_get_video_aspect(vm_t *vm) -{ - int aspect = 0; - - switch ((vm->state).domain) { - case VTS_DOMAIN: - aspect = vm->vtsi->vtsi_mat->vts_video_attr.display_aspect_ratio; - break; - case VTSM_DOMAIN: - aspect = vm->vtsi->vtsi_mat->vtsm_video_attr.display_aspect_ratio; - break; - case VMGM_DOMAIN: - aspect = vm->vmgi->vmgi_mat->vmgm_video_attr.display_aspect_ratio; - break; - default: - fprintf(MSG_OUT, "libdvdnav: vm_get_video_aspect failed. Unknown domain %d\n", - (vm->state).domain); - assert(0); - break; - } -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: get_video_aspect:aspect=%d\n",aspect); -#endif - assert(aspect == 0 || aspect == 3); - (vm->state).registers.SPRM[14] &= ~(0x3 << 10); - (vm->state).registers.SPRM[14] |= aspect << 10; - - return aspect; -} - -int vm_get_video_scale_permission(vm_t *vm) -{ - int permission = 0; - - if((vm->state).domain == VTS_DOMAIN) { - permission = vm->vtsi->vtsi_mat->vts_video_attr.permitted_df; - } else if((vm->state).domain == VTSM_DOMAIN) { - permission = vm->vtsi->vtsi_mat->vtsm_video_attr.permitted_df; - } else if((vm->state).domain == VMGM_DOMAIN) { - permission = vm->vmgi->vmgi_mat->vmgm_video_attr.permitted_df; - } -#ifdef TRACE - fprintf(MSG_OUT, "libdvdnav: get_video_scale_permission:permission=%d\n",permission); -#endif - - return permission; -} - -static void ifoOpenNewVTSI(vm_t *vm, dvd_reader_t *dvd, int vtsN) -{ +static void ifoOpenNewVTSI(vm_t *vm, dvd_reader_t *dvd, int vtsN) { if((vm->state).vtsN == vtsN) { return; /* We alread have it */ } @@ -1985,8 +218,1451 @@ (vm->state).vtsN = vtsN; } -static pgcit_t* get_MENU_PGCIT(vm_t *vm, ifo_handle_t *h, uint16_t lang) + +/* 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 */ + +void vm_start(vm_t *vm) { + /* Set pgc to FP (First Play) pgc */ + set_FP_PGC(vm); + process_command(vm, play_PGC(vm)); +} + +void vm_stop(vm_t *vm) { + if(vm->vmgi) { + ifoClose(vm->vmgi); + vm->vmgi=NULL; + } + if(vm->vtsi) { + ifoClose(vm->vtsi); + vm->vmgi=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, 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; +} + + +/* 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; + + /* still already detrmined or not at PGC end */ + if (position->still || (vm->state).cellN < (vm->state).pgc->nr_of_cells) + return; + /* handle PGC stills */ + if ((vm->state).pgc->still_time) { + position->still = (vm->state).pgc->still_time; + 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 < 250)) { + int time; + time = ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.hour & 0xf0) * 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 & 0xf0) * 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 & 0xf0) * 10; + time += ((vm->state).pgc->cell_playback[(vm->state).cellN - 1].playback_time.second & 0x0f) * 1; + if (time > 0xff) time = 0xff; + position->still = time; + } +} + +void vm_get_next_cell(vm_t *vm) { + process_command(vm, play_Cell_post(vm)); +} + + +/* Jumping */ + +int vm_jump_pg(vm_t *vm, int pg) { + (vm->state).pgN = pg; + process_command(vm, play_PG(vm)); + return 1; +} + +int vm_jump_title_part(vm_t *vm, int title, int part) { + int vtsN; + + vtsN = vm->vmgi->tt_srpt->title[title - 1].title_set_nr; + + if(!set_VTS_PTT(vm, vtsN, title, part)) + return 0; + process_command(vm, play_PGC_PG(vm, (vm->state).pgN)); + vm->hop_channel++; + return 1; +} + +int vm_jump_top_pg(vm_t *vm) { + process_command(vm, play_PG(vm)); + return 1; +} + +int vm_jump_next_pg(vm_t *vm) { + if((vm->state).pgN >= (vm->state).pgc->nr_of_programs) { + /* last program -> move to TailPGC */ + process_command(vm, play_PGC_post(vm)); + return 1; + } else { + vm_jump_pg(vm, (vm->state).pgN + 1); + return 1; + } +} + +int vm_jump_prev_pg(vm_t *vm) { + if ((vm->state).pgN <= 1) { + /* first program -> move to last program of previous PGC */ + if ((vm->state).pgc->prev_pgc_nr && set_PGCN(vm, (vm->state).pgc->prev_pgc_nr)) { + process_command(vm, play_PGC(vm)); + return 1; + } + return 0; + } else { + vm_jump_pg(vm, (vm->state).pgN - 1); + return 1; + } +} + +int vm_jump_up(vm_t *vm) { + if((vm->state).pgc->goup_pgc_nr && set_PGCN(vm, (vm->state).pgc->goup_pgc_nr)) { + process_command(vm, play_PGC(vm)); + return 1; + } + return 0; +} + +int vm_jump_menu(vm_t *vm, DVDMenuID_t menuid) { + domain_t old_domain = (vm->state).domain; + + switch ((vm->state).domain) { + case VTS_DOMAIN: + set_RSMinfo(vm, 0, (vm->state).blockN); + /* FALL THROUGH */ + case VTSM_DOMAIN: + case VMGM_DOMAIN: + switch(menuid) { + case DVD_MENU_Title: + (vm->state).domain = VMGM_DOMAIN; + break; + case DVD_MENU_Root: + case DVD_MENU_Subpicture: + case DVD_MENU_Audio: + case DVD_MENU_Angle: + case DVD_MENU_Part: + (vm->state).domain = VTSM_DOMAIN; + break; + } + if(get_PGCIT(vm) && set_MENU(vm, menuid)) { + process_command(vm, play_PGC(vm)); + return 1; /* Jump */ + } else { + (vm->state).domain = old_domain; + } + break; + case FP_DOMAIN: /* FIXME XXX $$$ What should we do here? */ + break; + } + + return 0; +} + +int vm_exec_cmd(vm_t *vm, vm_cmd_t *cmd) { + link_t link_values; + + if(vmEval_CMD(cmd, 1, &(vm->state).registers, &link_values)) + return process_command(vm, link_values); + else + return 0; /* It updated some state thats all... */ +} + + +/* getting information */ + +int vm_get_current_title_part(vm_t *vm, int *title_result, int *part_result) { + vts_ptt_srpt_t *vts_ptt_srpt; + int title, part = 0, vts_ttn; + int found; + int16_t pgcN, pgN; + + vts_ptt_srpt = vm->vtsi->vts_ptt_srpt; + pgcN = get_PGCN(vm); + pgN = vm->state.pgN; + + found = 0; + for (vts_ttn = 0; (vts_ttn < vts_ptt_srpt->nr_of_srpts) && !found; vts_ttn++) { + for (part = 0; (part < vts_ptt_srpt->title[vts_ttn].nr_of_ptts) && !found; part++) { + if ((vts_ptt_srpt->title[vts_ttn].ptt[part].pgcn == pgcN) && + (vts_ptt_srpt->title[vts_ttn].ptt[part].pgn == pgN )) { + found = 1; + break; + } + } + if (found) break; + } + vts_ttn++; + part++; + + title = get_TT(vm, vm->state.vtsN, vts_ttn); + +#ifdef TRACE + if (title) { + fprintf(MSG_OUT, "libdvdnav: ************ this chapter FOUND!\n"); + fprintf(MSG_OUT, "libdvdnav: VTS_PTT_SRPT - Title %3i part %3i: PGC: %3i PG: %3i\n", + title, part, + vts_ptt_srpt->title[ttn-1].ptt[part-1].pgcn , + vts_ptt_srpt->title[ttn-1].ptt[part-1].pgn ); + } else { + fprintf(MSG_OUT, "libdvdnav: ************ this chapter NOT FOUND!\n"); + } +#endif + if (!title) + return 0; + *title_result = title; + *part_result = part; + return 1; +} + +/* Return the substream id for 'logical' audio stream audioN. + * 0 <= audioN < 8 + */ +int vm_get_audio_stream(vm_t *vm, int audioN) { + int streamN = -1; + +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: vm.c:get_audio_stream audioN=%d\n",audioN); +#endif + + if((vm->state).domain != VTS_DOMAIN) + audioN = 0; + + if(audioN < 8) { + /* Is there any contol info for this logical stream */ + if((vm->state).pgc->audio_control[audioN] & (1<<15)) { + streamN = ((vm->state).pgc->audio_control[audioN] >> 8) & 0x07; + } + } + + if((vm->state).domain != VTS_DOMAIN && streamN == -1) + streamN = 0; + + /* FIXME: Should also check in vtsi/vmgi status what kind of stream + * it is (ac3/lpcm/dts/sdds...) to find the right (sub)stream id */ + return streamN; +} + +/* Return the substream id for 'logical' subpicture stream subpN and given mode. + * 0 <= subpN < 32 + * mode == 0 - widescreen + * mode == 1 - letterbox + * mode == 2 - pan&scan + */ +int vm_get_subp_stream(vm_t *vm, int subpN, int mode) { + int streamN = -1; + int source_aspect = vm_get_video_aspect(vm); + + if((vm->state).domain != VTS_DOMAIN) + subpN = 0; + + if(subpN < 32) { /* a valid logical stream */ + /* Is this logical stream present */ + if((vm->state).pgc->subp_control[subpN] & (1<<31)) { + if(source_aspect == 0) /* 4:3 */ + streamN = ((vm->state).pgc->subp_control[subpN] >> 24) & 0x1f; + if(source_aspect == 3) /* 16:9 */ + switch (mode) { + case 0: + streamN = ((vm->state).pgc->subp_control[subpN] >> 16) & 0x1f; + break; + case 1: + streamN = ((vm->state).pgc->subp_control[subpN] >> 8) & 0x1f; + break; + case 2: + streamN = (vm->state).pgc->subp_control[subpN] & 0x1f; + } + } + } + + if((vm->state).domain != VTS_DOMAIN && streamN == -1) + streamN = 0; + + /* FIXME: Should also check in vtsi/vmgi status what kind of stream it is. */ + return streamN; +} + +int vm_get_audio_active_stream(vm_t *vm) { + int audioN; + int streamN; + audioN = (vm->state).AST_REG ; + streamN = vm_get_audio_stream(vm, audioN); + + /* If no such stream, then select the first one that exists. */ + if(streamN == -1) { + for(audioN = 0; audioN < 8; audioN++) { + if((vm->state).pgc->audio_control[audioN] & (1<<15)) { + if ((streamN = vm_get_audio_stream(vm, audioN)) >= 0) + break; + } + } + } + + return streamN; +} + +int vm_get_subp_active_stream(vm_t *vm, int mode) { + int subpN; + int streamN; + subpN = (vm->state).SPST_REG & ~0x40; + streamN = vm_get_subp_stream(vm, subpN, mode); + + /* If no such stream, then select the first one that exists. */ + if(streamN == -1) { + for(subpN = 0; subpN < 32; subpN++) { + if((vm->state).pgc->subp_control[subpN] & (1<<31)) { + if ((streamN = vm_get_subp_stream(vm, subpN, mode)) >= 0) + break; + } + } + } + + if((vm->state).domain == VTS_DOMAIN && !((vm->state).SPST_REG & 0x40)) + /* Bit 7 set means hide, and only let Forced display show */ + return (streamN | 0x80); + else + return streamN; +} + +void vm_get_angle_info(vm_t *vm, int *current, int *num_avail) { + *num_avail = 1; + *current = 1; + + if((vm->state).domain == VTS_DOMAIN) { + title_info_t *title; + /* TTN_REG does not allways point to the correct title.. */ + if((vm->state).TTN_REG > vm->vmgi->tt_srpt->nr_of_srpts) + return; + title = &vm->vmgi->tt_srpt->title[(vm->state).TTN_REG - 1]; + if(title->title_set_nr != (vm->state).vtsN || + title->vts_ttn != (vm->state).VTS_TTN_REG) + return; + *num_avail = title->nr_of_angles; + *current = (vm->state).AGL_REG; + } +} + +#if 0 +/* currently unused */ +void vm_get_audio_info(vm_t *vm, int *current, int *num_avail) { + switch ((vm->state).domain) { + case VTS_DOMAIN: + *num_avail = vm->vtsi->vtsi_mat->nr_of_vts_audio_streams; + *current = (vm->state).AST_REG; + break; + case VTSM_DOMAIN: + *num_avail = vm->vtsi->vtsi_mat->nr_of_vtsm_audio_streams; /* 1 */ + *current = 1; + break; + case VMGM_DOMAIN: + case FP_DOMAIN: + *num_avail = vm->vmgi->vmgi_mat->nr_of_vmgm_audio_streams; /* 1 */ + *current = 1; + break; + } +} + +/* currently unused */ +void vm_get_subp_info(vm_t *vm, int *current, int *num_avail) { + switch ((vm->state).domain) { + case VTS_DOMAIN: + *num_avail = vm->vtsi->vtsi_mat->nr_of_vts_subp_streams; + *current = (vm->state).SPST_REG; + break; + case VTSM_DOMAIN: + *num_avail = vm->vtsi->vtsi_mat->nr_of_vtsm_subp_streams; /* 1 */ + *current = 0x41; + break; + case VMGM_DOMAIN: + case FP_DOMAIN: + *num_avail = vm->vmgi->vmgi_mat->nr_of_vmgm_subp_streams; /* 1 */ + *current = 0x41; + break; + } +} + +/* currently unused */ +void vm_get_video_res(vm_t *vm, int *width, int *height) { + video_attr_t attr = vm_get_video_attr(vm); + + if(attr.video_format != 0) + *height = 576; + else + *height = 480; + switch(attr.picture_size) { + case 0: + *width = 720; + break; + case 1: + *width = 704; + break; + case 2: + *width = 352; + break; + case 3: + *width = 352; + *height /= 2; + break; + } +} +#endif + +int vm_get_video_aspect(vm_t *vm) { + int aspect = vm_get_video_attr(vm).display_aspect_ratio; + + assert(aspect == 0 || aspect == 3); + (vm->state).registers.SPRM[14] &= ~(0x3 << 10); + (vm->state).registers.SPRM[14] |= aspect << 10; + + return aspect; +} + +int vm_get_video_scale_permission(vm_t *vm) { + return vm_get_video_attr(vm).permitted_df; +} + +video_attr_t vm_get_video_attr(vm_t *vm) { + switch ((vm->state).domain) { + case VTS_DOMAIN: + return vm->vtsi->vtsi_mat->vts_video_attr; + case VTSM_DOMAIN: + return vm->vtsi->vtsi_mat->vtsm_video_attr; + case VMGM_DOMAIN: + case FP_DOMAIN: + return vm->vmgi->vmgi_mat->vmgm_video_attr; + } + assert(0); +} + +audio_attr_t vm_get_audio_attr(vm_t *vm, int streamN) { + switch ((vm->state).domain) { + case VTS_DOMAIN: + return vm->vtsi->vtsi_mat->vts_audio_attr[streamN]; + case VTSM_DOMAIN: + return vm->vtsi->vtsi_mat->vtsm_audio_attr; + case VMGM_DOMAIN: + case FP_DOMAIN: + return vm->vmgi->vmgi_mat->vmgm_audio_attr; + } + assert(0); +} + +subp_attr_t vm_get_subp_attr(vm_t *vm, int streamN) { + switch ((vm->state).domain) { + case VTS_DOMAIN: + return vm->vtsi->vtsi_mat->vts_subp_attr[streamN]; + case VTSM_DOMAIN: + return vm->vtsi->vtsi_mat->vtsm_subp_attr; + case VMGM_DOMAIN: + case FP_DOMAIN: + return vm->vmgi->vmgi_mat->vmgm_subp_attr; + } + assert(0); +} + + +/* Playback control */ + +static link_t play_PGC(vm_t *vm) { + link_t link_values; + +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: vm: play_PGC:"); + if((vm->state).domain != FP_DOMAIN) { + fprintf(MSG_OUT, " (vm->state).pgcN (%i)\n", get_PGCN(vm)); + } else { + fprintf(MSG_OUT, " first_play_pgc\n"); + } +#endif + + /* This must be set before the pre-commands are executed because they + * might contain a CallSS that will save resume state */ + + /* FIXME: This may be only a temporary fix for something... */ + (vm->state).pgN = 1; + (vm->state).cellN = 0; + (vm->state).blockN = 0; + + /* 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 an error (are there more cases?) */ + if((vm->state).pgc->command_tbl && (vm->state).pgc->command_tbl->nr_of_pre) { + if(vmEval_CMD((vm->state).pgc->command_tbl->pre_cmds, + (vm->state).pgc->command_tbl->nr_of_pre, + &(vm->state).registers, &link_values)) { + /* link_values contains the 'jump' return value */ + return link_values; + } else { +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: PGC pre commands didn't do a Jump, Link or Call\n"); +#endif + } + } + return play_PG(vm); +} + +static link_t play_PGC_PG(vm_t *vm, int pgN) { + link_t link_values; + +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: vm: play_PGC:"); + if((vm->state).domain != FP_DOMAIN) { + fprintf(MSG_OUT, " (vm->state).pgcN (%i)\n", get_PGCN(vm)); + } else { + fprintf(MSG_OUT, " first_play_pgc\n"); + } +#endif + + /* This must be set before the pre-commands are executed because they + * might contain a CallSS that will save resume state */ + + /* FIXME: This may be only a temporary fix for something... */ + (vm->state).pgN = pgN; + (vm->state).cellN = 0; + (vm->state).blockN = 0; + + /* 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 an error (are there more cases?) */ + if((vm->state).pgc->command_tbl && (vm->state).pgc->command_tbl->nr_of_pre) { + if(vmEval_CMD((vm->state).pgc->command_tbl->pre_cmds, + (vm->state).pgc->command_tbl->nr_of_pre, + &(vm->state).registers, &link_values)) { + /* link_values contains the 'jump' return value */ + return link_values; + } else { +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: PGC pre commands didn't do a Jump, Link or Call\n"); +#endif + } + } + return play_PG(vm); +} + +static link_t play_PGC_post(vm_t *vm) { + link_t link_values; + +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: play_PGC_post:\n"); +#endif + + /* FIXME: Implement PGC Stills. Currently only Cell stills work */ + assert((vm->state).pgc->still_time == 0); + + /* eval -> updates the state and returns either + - some kind of jump (Jump(TT/SS/VTS_TTN/CallSS/link C/PG/PGC/PTTN) + - just go to next PGC + (This is what happens if you fall of the end of the post_cmds) + - or an error (are there more cases?) */ + if((vm->state).pgc->command_tbl && + vmEval_CMD((vm->state).pgc->command_tbl->post_cmds, + (vm->state).pgc->command_tbl->nr_of_post, + &(vm->state).registers, &link_values)) { + return link_values; + } + +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: ** Fell of the end of the pgc, continuing in NextPGC\n"); +#endif + assert((vm->state).pgc->next_pgc_nr != 0); + /* Should end up in the STOP_DOMAIN if next_pgc is 0. */ + if(!set_PGCN(vm, (vm->state).pgc->next_pgc_nr)) + assert(0); + return play_PGC(vm); +} + +static link_t play_PG(vm_t *vm) { +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: play_PG: (vm->state).pgN (%i)\n", (vm->state).pgN); +#endif + + assert((vm->state).pgN > 0); + if((vm->state).pgN > (vm->state).pgc->nr_of_programs) { +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: play_PG: (vm->state).pgN (%i) > pgc->nr_of_programs (%i)\n", + (vm->state).pgN, (vm->state).pgc->nr_of_programs ); +#endif + assert((vm->state).pgN == (vm->state).pgc->nr_of_programs + 1); + return play_PGC_post(vm); + } + + (vm->state).cellN = (vm->state).pgc->program_map[(vm->state).pgN - 1]; + + return play_Cell(vm); +} + +static link_t play_Cell(vm_t *vm) { + static const link_t play_this = {PlayThis, /* Block in Cell */ 0, 0, 0}; + +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: play_Cell: (vm->state).cellN (%i)\n", (vm->state).cellN); +#endif + + assert((vm->state).cellN > 0); + if((vm->state).cellN > (vm->state).pgc->nr_of_cells) { +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: (vm->state).cellN (%i) > pgc->nr_of_cells (%i)\n", + (vm->state).cellN, (vm->state).pgc->nr_of_cells ); +#endif + assert((vm->state).cellN == (vm->state).pgc->nr_of_cells + 1); + return play_PGC_post(vm); + } + + /* Multi angle/Interleaved */ + switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode) { + case 0: /* Normal */ + assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 0); + break; + case 1: /* The first cell in the block */ + switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type) { + case 0: /* Not part of a block */ + assert(0); + case 1: /* Angle block */ + /* Loop and check each cell instead? So we don't get outside the block? */ + (vm->state).cellN += (vm->state).AGL_REG - 1; +#ifdef STRICT + assert((vm->state).cellN <= (vm->state).pgc->nr_of_cells); + assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode != 0); + assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 1); +#else + if (!((vm->state).cellN <= (vm->state).pgc->nr_of_cells) || + !((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode != 0) || + !((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 1)) { + fprintf(MSG_OUT, "libdvdnav: Invalid angle block\n"); + (vm->state).cellN -= (vm->state).AGL_REG - 1; + } +#endif + break; + case 2: /* ?? */ + case 3: /* ?? */ + default: + fprintf(MSG_OUT, "libdvdnav: Invalid? Cell block_mode (%d), block_type (%d)\n", + (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode, + (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type); + assert(0); + } + break; + case 2: /* Cell in the block */ + case 3: /* Last cell in the block */ + /* These might perhaps happen for RSM or LinkC commands? */ + default: + fprintf(MSG_OUT, "libdvdnav: Cell is in block but did not enter at first cell!\n"); + } + + /* Updates (vm->state).pgN and PTTN_REG */ + if(!set_PGN(vm)) { + /* Should not happen */ + assert(0); + return play_PGC_post(vm); + } + (vm->state).cell_restart++; + (vm->state).blockN = 0; +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: Cell should restart here\n"); +#endif + return play_this; +} + +static link_t play_Cell_post(vm_t *vm) { + cell_playback_t *cell; + +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: play_Cell_post: (vm->state).cellN (%i)\n", (vm->state).cellN); +#endif + + cell = &(vm->state).pgc->cell_playback[(vm->state).cellN - 1]; + + /* Still time is already taken care of before we get called. */ + + /* Deal with a Cell command, if any */ + if(cell->cell_cmd_nr != 0) { + link_t link_values; + +/* These asserts are now not needed. + * Some DVDs have no cell commands listed in the PGC, + * but the Cell itself points to a cell command that does not exist. + * For this situation, just ignore the cell command and continue. + * + * assert((vm->state).pgc->command_tbl != NULL); + * assert((vm->state).pgc->command_tbl->nr_of_cell >= cell->cell_cmd_nr); + */ + + if ((vm->state).pgc->command_tbl != NULL && + (vm->state).pgc->command_tbl->nr_of_cell >= cell->cell_cmd_nr) { +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: Cell command present, executing\n"); +#endif + if(vmEval_CMD(&(vm->state).pgc->command_tbl->cell_cmds[cell->cell_cmd_nr - 1], 1, + &(vm->state).registers, &link_values)) { + return link_values; + } else { +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: Cell command didn't do a Jump, Link or Call\n"); +#endif + } + } else { +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: Invalid Cell command\n"); +#endif + } + } + + /* Where to continue after playing the cell... */ + /* Multi angle/Interleaved */ + switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode) { + case 0: /* Normal */ + assert((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type == 0); + (vm->state).cellN++; + break; + case 1: /* The first cell in the block */ + case 2: /* A cell in the block */ + case 3: /* The last cell in the block */ + default: + switch((vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type) { + case 0: /* Not part of a block */ + assert(0); + case 1: /* Angle block */ + /* Skip the 'other' angles */ + (vm->state).cellN++; + while((vm->state).cellN <= (vm->state).pgc->nr_of_cells && + (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode >= 2) { + (vm->state).cellN++; + } + break; + case 2: /* ?? */ + case 3: /* ?? */ + default: + fprintf(MSG_OUT, "libdvdnav: Invalid? Cell block_mode (%d), block_type (%d)\n", + (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_mode, + (vm->state).pgc->cell_playback[(vm->state).cellN - 1].block_type); + assert(0); + } + break; + } + + /* Figure out the correct pgN for the new cell */ + if(!set_PGN(vm)) { +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: last cell in this PGC\n"); +#endif + return play_PGC_post(vm); + } + return play_Cell(vm); +} + + +/* link processing */ + +static int process_command(vm_t *vm, link_t link_values) { + + while(link_values.command != PlayThis) { + +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: Before printout starts:\n"); + vmPrint_LINK(link_values); + fprintf(MSG_OUT, "libdvdnav: Link values %i %i %i %i\n", link_values.command, + link_values.data1, link_values.data2, link_values.data3); + vm_print_current_domain_state(vm); + fprintf(MSG_OUT, "libdvdnav: Before printout ends.\n"); +#endif + + switch(link_values.command) { + case LinkNoLink: + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + (vm->state).HL_BTNN_REG = link_values.data1 << 10; + return 0; /* no actual jump */ + + case LinkTopC: + /* Restart playing from the beginning of the current Cell. */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + (vm->state).HL_BTNN_REG = link_values.data1 << 10; + link_values = play_Cell(vm); + break; + case LinkNextC: + /* Link to Next Cell */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + (vm->state).HL_BTNN_REG = link_values.data1 << 10; + assert((vm->state).cellN > 1); + (vm->state).cellN += 1; + link_values = play_Cell(vm); + break; + case LinkPrevC: + /* Link to Previous Cell */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + (vm->state).HL_BTNN_REG = link_values.data1 << 10; + (vm->state).cellN -= 1; + link_values = play_Cell(vm); + break; + + case LinkTopPG: + /* Link to Top of Program */ + /* BUTTON number:data1 */ + fprintf(MSG_OUT, "libdvdnav: FIXME: LinkTopPG. This should start the current PG again.\n"); + if(link_values.data1 != 0) + (vm->state).HL_BTNN_REG = link_values.data1 << 10; + link_values = play_PG(vm); + break; + case LinkNextPG: + /* Link to Next Program */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + (vm->state).HL_BTNN_REG = link_values.data1 << 10; + (vm->state).pgN += 1; + link_values = play_PG(vm); + break; + case LinkPrevPG: + /* Link to Previous Program */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + (vm->state).HL_BTNN_REG = link_values.data1 << 10; + assert((vm->state).pgN > 1); + (vm->state).pgN -= 1; + link_values = play_PG(vm); + break; + + case LinkTopPGC: + /* Restart playing from beginning of current Program Chain */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + (vm->state).HL_BTNN_REG = link_values.data1 << 10; + link_values = play_PGC(vm); + break; + case LinkNextPGC: + /* Link to Next Program Chain */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + (vm->state).HL_BTNN_REG = link_values.data1 << 10; + assert((vm->state).pgc->next_pgc_nr != 0); + if(!set_PGCN(vm, (vm->state).pgc->next_pgc_nr)) + assert(0); + link_values = play_PGC(vm); + break; + case LinkPrevPGC: + /* Link to Previous Program Chain */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + (vm->state).HL_BTNN_REG = link_values.data1 << 10; + assert((vm->state).pgc->prev_pgc_nr != 0); + if(!set_PGCN(vm, (vm->state).pgc->prev_pgc_nr)) + assert(0); + link_values = play_PGC(vm); + break; + case LinkGoUpPGC: + /* Link to GoUp Program Chain */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + (vm->state).HL_BTNN_REG = link_values.data1 << 10; + assert((vm->state).pgc->goup_pgc_nr != 0); + if(!set_PGCN(vm, (vm->state).pgc->goup_pgc_nr)) + assert(0); + link_values = play_PGC(vm); + break; + case LinkTailPGC: + /* Link to Tail of Program Chain */ + /* BUTTON number:data1 */ + if(link_values.data1 != 0) + (vm->state).HL_BTNN_REG = link_values.data1 << 10; + link_values = play_PGC_post(vm); + break; + + case LinkRSM: + { + /* Link to Resume point */ + int i; + + /* Check and see if there is any rsm info!! */ + assert((vm->state).rsm_vtsN); + (vm->state).domain = VTS_DOMAIN; + ifoOpenNewVTSI(vm, vm->dvd, (vm->state).rsm_vtsN); + set_PGCN(vm, (vm->state).rsm_pgcN); + + /* These should never be set in SystemSpace and/or MenuSpace */ + /* (vm->state).TTN_REG = rsm_tt; ?? */ + /* (vm->state).TT_PGCN_REG = (vm->state).rsm_pgcN; ?? */ + for(i = 0; i < 5; i++) { + (vm->state).registers.SPRM[4 + i] = (vm->state).rsm_regs[i]; + } + + if(link_values.data1 != 0) + (vm->state).HL_BTNN_REG = link_values.data1 << 10; + + if((vm->state).rsm_cellN == 0) { + assert((vm->state).cellN); /* Checking if this ever happens */ + (vm->state).pgN = 1; + link_values = play_PG(vm); + } else { + /* (vm->state).pgN = ?? this gets the righ value in set_PGN() below */ + (vm->state).cellN = (vm->state).rsm_cellN; + link_values.command = PlayThis; + link_values.data1 = (vm->state).rsm_blockN; + if(!set_PGN(vm)) { + /* Were at the end of the PGC, should not happen for a RSM */ + assert(0); + link_values.command = LinkTailPGC; + link_values.data1 = 0; /* No button */ + } + } + } + break; + case LinkPGCN: + /* Link to Program Chain Number:data1 */ + if(!set_PGCN(vm, link_values.data1)) + assert(0); + link_values = play_PGC(vm); + break; + case LinkPTTN: + /* Link to Part of current Title Number:data1 */ + /* BUTTON number:data2 */ + assert((vm->state).domain == VTS_DOMAIN); + if(link_values.data2 != 0) + (vm->state).HL_BTNN_REG = link_values.data2 << 10; + if(!set_VTS_PTT(vm, (vm->state).vtsN, (vm->state).VTS_TTN_REG, link_values.data1)) + assert(0); + link_values = play_PG(vm); + break; + case LinkPGN: + /* Link to Program Number:data1 */ + /* BUTTON number:data2 */ + if(link_values.data2 != 0) + (vm->state).HL_BTNN_REG = link_values.data2 << 10; + /* Update any other state, PTTN perhaps? */ + (vm->state).pgN = link_values.data1; + link_values = play_PG(vm); + break; + case LinkCN: + /* Link to Cell Number:data1 */ + /* BUTTON number:data2 */ + if(link_values.data2 != 0) + (vm->state).HL_BTNN_REG = link_values.data2 << 10; + /* Update any other state, pgN, PTTN perhaps? */ + (vm->state).cellN = link_values.data1; + link_values = play_Cell(vm); + break; + + case Exit: + fprintf(MSG_OUT, "libdvdnav: FIXME:in trouble...Link Exit - CRASHING!!!\n"); + assert(0); /* What should we do here?? */ + + case JumpTT: + /* Jump to VTS Title Domain */ + /* Only allowed from the First Play domain(PGC) */ + /* or the Video Manager domain (VMG) */ + assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN); /* ?? */ + if(!set_TT(vm, link_values.data1)) + assert(0); + link_values = play_PGC(vm); + break; + case JumpVTS_TT: + /* Jump to Title:data1 in same VTS Title Domain */ + /* Only allowed from the VTS Menu Domain(VTSM) */ + /* or the Video Title Set Domain(VTS) */ + assert((vm->state).domain == VTSM_DOMAIN || (vm->state).domain == VTS_DOMAIN); /* ?? */ + if(!set_VTS_TT(vm, (vm->state).vtsN, link_values.data1)) + assert(0); + link_values = play_PGC(vm); + break; + case JumpVTS_PTT: + /* Jump to Part:data2 of Title:data1 in same VTS Title Domain */ + /* Only allowed from the VTS Menu Domain(VTSM) */ + /* or the Video Title Set Domain(VTS) */ + assert((vm->state).domain == VTSM_DOMAIN || (vm->state).domain == VTS_DOMAIN); /* ?? */ + if(!set_VTS_PTT(vm, (vm->state).vtsN, link_values.data1, link_values.data2)) + assert(0); + link_values = play_PGC_PG(vm, (vm->state).pgN); + break; + + case JumpSS_FP: + /* Jump to First Play Domain */ + /* Only allowed from the VTS Menu Domain(VTSM) */ + /* or the Video Manager domain (VMG) */ + assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == VTSM_DOMAIN); /* ?? */ + if (!set_FP_PGC(vm)) + assert(0); + link_values = play_PGC(vm); + break; + case JumpSS_VMGM_MENU: + /* Jump to Video Manger domain - Title Menu:data1 or any PGC in VMG */ + /* Allowed from anywhere except the VTS Title domain */ + assert((vm->state).domain != VTS_DOMAIN); /* ?? */ + (vm->state).domain = VMGM_DOMAIN; + if(!set_MENU(vm, link_values.data1)) + assert(0); + link_values = play_PGC(vm); + break; + case JumpSS_VTSM: + /* Jump to a menu in Video Title domain, */ + /* or to a Menu is the current VTS */ + /* FIXME: This goes badly wrong for some DVDs. */ + /* FIXME: Keep in touch with ogle people regarding what to do here */ + /* ifoOpenNewVTSI:data1 */ + /* VTS_TTN_REG:data2 */ + /* get_MENU:data3 */ +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: BUG TRACKING *******************************************************************\n"); + fprintf(MSG_OUT, "libdvdnav: data1=%u data2=%u data3=%u\n", + link_values.data1, + link_values.data2, + link_values.data3); + fprintf(MSG_OUT, "libdvdnav: *******************************************************************\n"); +#endif + + if(link_values.data1 != 0) { + assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == FP_DOMAIN); /* ?? */ + (vm->state).domain = VTSM_DOMAIN; + ifoOpenNewVTSI(vm, vm->dvd, link_values.data1); /* Also sets (vm->state).vtsN */ + } else { + /* This happens on 'The Fifth Element' region 2. */ + assert((vm->state).domain == VTSM_DOMAIN); + } + /* I don't know what title is supposed to be used for. */ + /* Alien or Aliens has this != 1, I think. */ + /* assert(link_values.data2 == 1); */ + (vm->state).VTS_TTN_REG = link_values.data2; + if(!set_MENU(vm, link_values.data3)) + assert(0); + link_values = play_PGC(vm); + break; + case JumpSS_VMGM_PGC: + /* set_PGCN:data1 */ + assert((vm->state).domain != VTS_DOMAIN); /* ?? */ + (vm->state).domain = VMGM_DOMAIN; + if(!set_PGCN(vm, link_values.data1)) + assert(0); + link_values = play_PGC(vm); + break; + + case CallSS_FP: + /* set_RSMinfo:data1 */ + assert((vm->state).domain == VTS_DOMAIN); /* ?? */ + /* Must be called before domain is changed */ + set_RSMinfo(vm, link_values.data1, /* We dont have block info */ 0); + set_FP_PGC(vm); + link_values = play_PGC(vm); + break; + case CallSS_VMGM_MENU: + /* set_MENU:data1 */ + /* set_RSMinfo:data2 */ + assert((vm->state).domain == VTS_DOMAIN); /* ?? */ + /* Must be called before domain is changed */ + set_RSMinfo(vm, link_values.data2, /* We dont have block info */ 0); + (vm->state).domain = VMGM_DOMAIN; + if(!set_MENU(vm, link_values.data1)) + assert(0); + link_values = play_PGC(vm); + break; + case CallSS_VTSM: + /* set_MENU:data1 */ + /* set_RSMinfo:data2 */ + assert((vm->state).domain == VTS_DOMAIN); /* ?? */ + /* Must be called before domain is changed */ + set_RSMinfo(vm, link_values.data2, /* We dont have block info */ 0); + (vm->state).domain = VTSM_DOMAIN; + if(!set_MENU(vm, link_values.data1)) + assert(0); + link_values = play_PGC(vm); + break; + case CallSS_VMGM_PGC: + /* set_PGC:data1 */ + /* set_RSMinfo:data2 */ + assert((vm->state).domain == VTS_DOMAIN); /* ?? */ + /* Must be called before domain is changed */ + set_RSMinfo(vm, link_values.data2, /* We dont have block info */ 0); + (vm->state).domain = VMGM_DOMAIN; + if(!set_PGCN(vm, link_values.data1)) + assert(0); + link_values = play_PGC(vm); + break; + case PlayThis: + /* Should never happen. */ + assert(0); + break; + } + +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: After printout starts:\n"); + vm_print_current_domain_state(vm); + fprintf(MSG_OUT, "libdvdnav: After printout ends.\n"); +#endif + + } + (vm->state).blockN = link_values.data1; + return 1; +} + + +/* Set functions */ + +static int set_TT(vm_t *vm, int tt) { + assert(tt <= vm->vmgi->tt_srpt->nr_of_srpts); + (vm->state).TTN_REG = tt; + return set_VTS_TT(vm, vm->vmgi->tt_srpt->title[tt - 1].title_set_nr, + vm->vmgi->tt_srpt->title[tt - 1].vts_ttn); +} + +static int set_VTS_TT(vm_t *vm, int vtsN, int vts_ttn) { + return set_VTS_PTT(vm, vtsN, vts_ttn, 1); +} + +static int set_VTS_PTT(vm_t *vm, int vtsN, int vts_ttn, int part) { + int pgcN, pgN, res; + + (vm->state).domain = VTS_DOMAIN; + + if(vtsN != (vm->state).vtsN) + ifoOpenNewVTSI(vm, vm->dvd, vtsN); /* Also sets (vm->state).vtsN */ + + if ((vts_ttn < 1) || (vts_ttn > vm->vtsi->vts_ptt_srpt->nr_of_srpts) || + (part < 1) || (part > vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].nr_of_ptts) ) { + return 0; + } + + pgcN = vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].ptt[part - 1].pgcn; + pgN = vm->vtsi->vts_ptt_srpt->title[vts_ttn - 1].ptt[part - 1].pgn; + + (vm->state).TT_PGCN_REG = pgcN; + (vm->state).PTTN_REG = pgN; + (vm->state).TTN_REG = get_TT(vm, vtsN, vts_ttn); + assert( (vm->state.TTN_REG) != 0 ); + (vm->state).VTS_TTN_REG = vts_ttn; + (vm->state).vtsN = vtsN; /* Not sure about this one. We can get to it easily from TTN_REG */ + /* Any other registers? */ + + res = set_PGCN(vm, pgcN); /* This clobber's state.pgN (sets it to 1), but we don't want clobbering here. */ + (vm->state).pgN = pgN; + return res; +} + +static int set_FP_PGC(vm_t *vm) { + (vm->state).domain = FP_DOMAIN; + (vm->state).pgc = vm->vmgi->first_play_pgc; + return 1; +} + + +static int set_MENU(vm_t *vm, int menu) { + assert((vm->state).domain == VMGM_DOMAIN || (vm->state).domain == VTSM_DOMAIN); + return set_PGCN(vm, get_ID(vm, menu)); +} + +static int set_PGCN(vm_t *vm, int pgcN) { + pgcit_t *pgcit; + + pgcit = get_PGCIT(vm); + assert(pgcit != NULL); /* ?? Make this return -1 instead */ + + if(pgcN < 1 || pgcN > pgcit->nr_of_pgci_srp) { +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: ** No such pgcN = %d\n", pgcN); +#endif + return 0; + } + + (vm->state).pgc = pgcit->pgci_srp[pgcN - 1].pgc; + (vm->state).pgN = 1; + + if((vm->state).domain == VTS_DOMAIN) + (vm->state).TT_PGCN_REG = pgcN; + + return 1; +} + +/* Figure out the correct pgN from the cell and update (vm->state). */ +static int set_PGN(vm_t *vm) { + int new_pgN = 0; + + while(new_pgN < (vm->state).pgc->nr_of_programs + && (vm->state).cellN >= (vm->state).pgc->program_map[new_pgN]) + new_pgN++; + + if(new_pgN == (vm->state).pgc->nr_of_programs) /* We are at the last program */ + if((vm->state).cellN > (vm->state).pgc->nr_of_cells) + return 0; /* We are past the last cell */ + + (vm->state).pgN = new_pgN; + + if((vm->state).domain == VTS_DOMAIN) { + playback_type_t *pb_ty; + if((vm->state).TTN_REG > vm->vmgi->tt_srpt->nr_of_srpts) + return 0; /* ?? */ + pb_ty = &vm->vmgi->tt_srpt->title[(vm->state).TTN_REG - 1].pb_ty; + if(pb_ty->multi_or_random_pgc_title == /* One_Sequential_PGC_Title */ 0) { + int dummy; +#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((vm->state).VTS_TTN_REG <= ptt_srpt->nr_of_srpts); + assert(get_PGCN() == ptt_srpt->title[(vm->state).VTS_TTN_REG - 1].ptt[0].pgcn); + assert(1 == ptt_srpt->title[(vm->state).VTS_TTN_REG - 1].ptt[0].pgn); +#endif + vm_get_current_title_part(vm, &dummy, &(vm->state).pgN); + (vm->state).PTTN_REG = (vm->state).pgN; + } else { + /* FIXME: Handle RANDOM or SHUFFLE titles. */ + fprintf(MSG_OUT, "libdvdnav: RANDOM or SHUFFLE titles are NOT handled yet.\n"); + } + } + return 1; +} + +/* Must be called before domain is changed (set_PGCN()) */ +static void set_RSMinfo(vm_t *vm, int cellN, int blockN) { + int i; + + if(cellN) { + (vm->state).rsm_cellN = cellN; + (vm->state).rsm_blockN = blockN; + } else { + (vm->state).rsm_cellN = (vm->state).cellN; + (vm->state).rsm_blockN = blockN; + } + (vm->state).rsm_vtsN = (vm->state).vtsN; + (vm->state).rsm_pgcN = get_PGCN(vm); + + /* assert((vm->state).rsm_pgcN == (vm->state).TT_PGCN_REG); for VTS_DOMAIN */ + + for(i = 0; i < 5; i++) { + (vm->state).rsm_regs[i] = (vm->state).registers.SPRM[4 + i]; + } +} + + +/* Get functions */ + +/* Searches the TT tables, to find the current TT. + * returns the current TT. + * returns 0 if not found. + */ +static int get_TT(vm_t *vm, int vtsN, int vts_ttn) { + int i; + int tt=0; + + for(i = 1; i <= vm->vmgi->tt_srpt->nr_of_srpts; i++) { + if( vm->vmgi->tt_srpt->title[i - 1].title_set_nr == vtsN && + vm->vmgi->tt_srpt->title[i - 1].vts_ttn == vts_ttn) { + tt=i; + break; + } + } + return tt; +} + +/* Search for entry_id match of the PGC Category in the current VTS PGCIT table. + * Return pgcN based on entry_id match. + */ +static int get_ID(vm_t *vm, int id) { + int pgcN, i; + pgcit_t *pgcit; + + /* Relies on state to get the correct pgcit. */ + pgcit = get_PGCIT(vm); + assert(pgcit != NULL); +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: ** Searching for menu (0x%x) entry PGC\n", id); +#endif + + /* Force high bit set. */ + id |=0x80; + + /* Get menu/title */ + for(i = 0; i < pgcit->nr_of_pgci_srp; i++) { + if( (pgcit->pgci_srp[i].entry_id) == id) { + pgcN = i + 1; +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: Found menu.\n"); +#endif + return pgcN; + } + } +#ifdef TRACE + fprintf(MSG_OUT, "libdvdnav: ** No such id/menu (0x%02x) entry PGC\n", id & 0x7f); + for(i = 0; i < pgcit->nr_of_pgci_srp; i++) { + if ( (pgcit->pgci_srp[i].entry_id & 0x80) == 0x80) { + fprintf(MSG_OUT, "libdvdnav: Available menus: 0x%x\n", + pgcit->pgci_srp[i].entry_id & 0x7f); + } + } +#endif + return 0; /* error */ +} + +static int get_PGCN(vm_t *vm) { + pgcit_t *pgcit; + int pgcN = 1; + + pgcit = get_PGCIT(vm); + assert(pgcit != NULL); + + while(pgcN <= pgcit->nr_of_pgci_srp) { + if(pgcit->pgci_srp[pgcN - 1].pgc == (vm->state).pgc) + return pgcN; + pgcN++; + } + fprintf(MSG_OUT, "libdvdnav: get_PGCN failed. Was trying to find pgcN in domain %d\n", + (vm->state).domain); + /* assert(0);*/ + return 0; /* error */ +} + +static pgcit_t* get_MENU_PGCIT(vm_t *vm, ifo_handle_t *h, uint16_t lang) { int i; if(h == NULL || h->pgci_ut == NULL) { @@ -2004,13 +1680,12 @@ (char)(h->pgci_ut->lu[0].lang_code >> 8), (char)(h->pgci_ut->lu[0].lang_code & 0xff)); fprintf(MSG_OUT, "libdvdnav: Menu Languages available: "); - for(i=0;i< h->pgci_ut->nr_of_lus;i++) { + for(i = 0; i < h->pgci_ut->nr_of_lus; i++) { fprintf(MSG_OUT, "%c%c ", - (char)(h->pgci_ut->lu[0].lang_code >> 8), - (char)(h->pgci_ut->lu[0].lang_code & 0xff)); + (char)(h->pgci_ut->lu[i].lang_code >> 8), + (char)(h->pgci_ut->lu[i].lang_code & 0xff)); } fprintf(MSG_OUT, "\n"); - i = 0; /* error? */ } @@ -2043,8 +1718,39 @@ return pgcit; } + +/* Debug functions */ + +#ifdef TRACE +void vm_position_print(vm_t *vm, vm_position_t *position) { + 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", + position->button, + position->spu_channel, + position->audio_channel, + position->angle_channel, + position->hop_channel, + position->vts, + position->domain, + position->cell, + position->cell_restart, + position->cell_start, + position->still, + position->block); +} +#endif + + /* * $Log$ + * Revision 1.43 2003/02/20 15:32:19 mroi + * big libdvdnav cleanup, quoting the ChangeLog: + * * some bugfixes + * * code cleanup + * * build process polishing + * * more sensible event order in get_next_block to ensure useful event delivery + * * VOBU level resume + * * fixed: seeking in a multiangle feature briefly showed the wrong angle + * * Revision 1.42 2003/01/13 13:33:45 mroi * slightly improved logic of program skipping: * previous program: diff -r ec2df154be56 -r b6834e6359cf vm.h --- a/vm.h Mon Jan 13 13:33:45 2003 +0000 +++ b/vm.h Thu Feb 20 15:32:21 2003 +0000 @@ -33,8 +33,8 @@ /* DOMAIN enum */ typedef enum { - FP_DOMAIN = 1, - VTS_DOMAIN = 2, + FP_DOMAIN = 1, + VTS_DOMAIN = 2, VMGM_DOMAIN = 4, VTSM_DOMAIN = 8 } domain_t; @@ -45,40 +45,37 @@ typedef struct { registers_t registers; - pgc_t *pgc; /* either this or *pgc is enough? */ - - domain_t domain; - int vtsN; /* 0 is vmgm? */ - /* int pgcN; // either this or *pgc is enough. Which to use? */ - int pgN; /* is this needed? can allways fid pgN from cellN? */ - int cellN; - int32_t cell_restart; /* get cell to restart */ - int blockN; + domain_t domain; + int vtsN; /* 0 is vmgm? */ + pgc_t *pgc; /* either this or 'int pgcN' is enough? */ + int pgN; /* is this needed? can allways fid pgN from cellN? */ + int cellN; + int32_t cell_restart; /* get cell to restart */ + int blockN; /* Resume info */ - int rsm_vtsN; - int rsm_blockN; /* of nav_packet */ - uint16_t rsm_regs[5]; /* system registers 4-8 */ - int rsm_pgcN; - int rsm_cellN; + int rsm_vtsN; + int rsm_blockN; /* of nav_packet */ + uint16_t rsm_regs[5]; /* system registers 4-8 */ + int rsm_pgcN; + int rsm_cellN; } dvd_state_t; typedef struct vm_position_s { - int16_t button; /* Button highlighted */ - uint32_t clut; /* CLUT to use, not needed in this struct */ - int32_t vts; /* vts number to use */ - int32_t domain; /* domain to use */ - int32_t spu_channel; /* spu channel to use */ - int32_t angle_channel; /* angle channel to use */ - int32_t audio_channel; /* audio channel to use */ - int32_t hop_channel; /* channel hopping. E.g menu button pressed */ - int32_t title; /* title number */ - int32_t chapter; /* chapter number */ - int32_t cell; /* cell number */ - int32_t cell_restart; /* get cell to restart */ - int32_t still; /* is cell still */ - int32_t vobu_start; /* block number of start of current VOBU in use */ - int32_t vobu_next; /* block number within VOBU in use */ + int16_t button; /* Button highlighted */ + int32_t vts; /* vts number to use */ + int32_t domain; /* domain to use */ + int32_t spu_channel; /* spu channel to use */ + int32_t angle_channel; /* angle channel to use */ + int32_t audio_channel; /* audio channel to use */ + int32_t hop_channel; /* channel hopping. E.g menu button pressed */ + int32_t title; /* title number */ + int32_t chapter; /* chapter number */ + int32_t cell; /* cell number */ + int32_t cell_restart; /* get cell to restart */ + int32_t cell_start; /* sector number of start of current cell in use */ + int32_t still; /* is cell still */ + int32_t block; /* block number within cell in use */ } vm_position_t; typedef struct { @@ -86,13 +83,15 @@ ifo_handle_t *vmgi; ifo_handle_t *vtsi; dvd_state_t state; - int badness_counter; - int32_t hop_channel; - char dvd_name[50]; - int dvd_name_length; - remap_t *map; + int32_t hop_channel; + char dvd_name[50]; + remap_t *map; + int stopped; } vm_t; +/* magic number for seeking hops */ +#define HOP_SEEK 0x1000 + /* Audio stream number */ #define AST_REG registers.SPRM[1] @@ -115,7 +114,7 @@ /* Initialisation & destruction */ vm_t* vm_new_vm(); -void vm_free_vm(vm_t *vm); +void vm_free_vm(vm_t *vm); /* IFO access */ ifo_handle_t *vm_get_vmgi(vm_t *vm); @@ -124,38 +123,48 @@ /* Reader Access */ dvd_reader_t *vm_get_dvd_reader(vm_t *vm); -/* Jumping */ -int vm_start_title(vm_t *vm, int tt); -int vm_jump_prog(vm_t *vm, int pr); +/* Basic Handling */ +void vm_start(vm_t *vm); +void vm_stop(vm_t *vm); +int vm_reset(vm_t *vm, const char *dvdroot); + +/* regular playback */ +void vm_position_get(vm_t *vm, vm_position_t *position); +void vm_get_next_cell(vm_t *vm); + +/* Jumping - all these return 1, if a hop has been performed */ +int vm_jump_pg(vm_t *vm, int pg); int vm_jump_title_part(vm_t *vm, int title, int part); +int vm_jump_top_pg(vm_t *vm); +int vm_jump_next_pg(vm_t *vm); +int vm_jump_prev_pg(vm_t *vm); +int vm_jump_up(vm_t *vm); +int vm_jump_menu(vm_t *vm, DVDMenuID_t menuid); +int vm_exec_cmd(vm_t *vm, vm_cmd_t *cmd); -/* Other calls */ -int vm_reset(vm_t *vm, char *dvdroot); /* , register_t regs); */ -int vm_start(vm_t *vm); -int vm_position_get(vm_t *vm, vm_position_t *position); -int vm_position_print(vm_t *vm, vm_position_t *position); -int vm_eval_cmd(vm_t *vm, vm_cmd_t *cmd); -int vm_get_next_cell(vm_t *vm); -int vm_menu_call(vm_t *vm, DVDMenuID_t menuid, int block); -int vm_resume(vm_t *vm); -int vm_go_up(vm_t *vm); -int vm_top_pg(vm_t *vm); -int vm_next_pg(vm_t *vm); -int vm_prev_pg(vm_t *vm); +/* getting information */ +int vm_get_current_title_part(vm_t *vm, int *title_result, int *part_result); int vm_get_audio_stream(vm_t *vm, int audioN); +int vm_get_subp_stream(vm_t *vm, int subpN, int mode); int vm_get_audio_active_stream(vm_t *vm); -int vm_get_subp_stream(vm_t *vm, int subpN, int mode); int vm_get_subp_active_stream(vm_t *vm, int mode); -void vm_get_angle_info(vm_t *vm, int *num_avail, int *current); -void vm_get_audio_info(vm_t *vm, int *num_avail, int *current); -void vm_get_subp_info(vm_t *vm, int *num_avail, int *current); -int vm_get_current_title_part(vm_t *vm, int *title_result, int *part_result); +void vm_get_angle_info(vm_t *vm, int *current, int *num_avail); +#if 0 +/* currently unused */ +void vm_get_audio_info(vm_t *vm, int *current, int *num_avail); +void vm_get_subp_info(vm_t *vm, int *current, int *num_avail); +void vm_get_video_res(vm_t *vm, int *width, int *height); +#endif +int vm_get_video_aspect(vm_t *vm); +int vm_get_video_scale_permission(vm_t *vm); +video_attr_t vm_get_video_attr(vm_t *vm); +audio_attr_t vm_get_audio_attr(vm_t *vm, int streamN); +subp_attr_t vm_get_subp_attr(vm_t *vm, int streamN); -subp_attr_t vm_get_subp_attr(vm_t *vm, int streamN); -audio_attr_t vm_get_audio_attr(vm_t *vm, int streamN); -void vm_get_video_res(vm_t *vm, int *width, int *height); -int vm_get_video_aspect(vm_t *vm); -int vm_get_video_scale_permission(vm_t *vm); +#ifdef TRACE +/* Debug */ +void vm_position_print(vm_t *vm, vm_position_t *position); +#endif + #endif /* VM_HV_INCLUDED */ - diff -r ec2df154be56 -r b6834e6359cf vmcmd.c --- a/vmcmd.c Mon Jan 13 13:33:45 2003 +0000 +++ b/vmcmd.c Thu Feb 20 15:32:21 2003 +0000 @@ -35,6 +35,8 @@ #include "dvdnav_internal.h" +#ifdef TRACE + /* freebsd compatibility */ #ifndef PRIu8 #define PRIu8 "d" @@ -486,4 +488,4 @@ fprintf(MSG_OUT, "\n"); } - +#endif diff -r ec2df154be56 -r b6834e6359cf vmcmd.h --- a/vmcmd.h Mon Jan 13 13:33:45 2003 +0000 +++ b/vmcmd.h Thu Feb 20 15:32:21 2003 +0000 @@ -29,7 +29,9 @@ #include /* Only for vm_cmd_t */ #include "decoder.h" +#ifdef TRACE void vmPrint_mnemonic(vm_cmd_t *command); void vmPrint_CMD(int row, vm_cmd_t *command); +#endif #endif /* VMCMD_H_INCLUDED */