$OpenBSD: patch-src_dvdnav_c,v 1.1 2012/09/26 00:04:09 brad Exp $

- Check that a VOB is open in case we need to open one
- Add a function to create a dpu of a dvdnav handle
- Prevent overflow by pre-casting to int64_t
- Remove unneeded address-of of CLUT
- Make sure we get nav packets for all cells in multi angle features
- Test still like a boolean value and not an integer value
- Always check whether a still is being handled

--- src/dvdnav.c.orig	Wed Sep  5 14:23:55 2012
+++ src/dvdnav.c	Wed Sep  5 14:24:07 2012
@@ -71,6 +71,67 @@ static dvdnav_status_t dvdnav_clear(dvdnav_t * this) {
   return DVDNAV_STATUS_OK;
 }
 
+dvdnav_status_t dvdnav_dup(dvdnav_t **dest, dvdnav_t *src) {
+  dvdnav_t *this;
+
+  (*dest) = NULL;
+  this = (dvdnav_t*)malloc(sizeof(dvdnav_t));
+  if(!this)
+    return DVDNAV_STATUS_ERR;
+
+  memcpy(this, src, sizeof(dvdnav_t));
+  this->file = NULL;
+
+  pthread_mutex_init(&this->vm_lock, NULL);
+
+  this->vm = vm_new_copy(src->vm);
+  if(!this->vm) {
+    printerr("Error initialising the DVD VM.");
+    pthread_mutex_destroy(&this->vm_lock);
+    free(this);
+    return DVDNAV_STATUS_ERR;
+  }
+
+  /* Start the read-ahead cache. */
+  this->cache = dvdnav_read_cache_new(this);
+
+  (*dest) = this;
+  return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_free_dup(dvdnav_t *this) {
+
+#ifdef LOG_DEBUG
+  fprintf(MSG_OUT, "libdvdnav: free_dup:called\n");
+#endif
+
+  if (this->file) {
+    pthread_mutex_lock(&this->vm_lock);
+    DVDCloseFile(this->file);
+#ifdef LOG_DEBUG
+    fprintf(MSG_OUT, "libdvdnav: close:file closing\n");
+#endif
+    this->file = NULL;
+    pthread_mutex_unlock(&this->vm_lock);
+  }
+
+  /* Free the VM */
+  if(this->vm)
+    vm_free_copy(this->vm);
+
+  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)
+    dvdnav_read_cache_free(this->cache);
+  else
+    free(this);
+
+  return DVDNAV_STATUS_OK;
+}
+
 dvdnav_status_t dvdnav_open(dvdnav_t** dest, const char *path) {
   dvdnav_t *this;
   struct timeval time;
@@ -203,12 +264,12 @@ int64_t dvdnav_convert_time(dvd_time_t *time) {
   int64_t result;
   int64_t frames;
 
-  result  = (time->hour    >> 4  ) * 10 * 60 * 60 * 90000;
-  result += (time->hour    & 0x0f)      * 60 * 60 * 90000;
-  result += (time->minute  >> 4  )      * 10 * 60 * 90000;
-  result += (time->minute  & 0x0f)           * 60 * 90000;
-  result += (time->second  >> 4  )           * 10 * 90000;
-  result += (time->second  & 0x0f)                * 90000;
+  result  = ((int64_t)(time->hour    >> 4  )) * 10 * 60 * 60 * 90000;
+  result += ((int64_t)(time->hour    & 0x0f))      * 60 * 60 * 90000;
+  result += ((int64_t)(time->minute  >> 4  ))      * 10 * 60 * 90000;
+  result += ((int64_t)(time->minute  & 0x0f))           * 60 * 90000;
+  result += ((int64_t)(time->second  >> 4  ))           * 10 * 90000;
+  result += ((int64_t)(time->second  & 0x0f))                * 90000;
 
   frames  = ((time->frame_u & 0x30) >> 4) * 10;
   frames += ((time->frame_u & 0x0f)     )     ;
@@ -336,9 +397,10 @@ static int32_t dvdnav_get_vobu(dvdnav_t *this, dsi_t *
     dvdnav_angle_change(this, 1);
   }
 #endif
+  /* only use ILVU information if we are at the last vobunit in ILVU */
+  /* otherwise we will miss nav packets from vobunits inbetween */
+  if(num_angle != 0 && (nav_dsi->sml_pbi.category & DSI_ILVU_MASK) == (DSI_ILVU_BLOCK | DSI_ILVU_LAST)) {
 
-  if(num_angle != 0) {
-
     if((next = nav_pci->nsml_agli.nsml_agl_dsta[angle-1]) != 0) {
       if((next & 0x3fffffff) != 0) {
 	if(next & 0x80000000)
@@ -518,8 +580,10 @@ dvdnav_status_t dvdnav_get_next_cache_block(dvdnav_t *
     return DVDNAV_STATUS_OK;
   }
 
-  /* Check to see if we need to change the currently opened VOB */
-  if((this->position_current.vts != this->position_next.vts) ||
+  /* Check to see if we need to change the currently opened VOB or open
+   * a new one because we don't currently have an opened VOB. */
+  if((this->file == NULL) ||
+     (this->position_current.vts != this->position_next.vts) ||
      (this->position_current.domain != this->position_next.domain)) {
     dvd_read_domain_t domain;
     int32_t vtsN;
@@ -655,7 +719,7 @@ dvdnav_status_t dvdnav_get_next_cache_block(dvdnav_t *
     fprintf(MSG_OUT, "libdvdnav: SPU_CLUT_CHANGE\n");
 #endif
     (*len) = 16 * sizeof(uint32_t);
-    memcpy(*buf, &(state->pgc->palette), 16 * sizeof(uint32_t));
+    memcpy(*buf, state->pgc->palette, sizeof(state->pgc->palette));
     this->spu_clut_changed = 0;
     pthread_mutex_unlock(&this->vm_lock);
     return DVDNAV_STATUS_OK;
@@ -731,16 +795,15 @@ dvdnav_status_t dvdnav_get_next_cache_block(dvdnav_t *
       /* we are about to leave a cell, so a lot of state changes could occur;
        * under certain conditions, the application should get in sync with us before this,
        * otherwise it might show stills or menus too shortly */
-      if ((this->position_current.still || this->pci.hli.hl_gi.hli_ss) && !this->sync_wait_skip) {
+      if ((this->position_current.still || this->pci.hli.hl_gi.hli_ss) && !this->sync_wait_skip)
         this->sync_wait = 1;
-      } else {
-	if( this->position_current.still == 0 || this->skip_still ) {
-	  /* 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;
-	  this->sync_wait_skip = 0;
-	}
+
+      if(!this->position_current.still || this->skip_still ) {
+	/* 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;
+	this->sync_wait_skip = 0;
       }
       /* handle related state changes in next iteration */
       (*event) = DVDNAV_NOP;
