changeset 114:b6834e6359cf src

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
author mroi
date Thu, 20 Feb 2003 15:32:21 +0000
parents ec2df154be56
children b527b7cbfb19
files Makefile.am decoder.c decoder.h dvdnav.c dvdnav.h dvdnav_events.h dvdnav_internal.h.in highlight.c navigation.c read_cache.c remap.c remap.h searching.c settings.c vm.c vm.h vmcmd.c vmcmd.h
diffstat 18 files changed, 2681 insertions(+), 2907 deletions(-) [+]
line wrap: on
line diff
--- 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)
 #
--- 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
+
--- 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 <dvdread/ifo_types.h> /*  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 */
--- 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 <pthread.h>
-#include <dvdnav.h>
 #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, &current, &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, &current, &num) != S_OK) {
-    printerr("Error getting angle info");
-    return S_ERR;
-  }
-  
+  pthread_mutex_lock(&this->vm_lock);
+  vm_get_angle_info(this->vm, &current, &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
--- 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
+ * `<tt>THE_MATRIX</tt>' but sometimes is sigularly uninformative such as
+ * `<tt>PDVD-011421</tt>'.
+ * 
+ * \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 <tt>libdvdread</tt>)
+ */
+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 <tt>libdvdread</tt>)
- */
-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
- * `<tt>THE_MATRIX</tt>' but sometimes is sigularly uninformative such as
- * `<tt>PDVD-011421</tt>'.
- * 
- * \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.
--- 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. */
--- 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 <dvdread/ifo_types.h>
 
 
+/* 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
 
--- 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 <assert.h>
 
-#include <dvdnav.h>
 #include "dvdnav_internal.h"
 
 #include "vm.h"
 #include <dvdread/nav_types.h>
 
+/*
+#define BUTTON_TESTING
+*/
+
 #ifdef BUTTON_TESTING
+
 #include <dvdread/nav_print.h>
 #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;
 }
-
--- 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 <dvdnav.h>
 #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;
 }
-
-
-
--- 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;
 
--- 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 <stdlib.h>
 #include <string.h>
 #include <stdio.h>
@@ -5,6 +25,7 @@
 #include <sys/fcntl.h>
 #include <assert.h>
 #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;
     }
--- 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;
--- 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 <dvdnav.h>
+#include <assert.h>
+
 #include "dvdnav_internal.h"
 
 #include "vm.h"
 #include <dvdread/nav_types.h>
 
+/*
+#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;
 }
-
-
--- 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 <dvdnav.h>
 #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;
   }
   
--- 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:
--- 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 */
-
--- 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
--- 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 <dvdread/ifo_types.h> /*  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 */