summaryrefslogtreecommitdiffstats
path: root/libdvdnav-embedded/src
diff options
context:
space:
mode:
Diffstat (limited to 'libdvdnav-embedded/src')
-rw-r--r--libdvdnav-embedded/src/dvdnav.c1470
-rw-r--r--libdvdnav-embedded/src/dvdnav/dvd_types.h282
-rw-r--r--libdvdnav-embedded/src/dvdnav/dvdnav.h797
-rw-r--r--libdvdnav-embedded/src/dvdnav/dvdnav_events.h236
-rw-r--r--libdvdnav-embedded/src/dvdnav/version.h.in35
-rw-r--r--libdvdnav-embedded/src/dvdnav_internal.h246
-rw-r--r--libdvdnav-embedded/src/highlight.c514
-rw-r--r--libdvdnav-embedded/src/logger.c41
-rw-r--r--libdvdnav-embedded/src/logger.h32
-rw-r--r--libdvdnav-embedded/src/navigation.c300
-rw-r--r--libdvdnav-embedded/src/read_cache.c350
-rw-r--r--libdvdnav-embedded/src/read_cache.h46
-rw-r--r--libdvdnav-embedded/src/searching.c1356
-rw-r--r--libdvdnav-embedded/src/settings.c91
-rw-r--r--libdvdnav-embedded/src/vm/decoder.c783
-rw-r--r--libdvdnav-embedded/src/vm/decoder.h111
-rw-r--r--libdvdnav-embedded/src/vm/getset.c371
-rw-r--r--libdvdnav-embedded/src/vm/getset.h50
-rw-r--r--libdvdnav-embedded/src/vm/play.c339
-rw-r--r--libdvdnav-embedded/src/vm/play.h31
-rw-r--r--libdvdnav-embedded/src/vm/vm.c1177
-rw-r--r--libdvdnav-embedded/src/vm/vm.h178
-rw-r--r--libdvdnav-embedded/src/vm/vmcmd.c543
-rw-r--r--libdvdnav-embedded/src/vm/vmcmd.h28
-rw-r--r--libdvdnav-embedded/src/vm/vmget.c353
25 files changed, 9760 insertions, 0 deletions
diff --git a/libdvdnav-embedded/src/dvdnav.c b/libdvdnav-embedded/src/dvdnav.c
new file mode 100644
index 0000000..4ef7d1a
--- /dev/null
+++ b/libdvdnav-embedded/src/dvdnav.c
@@ -0,0 +1,1470 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * 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 libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* Un-comment the following to enable additional Log3() function calls.
+ * Do not forget to #define TRACE in src/vm/vm.h (required) */
+/*
+#define LOG_DEBUG
+*/
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include <string.h>
+#include <sys/time.h>
+#include "dvdnav/dvdnav.h"
+#include <dvdread/dvd_reader.h>
+#include <dvdread/nav_types.h>
+#include "vm/decoder.h"
+#include "vm/vm.h"
+#include "vm/getset.h"
+#include "dvdnav_internal.h"
+#include "logger.h"
+#include "read_cache.h"
+#include <dvdread/nav_read.h>
+
+static dvdnav_status_t dvdnav_clear(dvdnav_t * this) {
+ /* clear everything except file, vm, mutex, readahead */
+
+ pthread_mutex_lock(&this->vm_lock);
+ if (this->file) DVDCloseFile(this->file);
+ this->file = NULL;
+
+ memset(&this->position_current,0,sizeof(this->position_current));
+ memset(&this->pci,0,sizeof(this->pci));
+ memset(&this->dsi,0,sizeof(this->dsi));
+ this->last_cmd_nav_lbn = SRI_END_OF_CELL;
+
+ /* Set initial values of flags */
+ this->skip_still = 0;
+ this->sync_wait = 0;
+ this->sync_wait_skip = 0;
+ this->spu_clut_changed = 0;
+ this->started = 0;
+ this->cur_cell_time = 0;
+
+ dvdnav_read_cache_clear(this->cache);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_dup(dvdnav_t **dest, dvdnav_t *src) {
+ dvdnav_t *this;
+
+ (*dest) = NULL;
+ this = (dvdnav_t*)malloc(sizeof(dvdnav_t));
+ if (!this)
+ return DVDNAV_STATUS_ERR;
+
+ memcpy(this, src, sizeof(dvdnav_t));
+ this->file = NULL;
+ this->vm = NULL;
+ this->path = NULL;
+ this->cache = NULL;
+
+ pthread_mutex_init(&this->vm_lock, NULL);
+
+ this->vm = vm_new_copy(src->vm);
+ if (!this->vm)
+ goto fail;
+
+ this->path = strdup(src->path);
+ if (!this->path)
+ goto fail;
+
+ /* Start the read-ahead cache. */
+ this->cache = dvdnav_read_cache_new(this);
+ if (!this->cache)
+ goto fail;
+
+ (*dest) = this;
+ return DVDNAV_STATUS_OK;
+
+fail:
+ printerr("Error initialising the DVD VM.");
+ pthread_mutex_destroy(&this->vm_lock);
+ vm_free_vm(this->vm);
+ free(this->path);
+ free(this);
+ return DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_free_dup(dvdnav_t *this) {
+
+#ifdef LOG_DEBUG
+ Log3(this, "free_dup:called");
+#endif
+
+ if (this->file) {
+ pthread_mutex_lock(&this->vm_lock);
+ DVDCloseFile(this->file);
+#ifdef LOG_DEBUG
+ Log3(this, "close:file closing");
+#endif
+ this->file = NULL;
+ pthread_mutex_unlock(&this->vm_lock);
+ }
+
+ /* Free the VM */
+ if(this->vm)
+ vm_free_copy(this->vm);
+
+ pthread_mutex_destroy(&this->vm_lock);
+
+ free(this->path);
+
+ /* We leave the final freeing of the entire structure to the cache,
+ * because we don't know, if there are still buffers out in the wild,
+ * that must return first. */
+ if(this->cache)
+ dvdnav_read_cache_free(this->cache);
+ else
+ free(this);
+
+ return DVDNAV_STATUS_OK;
+}
+
+static dvdnav_status_t dvdnav_open_common(dvdnav_t** dest,
+ void *priv, const dvdnav_logger_cb *logcb,
+ const char *path,
+ dvdnav_stream_cb *stream_cb) {
+ dvdnav_t *this;
+ struct timeval time;
+
+ /* Create a new structure */
+ (*dest) = NULL;
+ this = (dvdnav_t*)calloc(1, sizeof(dvdnav_t));
+ if(!this)
+ return DVDNAV_STATUS_ERR;
+
+ this->priv = priv;
+ if(logcb)
+ this->logcb = *logcb;
+
+ pthread_mutex_init(&this->vm_lock, NULL);
+ /* Initialise the error string */
+ printerr("");
+
+ /* Initialise the VM */
+ this->vm = vm_new_vm(priv, logcb);
+ if(!this->vm) {
+ goto fail;
+ }
+ if(!vm_reset(this->vm, path, priv, stream_cb)) {
+ goto fail;
+ }
+
+ /* Set the path. */
+ if(path != NULL)
+ {
+ this->path = strdup(path);
+ if(!this->path)
+ goto fail;
+ }
+
+ /* 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);
+
+ /* Start the read-ahead cache. */
+ this->cache = dvdnav_read_cache_new(this);
+ if(!this->cache)
+ goto fail;
+
+ /* 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 DVDNAV_STATUS_OK;
+
+fail:
+ pthread_mutex_destroy(&this->vm_lock);
+ vm_free_vm(this->vm);
+ free(this->path);
+ free(this);
+ return DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_open(dvdnav_t** dest, const char *path) {
+ return dvdnav_open_common(dest, NULL, NULL, path, NULL);
+}
+
+dvdnav_status_t dvdnav_open2(dvdnav_t** dest,
+ void *priv,const dvdnav_logger_cb *logcb,
+ const char *path) {
+ return dvdnav_open_common(dest, priv, logcb, path, NULL);
+}
+
+dvdnav_status_t dvdnav_open_stream(dvdnav_t** dest,
+ void *priv, dvdnav_stream_cb *stream_cb) {
+ return dvdnav_open_common(dest, priv, NULL, NULL, stream_cb);
+}
+
+dvdnav_status_t dvdnav_open_stream2(dvdnav_t** dest,
+ void *priv,const dvdnav_logger_cb *logcb,
+ dvdnav_stream_cb *stream_cb) {
+ return dvdnav_open_common(dest, priv, logcb, NULL, stream_cb);
+}
+
+dvdnav_status_t dvdnav_close(dvdnav_t *this) {
+
+#ifdef LOG_DEBUG
+ Log3(this, "close:called");
+#endif
+
+ if (this->file) {
+ pthread_mutex_lock(&this->vm_lock);
+ DVDCloseFile(this->file);
+#ifdef LOG_DEBUG
+ Log3(this, "close:file closing");
+#endif
+ this->file = NULL;
+ pthread_mutex_unlock(&this->vm_lock);
+ }
+
+ /* Free the VM */
+ if(this->vm)
+ vm_free_vm(this->vm);
+
+ pthread_mutex_destroy(&this->vm_lock);
+
+ free(this->path);
+
+ /* We leave the final freeing of the entire structure to the cache,
+ * because we don't know, if there are still buffers out in the wild,
+ * that must return first. */
+ if(this->cache)
+ dvdnav_read_cache_free(this->cache);
+ else
+ free(this);
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_reset(dvdnav_t *this) {
+ dvdnav_status_t result;
+
+#ifdef LOG_DEBUG
+ Log3(this, "reset:called");
+#endif
+
+ pthread_mutex_lock(&this->vm_lock);
+
+#ifdef LOG_DEBUG
+ Log3(this, "resetting vm");
+#endif
+ if(!vm_reset(this->vm, NULL, NULL, NULL)) {
+ printerr("Error restarting the VM.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+#ifdef LOG_DEBUG
+ Log3(this, "clearing dvdnav");
+#endif
+ pthread_mutex_unlock(&this->vm_lock);
+ result = dvdnav_clear(this);
+
+ return result;
+}
+
+dvdnav_status_t dvdnav_path(dvdnav_t *this, const char** path) {
+ (*path) = this->path;
+
+ return DVDNAV_STATUS_OK;
+}
+
+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;
+}
+
+/* converts a dvd_time_t to PTS ticks */
+int64_t dvdnav_convert_time(const dvd_time_t *time) {
+ int64_t result;
+ int64_t frames;
+
+ result = ((int64_t)(time->hour >> 4 )) * 10 * 60 * 60 * 90000;
+ result += ((int64_t)(time->hour & 0x0f)) * 60 * 60 * 90000;
+ result += ((int64_t)(time->minute >> 4 )) * 10 * 60 * 90000;
+ result += ((int64_t)(time->minute & 0x0f)) * 60 * 90000;
+ result += ((int64_t)(time->second >> 4 )) * 10 * 90000;
+ result += ((int64_t)(time->second & 0x0f)) * 90000;
+
+ frames = ((time->frame_u & 0x30) >> 4) * 10;
+ frames += ((time->frame_u & 0x0f) ) ;
+
+ if (time->frame_u & 0x80)
+ result += frames * 3000;
+ else
+ result += frames * 3600;
+
+ return result;
+}
+
+/*
+ * Returns 1 if block contains NAV packet, 0 otherwise.
+ * Processes 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.
+ */
+static int32_t dvdnav_decode_packet(dvdnav_t *this, uint8_t *p,
+ dsi_t *nav_dsi, pci_t *nav_pci) {
+ int32_t bMpeg1 = 0;
+ uint32_t nHeaderLen;
+ uint32_t nPacketLen;
+ uint32_t nStreamID;
+
+ if (p[3] == 0xBA) { /* program stream pack header */
+ int32_t nStuffingBytes;
+
+ bMpeg1 = (p[4] & 0x40) == 0;
+
+ if (bMpeg1) {
+ p += 12;
+ } else { /* mpeg2 */
+ nStuffingBytes = p[0xD] & 0x07;
+ p += 14 + nStuffingBytes;
+ }
+ }
+
+ if (p[3] == 0xbb) { /* program stream system header */
+ 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)) {
+ Log1(this, "demux error! %02x %02x %02x (should be 0x000001)",p[0],p[1],p[2]);
+ return 0;
+ }
+
+ nPacketLen = p[4] << 8 | p[5];
+ nStreamID = p[3];
+
+ nHeaderLen = 6;
+ p += nHeaderLen;
+
+ if (nStreamID == 0xbf) { /* Private stream 2 */
+#if 0
+ char buffer[80 * 3 + 1];
+ int32_t i;
+ for(i=0;i<80;i++)
+ sprintf(&buffer[i*3], "%02x ",p[i-6]);
+ Log3(this, "nav packet=%u %s",p-p_start-6, buffer);
+#endif
+
+ if(p[0] == 0x00) {
+ navRead_PCI(nav_pci, p+1);
+ }
+
+ p += nPacketLen;
+
+ /* We should now have a DSI packet. */
+ if(p[6] == 0x01) {
+ nPacketLen = p[4] << 8 | p[5];
+ p += 6;
+ navRead_DSI(nav_dsi, p+1);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+/* DSI is used for most angle stuff.
+ * PCI is used for only non-seemless angle stuff
+ */
+static int32_t dvdnav_get_vobu(dvdnav_t *this, dsi_t *nav_dsi, pci_t *nav_pci, dvdnav_vobu_t *vobu) {
+ uint32_t next;
+ int32_t angle, num_angle;
+
+ vobu->vobu_start = nav_dsi->dsi_gi.nv_pck_lbn; /* Absolute offset from start of disk */
+ vobu->vobu_length = nav_dsi->dsi_gi.vobu_ea; /* Relative offset from vobu_start */
+
+ /*
+ * If we're not at the end of this cell, we can determine the next
+ * VOBU to display using the VOBU_SRI information section of the
+ * DSI. Using this value correctly follows the current angle,
+ * avoiding the doubled scenes in The Matrix, and makes our life
+ * really happy.
+ *
+ * vobu_next is an offset value, 0x3fffffff = SRI_END_OF_CELL
+ * DVDs are about 6 Gigs, which is only up to 0x300000 blocks
+ * Should really assert if bit 31 != 1
+ */
+
+#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
+
+ vm_get_angle_info(this->vm, &angle, &num_angle);
+
+ /* FIMXE: The angle reset doesn't work for some reason for the moment */
+#if 0
+ if((num_angle < angle) && (angle != 1)) {
+ Log3(this, "angle ends!");
+
+ /* This is to switch back to angle one when we
+ * finish with angles. */
+ dvdnav_angle_change(this, 1);
+ }
+#endif
+ /* only use ILVU information if we are at the last vobunit in ILVU */
+ /* otherwise we will miss nav packets from vobunits inbetween */
+ if(num_angle != 0 && (nav_dsi->sml_pbi.category & DSI_ILVU_MASK) == (DSI_ILVU_BLOCK | DSI_ILVU_LAST)) {
+
+ if((next = nav_pci->nsml_agli.nsml_agl_dsta[angle-1]) != 0) {
+ if((next & 0x3fffffff) != 0) {
+ if(next & 0x80000000)
+ vobu->vobu_next = - (int32_t)(next & 0x3fffffff);
+ else
+ vobu->vobu_next = + (int32_t)(next & 0x3fffffff);
+ }
+ } 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))
+ vobu->vobu_next = - (int32_t)(next & 0x3fffffff);
+ else
+ vobu->vobu_next = + (int32_t)(next & 0x3fffffff);
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * 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 targeting 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, uint8_t *buf,
+ int32_t *event, int32_t *len) {
+ unsigned char *block;
+ dvdnav_status_t status;
+
+ block = buf;
+ status = dvdnav_get_next_cache_block(this, &block, event, len);
+ if (status == DVDNAV_STATUS_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);
+ }
+ return status;
+}
+
+int64_t dvdnav_get_current_time(dvdnav_t *this) {
+ int i;
+ int64_t tm=0;
+ dvd_state_t *state = &this->vm->state;
+
+ if(! state->pgc)
+ return 0;
+
+ for(i=0; i<state->cellN-1; i++) {
+ if(!
+ (state->pgc->cell_playback[i].block_type == BLOCK_TYPE_ANGLE_BLOCK &&
+ state->pgc->cell_playback[i].block_mode != BLOCK_MODE_FIRST_CELL)
+ )
+ tm += dvdnav_convert_time(&state->pgc->cell_playback[i].playback_time);
+ }
+ tm += this->cur_cell_time;
+
+ return tm;
+}
+
+dvdnav_status_t dvdnav_get_next_cache_block(dvdnav_t *this, uint8_t **buf,
+ int32_t *event, int32_t *len) {
+ dvd_state_t *state;
+ int32_t result;
+
+ if(!this)
+ return DVDNAV_STATUS_ERR;
+
+ pthread_mutex_lock(&this->vm_lock);
+
+ if(!this->started) {
+ /* Start the VM */
+ if (!vm_start(this->vm)) {
+ printerr("Encrypted or faulty DVD");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ this->started = 1;
+ }
+
+ if (!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+ state = &(this->vm->state);
+ (*event) = DVDNAV_NOP;
+ (*len) = 0;
+
+ /* Check the STOP flag */
+ if(this->vm->stopped) {
+ vm_stop(this->vm);
+ (*event) = DVDNAV_STOP;
+ this->started = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+
+ vm_position_get(this->vm, &this->position_next);
+
+#ifdef LOG_DEBUG
+ Log3(this->vm, "POS-NEXT ");
+ vm_position_print(this->vm, &this->position_next);
+ Log3(this->vm, "POS-CUR ");
+ vm_position_print(this->vm, &this->position_current);
+#endif
+
+ /* did we hop? */
+ if(this->position_current.hop_channel != this->position_next.hop_channel) {
+ (*event) = DVDNAV_HOP_CHANNEL;
+#ifdef LOG_DEBUG
+ Log3(this->vm, "HOP_CHANNEL");
+#endif
+ if (this->position_next.hop_channel - this->position_current.hop_channel >= HOP_SEEK) {
+ int32_t num_angles = 0, current;
+
+ /* we seeked -> check for multiple angles */
+ vm_get_angle_info(this->vm, &current, &num_angles);
+ if (num_angles > 1) {
+ int32_t 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 DVDNAV_STATUS_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 DVDNAV_STATUS_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;
+ /* update VOBU info */
+ 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 = 0;
+ this->sync_wait = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+
+ /* Check the HIGHLIGHT flag */
+ if(this->position_current.button != this->position_next.button) {
+ dvdnav_highlight_event_t *hevent = (dvdnav_highlight_event_t *)*buf;
+
+ (*event) = DVDNAV_HIGHLIGHT;
+#ifdef LOG_DEBUG
+ Log3(this, "HIGHLIGHT");
+#endif
+ (*len) = sizeof(dvdnav_highlight_event_t);
+ hevent->display = 1;
+ hevent->buttonN = this->position_next.button;
+ this->position_current.button = this->position_next.button;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+
+ /* Check the WAIT flag */
+ if(this->sync_wait) {
+ (*event) = DVDNAV_WAIT;
+#ifdef LOG_DEBUG
+ Log3(this, "WAIT");
+#endif
+ (*len) = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+
+ /* Check to see if we need to change the currently opened VOB or open
+ * a new one because we don't currently have an opened VOB. */
+ if((this->file == NULL) ||
+ (this->position_current.vts != this->position_next.vts) ||
+ (this->position_current.domain != this->position_next.domain)) {
+ dvd_read_domain_t domain;
+ int32_t vtsN;
+ dvdnav_vts_change_event_t *vts_event = (dvdnav_vts_change_event_t *)*buf;
+
+ if(this->file) {
+ DVDCloseFile(this->file);
+ this->file = NULL;
+ }
+
+ vts_event->old_vtsN = this->position_current.vts;
+ vts_event->old_domain = this->position_current.domain;
+
+ /* Use the DOMAIN to find whether to open menu or title VOBs */
+ switch(this->position_next.domain) {
+ case DVD_DOMAIN_FirstPlay:
+ case DVD_DOMAIN_VMGM:
+ domain = DVD_READ_MENU_VOBS;
+ vtsN = 0;
+ break;
+ case DVD_DOMAIN_VTSMenu:
+ domain = DVD_READ_MENU_VOBS;
+ vtsN = this->position_next.vts;
+ break;
+ case DVD_DOMAIN_VTSTitle:
+ domain = DVD_READ_TITLE_VOBS;
+ vtsN = this->position_next.vts;
+ break;
+ default:
+ printerr("Unknown domain when changing VTS.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+ this->position_current.vts = this->position_next.vts;
+ this->position_current.domain = this->position_next.domain;
+ dvdnav_read_cache_clear(this->cache);
+ this->file = DVDOpenFile(vm_get_dvd_reader(this->vm), vtsN, domain);
+ vts_event->new_vtsN = this->position_next.vts;
+ vts_event->new_domain = this->position_next.domain;
+
+ /* If couldn't open the file for some reason, moan */
+ if(this->file == NULL) {
+ printerrf("Error opening vtsN=%i, domain=%i.", vtsN, domain);
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+ /* File opened successfully so return a VTS change event */
+ (*event) = DVDNAV_VTS_CHANGE;
+#ifdef LOG_DEBUG
+ Log3(this, "VTS_CHANGE");
+#endif
+ (*len) = sizeof(dvdnav_vts_change_event_t);
+
+ this->spu_clut_changed = 1;
+ this->position_current.cell = -1; /* Force an update */
+ 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 DVDNAV_STATUS_OK;
+ }
+
+ /* 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.cell_start != this->position_next.cell_start) ) {
+ dvdnav_cell_change_event_t *cell_event = (dvdnav_cell_change_event_t *)*buf;
+ int32_t first_cell_nr, last_cell_nr, i;
+ dvd_state_t *state = &this->vm->state;
+
+ this->cur_cell_time = 0;
+ (*event) = DVDNAV_CELL_CHANGE;
+#ifdef LOG_DEBUG
+ Log3(this, "CELL_CHANGE");
+#endif
+ (*len) = sizeof(dvdnav_cell_change_event_t);
+
+ cell_event->cellN = state->cellN;
+ cell_event->pgN = state->pgN;
+ cell_event->cell_length =
+ (state->pgc->cell_playback[state->cellN - 1].last_sector + 1);
+
+ cell_event->pg_length = 0;
+ /* Find start cell of program. */
+ first_cell_nr = state->pgc->program_map[state->pgN-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
+ last_cell_nr = state->pgc->nr_of_cells;
+ for (i = first_cell_nr; i <= last_cell_nr; i++)
+ cell_event->pg_length +=
+ (state->pgc->cell_playback[i - 1].last_sector + 1);
+
+ cell_event->pgc_length = dvdnav_convert_time(&state->pgc->playback_time);
+
+ cell_event->cell_start = 0;
+ for (i = 1; i < state->cellN; i++)
+ cell_event->cell_start +=
+ (state->pgc->cell_playback[i - 1].last_sector + 1);
+
+ cell_event->pg_start = 0;
+ for (i = 1; i < state->pgc->program_map[state->pgN-1]; i++)
+ cell_event->pg_start +=
+ (state->pgc->cell_playback[i - 1].last_sector + 1);
+
+ this->position_current.cell = this->position_next.cell;
+ this->position_current.cell_restart = this->position_next.cell_restart;
+ 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 = 0;
+
+ /* 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 DVDNAV_STATUS_OK;
+ }
+
+ /* has the CLUT changed? */
+ if(this->spu_clut_changed) {
+ (*event) = DVDNAV_SPU_CLUT_CHANGE;
+#ifdef LOG_DEBUG
+ Log3(this, "SPU_CLUT_CHANGE");
+#endif
+ (*len) = 16 * sizeof(uint32_t);
+ memcpy(*buf, state->pgc->palette, sizeof(state->pgc->palette));
+ this->spu_clut_changed = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+
+ /* has the SPU channel changed? */
+ if(this->position_current.spu_channel != this->position_next.spu_channel) {
+ dvdnav_spu_stream_change_event_t *stream_change = (dvdnav_spu_stream_change_event_t *)*buf;
+
+ (*event) = DVDNAV_SPU_STREAM_CHANGE;
+#ifdef LOG_DEBUG
+ Log3(this, "SPU_STREAM_CHANGE");
+#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);
+ this->position_current.spu_channel = this->position_next.spu_channel;
+#ifdef LOG_DEBUG
+ Log3(this, "SPU_STREAM_CHANGE stream_id_wide=%d",stream_change->physical_wide);
+ Log3(this, "SPU_STREAM_CHANGE stream_id_letterbox=%d",stream_change->physical_letterbox);
+ Log3(this, "SPU_STREAM_CHANGE stream_id_pan_scan=%d",stream_change->physical_pan_scan);
+ Log3(this, "SPU_STREAM_CHANGE returning DVDNAV_STATUS_OK");
+#endif
+ /* This is not really the right place to do this. FOSL_BTNN should set the register
+ * at HLI_S_PTM rather than when we enter the SPU. As well we should activate FOAC_BTNN
+ * at HLI_E_PTM
+ */
+ if (this->pci.hli.hl_gi.fosl_btnn != 0) {
+ set_HL_BTN(this->vm, this->pci.hli.hl_gi.fosl_btnn);
+ }
+
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_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 = (dvdnav_audio_stream_change_event_t *)*buf;
+
+ (*event) = DVDNAV_AUDIO_STREAM_CHANGE;
+#ifdef LOG_DEBUG
+ Log3(this, "AUDIO_STREAM_CHANGE");
+#endif
+ (*len) = sizeof(dvdnav_audio_stream_change_event_t);
+ stream_change->physical = vm_get_audio_active_stream( this->vm );
+ stream_change->logical = this->position_next.audio_channel;
+ this->position_current.audio_channel = this->position_next.audio_channel;
+#ifdef LOG_DEBUG
+ Log3(this, "AUDIO_STREAM_CHANGE stream_id=%d returning DVDNAV_STATUS_OK",stream_change->physical);
+#endif
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+
+ /* Check the STILLFRAME flag */
+ if(this->position_current.still != 0) {
+ dvdnav_still_event_t *still_event = (dvdnav_still_event_t *)*buf;
+
+ (*event) = DVDNAV_STILL_FRAME;
+#ifdef LOG_DEBUG
+ Log3(this, "STILL_FRAME");
+#endif
+ (*len) = sizeof(dvdnav_still_event_t);
+ still_event->length = this->position_current.still;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_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
+ Log3(this, "Still set to %x", this->position_next.still);
+#endif
+ this->position_current.still = this->position_next.still;
+
+ /* we are about to leave a cell, so a lot of state changes could occur;
+ * under certain conditions, the application should get in sync with us before this,
+ * otherwise it might show stills or menus too shortly */
+ if ((this->position_current.still || this->pci.hli.hl_gi.hli_ss) && !this->sync_wait_skip)
+ this->sync_wait = 1;
+
+ if(!this->position_current.still || this->skip_still ) {
+ /* no active cell still -> get us to the next cell */
+ vm_get_next_cell(this->vm);
+ this->position_current.still = 0; /* still gets activated at end of cell */
+ this->skip_still = 0;
+ this->sync_wait_skip = 0;
+ }
+ /* handle related state changes in next iteration */
+ (*event) = DVDNAV_NOP;
+ (*len) = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+
+ /* 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) {
+ printerr("Error reading NAV packet.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_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 DVDNAV_STATUS_ERR;
+ }
+ /* 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->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);
+
+ /* release NAV menu filter, when we reach the same NAV packet again */
+ if (this->last_cmd_nav_lbn == this->pci.pci_gi.nv_pck_lbn)
+ this->last_cmd_nav_lbn = SRI_END_OF_CELL;
+
+ /* Successfully got a NAV packet */
+ (*event) = DVDNAV_NAV_PACKET;
+#ifdef LOG_DEBUG
+ Log3(this, "NAV_PACKET");
+#endif
+ (*len) = 2048;
+ this->cur_cell_time = dvdnav_convert_time(&this->dsi.dsi_gi.c_eltm);
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+
+ /* If we've got here, it must just be a normal block. */
+ if(!this->file) {
+ printerr("Attempting to read without opening file.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_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 DVDNAV_STATUS_ERR;
+ }
+ (*event) = DVDNAV_BLOCK_OK;
+ (*len) = 2048;
+
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_get_title_string(dvdnav_t *this, const char **title_str) {
+ (*title_str) = this->vm->dvd_name;
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_get_serial_string(dvdnav_t *this, const char **serial_str) {
+ (*serial_str) = this->vm->dvd_serial;
+ return DVDNAV_STATUS_OK;
+}
+
+const char * dvdnav_get_volid_string(dvdnav_t *this) {
+ if (!this || !this->vm || !this->vm->dvd) {
+ printerr("Invalid state, vm or reader not available.");
+ return NULL;
+ }
+
+ char *volid_str = malloc(33);
+ if (volid_str == NULL) {
+ printerr("Insufficient memory available.");
+ return NULL;
+ }
+
+ if (DVDUDFVolumeInfo(this->vm->dvd, volid_str, 32, NULL, 0) == -1) {
+ if (DVDISOVolumeInfo(this->vm->dvd, volid_str, 33, NULL, 0) == -1) {
+ printerr("Failed to obtain volume id.");
+ free(volid_str);
+ return NULL;
+ }
+ }
+ return volid_str;
+}
+
+uint8_t dvdnav_get_video_aspect(dvdnav_t *this) {
+ uint8_t retval;
+
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ 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;
+}
+dvdnav_status_t dvdnav_get_video_resolution(dvdnav_t *this, uint32_t *width, uint32_t *height) {
+ int w, h;
+
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ vm_get_video_res(this->vm, &w, &h);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ *width = w;
+ *height = h;
+ return DVDNAV_STATUS_OK;
+}
+
+uint8_t dvdnav_get_video_scale_permission(dvdnav_t *this) {
+ uint8_t retval;
+
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ 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;
+
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return -1;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ attr = vm_get_audio_attr(this->vm, stream);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ if(attr.lang_type != 1)
+ return 0xffff;
+
+ return attr.lang_code;
+}
+
+uint16_t dvdnav_audio_stream_format(dvdnav_t *this, uint8_t stream) {
+ audio_attr_t attr;
+ uint16_t format;
+
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return -1; /* 0xffff */
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ attr = vm_get_audio_attr(this->vm, stream);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ switch(attr.audio_format) {
+ case 0:
+ format = DVD_AUDIO_FORMAT_AC3;
+ break;
+ case 2: /* MPEG-1 or MPEG-2 without extension bitstream. */
+ case 3: /* MPEG-2 with extension bitstream. */
+ format = DVD_AUDIO_FORMAT_MPEG2_EXT;
+ break;
+ case 4:
+ format = DVD_AUDIO_FORMAT_LPCM;
+ break;
+ case 6:
+ format = DVD_AUDIO_FORMAT_DTS;
+ break;
+ case 7:
+ format = DVD_AUDIO_FORMAT_SDDS;
+ break;
+ default:
+ format = 0xffff;
+ break;
+ }
+
+ return format;
+}
+
+uint16_t dvdnav_audio_stream_channels(dvdnav_t *this, uint8_t stream) {
+ audio_attr_t attr;
+
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return -1; /* 0xffff */
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ attr = vm_get_audio_attr(this->vm, stream);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return attr.channels + 1;
+}
+
+uint16_t dvdnav_spu_stream_to_lang(dvdnav_t *this, uint8_t stream) {
+ subp_attr_t attr;
+
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return -1;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ attr = vm_get_subp_attr(this->vm, stream);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ if(attr.type != 1)
+ return 0xffff;
+
+ return attr.lang_code;
+}
+
+int8_t dvdnav_get_audio_logical_stream(dvdnav_t *this, uint8_t audio_num) {
+ int8_t retval;
+
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return -1;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ if (!this->vm->state.pgc) {
+ printerr("No current 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_number_of_streams(dvdnav_t *this, dvdnav_stream_type_t stream_type) {
+
+ if (stream_type != DVD_SUBTITLE_STREAM && stream_type != DVD_AUDIO_STREAM) {
+ printerr("Invalid provided stream type");
+ return -1;
+ }
+
+ if (!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return -1;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ if (!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return -1;
+ }
+
+ if (this->vm->state.domain != DVD_DOMAIN_VTSTitle &&
+ this->vm->state.domain != DVD_DOMAIN_VTSMenu)
+ {
+ printerr("Invalid domain provided");
+ pthread_mutex_unlock(&this->vm_lock);
+ return -1;
+ }
+
+ int8_t count = 0;
+ switch (stream_type) {
+ case DVD_SUBTITLE_STREAM:
+ for (int i = 0; i < 32; i++)
+ {
+ if (this->vm->state.pgc->subp_control[i] & (1<<31))
+ count++;
+ }
+ break;
+ case DVD_AUDIO_STREAM:
+ for (int i = 0; i < 8; i++)
+ {
+ if (this->vm->state.pgc->audio_control[i] & (1<<15))
+ count++;
+ }
+ break;
+ }
+ pthread_mutex_unlock(&this->vm_lock);
+ return count;
+}
+
+dvdnav_status_t dvdnav_get_audio_attr(dvdnav_t *this, uint8_t audio_num, audio_attr_t *audio_attr) {
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return DVDNAV_STATUS_ERR;
+ }
+ pthread_mutex_lock(&this->vm_lock);
+ if (!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ *audio_attr=vm_get_audio_attr(this->vm, audio_num);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return DVDNAV_STATUS_OK;
+}
+
+int8_t dvdnav_get_spu_logical_stream(dvdnav_t *this, uint8_t subp_num) {
+ int8_t retval;
+
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return -1;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ if (!this->vm->state.pgc) {
+ printerr("No current 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;
+}
+
+dvdnav_status_t dvdnav_get_spu_attr(dvdnav_t *this, uint8_t audio_num, subp_attr_t *subp_attr) {
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return DVDNAV_STATUS_ERR;
+ }
+ pthread_mutex_lock(&this->vm_lock);
+ if (!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ *subp_attr=vm_get_subp_attr(this->vm, audio_num);
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+}
+
+int8_t dvdnav_get_active_audio_stream(dvdnav_t *this) {
+ int8_t retval;
+
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return -1;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ if (!this->vm->state.pgc) {
+ printerr("No current 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;
+}
+
+int8_t dvdnav_get_active_spu_stream(dvdnav_t *this) {
+ int8_t retval;
+
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return -1;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ if (!this->vm->state.pgc) {
+ printerr("No current 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;
+}
+
+dvdnav_status_t dvdnav_set_active_stream(dvdnav_t *this, uint8_t stream_num, dvdnav_stream_type_t stream_type) {
+ if (stream_type != DVD_SUBTITLE_STREAM && stream_type != DVD_AUDIO_STREAM) {
+ printerr("Invalid provided stream type");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ if (!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ if (!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+ if (this->vm->state.domain != DVD_DOMAIN_VTSTitle &&
+ this->vm->state.domain != DVD_DOMAIN_VTSMenu)
+ {
+ printerr("Invalid active domain");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+ switch (stream_type) {
+ case DVD_SUBTITLE_STREAM:
+ if (stream_num >= 32 ||
+ !(this->vm->state.pgc->subp_control[stream_num] & (1 << 31))) {
+ printerr("Invalid stream index not allowed");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ // set state without changing the current visibility
+ this->vm->state.SPST_REG = stream_num | (this->vm->state.SPST_REG & 0x40);
+ break;
+ case DVD_AUDIO_STREAM:
+ if (stream_num >= 8 ||
+ !(this->vm->state.pgc->audio_control[stream_num] & (1 << 15))) {
+ printerr("Invalid stream index not allowed");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ this->vm->state.AST_REG = stream_num;
+ break;
+ }
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_toggle_spu_stream(dvdnav_t *this, uint8_t visibility) {
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ switch(visibility) {
+ case 0: /* disable */
+ this->vm->state.SPST_REG &= ~0x40;
+ break;
+ case 1: /* enable */
+ this->vm->state.SPST_REG |= 0x40;
+ break;
+ default:
+ printerr("Invalid provided enabled_flag value");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+}
+
+static int8_t dvdnav_is_domain(dvdnav_t *this, DVDDomain_t domain) {
+ int8_t retval;
+
+ if(!this->started) {
+ printerr("Virtual DVD machine not 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, DVD_DOMAIN_FirstPlay);
+}
+/* Video management Menu domain. (Menu) */
+int8_t dvdnav_is_domain_vmgm(dvdnav_t *this) {
+ return dvdnav_is_domain(this, DVD_DOMAIN_VMGM);
+}
+/* Video Title Menu domain (Menu) */
+int8_t dvdnav_is_domain_vtsm(dvdnav_t *this) {
+ return dvdnav_is_domain(this, DVD_DOMAIN_VTSMenu);
+}
+/* Video Title domain (playing movie). */
+int8_t dvdnav_is_domain_vts(dvdnav_t *this) {
+ return dvdnav_is_domain(this, DVD_DOMAIN_VTSTitle);
+}
+
+/* Generally delegate angle information handling to VM */
+dvdnav_status_t dvdnav_angle_change(dvdnav_t *this, int32_t angle) {
+ int32_t num, current;
+
+ 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.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *this, int32_t *current_angle,
+ int32_t *number_of_angles) {
+ pthread_mutex_lock(&this->vm_lock);
+ vm_get_angle_info(this->vm, current_angle, number_of_angles);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_get_disk_region_mask(dvdnav_t *this, int32_t *region_mask) {
+ pthread_mutex_lock(&this->vm_lock);
+ if (!this->vm || !this->vm->vmgi || !this->vm->vmgi->vmgi_mat) {
+ printerr("Bad VM state.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+ (*region_mask) = ((this->vm->vmgi->vmgi_mat->vmg_category >> 16) & 0xff) ^ 0xff;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+}
+
+pci_t* dvdnav_get_current_nav_pci(dvdnav_t *this) {
+ if(!this) return 0;
+ return &this->pci;
+}
+
+dsi_t* dvdnav_get_current_nav_dsi(dvdnav_t *this) {
+ if(!this) return 0;
+ return &this->dsi;
+}
+
+uint32_t dvdnav_get_next_still_flag(dvdnav_t *this) {
+ if(!this) return -1;
+ return this->position_next.still;
+}
+
+user_ops_t dvdnav_get_restrictions(dvdnav_t* this) {
+ /*
+ * user_ops_t is a structure of 32 bits. We want to compute
+ * the union of two of those bitfields so to make this quicker
+ * than performing 32 ORs, we will access them as 32bits words.
+ */
+ union {
+ user_ops_t ops_struct;
+ uint32_t ops_int;
+ } ops, tmp;
+
+ ops.ops_int = 0;
+
+ if(!this || !this->started) {
+ printerr("Virtual DVD machine not started.");
+ return ops.ops_struct;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ ops.ops_struct = this->pci.pci_gi.vobu_uop_ctl;
+
+ if(this->vm && this->vm->state.pgc) {
+ tmp.ops_struct = this->vm->state.pgc->prohibited_ops;
+ ops.ops_int |= tmp.ops_int;
+ }
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return ops.ops_struct;
+}
+
+const char* dvdnav_version(void) {
+ return VERSION;
+}
diff --git a/libdvdnav-embedded/src/dvdnav/dvd_types.h b/libdvdnav-embedded/src/dvdnav/dvd_types.h
new file mode 100644
index 0000000..04547a1
--- /dev/null
+++ b/libdvdnav-embedded/src/dvdnav/dvd_types.h
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2000, 2001 Björn Englund, Håkan Hjort
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is a modified
+ * file originally part of the Ogle DVD player project.
+ *
+ * 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 libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * Various useful structs and enums for DVDs.
+ */
+
+#ifndef LIBDVDNAV_DVD_TYPES_H
+#define LIBDVDNAV_DVD_TYPES_H
+
+#include <stdint.h>
+
+/*
+ * DVD Menu ID
+ * (see dvdnav_menu_call())
+ */
+typedef enum {
+ /* When used in VTS domain, DVD_MENU_Escape behaves like DVD_MENU_Root,
+ * but from within a menu domain, DVD_MENU_Escape resumes playback. */
+ DVD_MENU_Escape = 0,
+ DVD_MENU_Title = 2,
+ DVD_MENU_Root = 3,
+ DVD_MENU_Subpicture = 4,
+ DVD_MENU_Audio = 5,
+ DVD_MENU_Angle = 6,
+ DVD_MENU_Part = 7
+} DVDMenuID_t;
+
+/*
+ * Stream Types
+ * (see dvdnav_get_number_of_streams())
+ */
+typedef enum {
+ DVD_SUBTITLE_STREAM = 0,
+ DVD_AUDIO_STREAM = 1
+} dvdnav_stream_type_t;
+
+/* Domain */
+typedef enum {
+ DVD_DOMAIN_FirstPlay = 1, /* First Play Domain */
+ DVD_DOMAIN_VTSTitle = 2, /* Video Title Set Domain */
+ DVD_DOMAIN_VMGM = 4, /* Video Manager Domain */
+ DVD_DOMAIN_VTSMenu = 8 /* Video Title Set Menu Domain */
+} DVDDomain_t;
+
+/*
+ * Structure containing info on highlight areas
+ * (see dvdnav_get_highlight_area())
+ */
+typedef struct {
+ 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 */
+
+ /* button number for the SPU decoder/overlaying engine */
+ uint32_t buttonN;
+} dvdnav_highlight_area_t;
+
+/* The audio format */
+typedef enum {
+ DVD_AUDIO_FORMAT_AC3 = 0,
+ DVD_AUDIO_FORMAT_UNKNOWN_1 = 1,
+ DVD_AUDIO_FORMAT_MPEG = 2,
+ DVD_AUDIO_FORMAT_MPEG2_EXT = 3,
+ DVD_AUDIO_FORMAT_LPCM = 4,
+ DVD_AUDIO_FORMAT_UNKNOWN_5 = 5,
+ DVD_AUDIO_FORMAT_DTS = 6,
+ DVD_AUDIO_FORMAT_SDDS = 7
+} DVDAudioFormat_t;
+
+/* the following types are currently unused */
+
+#if 0
+
+/* User operation permissions */
+typedef enum {
+ UOP_FLAG_TitleOrTimePlay = 0x00000001,
+ UOP_FLAG_ChapterSearchOrPlay = 0x00000002,
+ UOP_FLAG_TitlePlay = 0x00000004,
+ UOP_FLAG_Stop = 0x00000008,
+ UOP_FLAG_GoUp = 0x00000010,
+ UOP_FLAG_TimeOrChapterSearch = 0x00000020,
+ UOP_FLAG_PrevOrTopPGSearch = 0x00000040,
+ UOP_FLAG_NextPGSearch = 0x00000080,
+ UOP_FLAG_ForwardScan = 0x00000100,
+ UOP_FLAG_BackwardScan = 0x00000200,
+ UOP_FLAG_TitleMenuCall = 0x00000400,
+ UOP_FLAG_RootMenuCall = 0x00000800,
+ UOP_FLAG_SubPicMenuCall = 0x00001000,
+ UOP_FLAG_AudioMenuCall = 0x00002000,
+ UOP_FLAG_AngleMenuCall = 0x00004000,
+ UOP_FLAG_ChapterMenuCall = 0x00008000,
+ UOP_FLAG_Resume = 0x00010000,
+ UOP_FLAG_ButtonSelectOrActivate = 0x00020000,
+ UOP_FLAG_StillOff = 0x00040000,
+ UOP_FLAG_PauseOn = 0x00080000,
+ UOP_FLAG_AudioStreamChange = 0x00100000,
+ UOP_FLAG_SubPicStreamChange = 0x00200000,
+ UOP_FLAG_AngleChange = 0x00400000,
+ UOP_FLAG_KaraokeAudioPresModeChange = 0x00800000,
+ UOP_FLAG_VideoPresModeChange = 0x01000000
+} DVDUOP_t;
+
+/* Parental Level */
+typedef enum {
+ DVD_PARENTAL_LEVEL_1 = 1,
+ DVD_PARENTAL_LEVEL_2 = 2,
+ DVD_PARENTAL_LEVEL_3 = 3,
+ DVD_PARENTAL_LEVEL_4 = 4,
+ DVD_PARENTAL_LEVEL_5 = 5,
+ DVD_PARENTAL_LEVEL_6 = 6,
+ DVD_PARENTAL_LEVEL_7 = 7,
+ DVD_PARENTAL_LEVEL_8 = 8,
+ DVD_PARENTAL_LEVEL_None = 15
+} DVDParentalLevel_t;
+
+/* Language ID (ISO-639 language code) */
+typedef uint16_t DVDLangID_t;
+
+/* Country ID (ISO-3166 country code) */
+typedef uint16_t DVDCountryID_t;
+
+/* Register */
+typedef uint16_t DVDRegister_t;
+typedef enum {
+ DVDFalse = 0,
+ DVDTrue = 1
+} DVDBool_t;
+typedef DVDRegister_t DVDGPRMArray_t[16];
+typedef DVDRegister_t DVDSPRMArray_t[24];
+
+/* Navigation */
+typedef int DVDStream_t;
+typedef int DVDPTT_t;
+typedef int DVDTitle_t;
+
+/* Angle number (1-9 or default?) */
+typedef int DVDAngle_t;
+
+/* Timecode */
+typedef struct {
+ uint8_t Hours;
+ uint8_t Minutes;
+ uint8_t Seconds;
+ uint8_t Frames;
+} DVDTimecode_t;
+
+/* Subpicture stream number (0-31,62,63) */
+typedef int DVDSubpictureStream_t;
+
+/* Audio stream number (0-7, 15(none)) */
+typedef int DVDAudioStream_t;
+
+/* The audio application mode */
+typedef enum {
+ DVD_AUDIO_APP_MODE_None = 0,
+ DVD_AUDIO_APP_MODE_Karaoke = 1,
+ DVD_AUDIO_APP_MODE_Surround = 2,
+ DVD_AUDIO_APP_MODE_Other = 3
+} DVDAudioAppMode_t;
+
+/* Audio language extension */
+typedef enum {
+ DVD_AUDIO_LANG_EXT_NotSpecified = 0,
+ DVD_AUDIO_LANG_EXT_NormalCaptions = 1,
+ DVD_AUDIO_LANG_EXT_VisuallyImpaired = 2,
+ DVD_AUDIO_LANG_EXT_DirectorsComments1 = 3,
+ DVD_AUDIO_LANG_EXT_DirectorsComments2 = 4
+} DVDAudioLangExt_t;
+
+/* Subpicture language extension */
+typedef enum {
+ DVD_SUBPICTURE_LANG_EXT_NotSpecified = 0,
+ DVD_SUBPICTURE_LANG_EXT_NormalCaptions = 1,
+ DVD_SUBPICTURE_LANG_EXT_BigCaptions = 2,
+ DVD_SUBPICTURE_LANG_EXT_ChildrensCaptions = 3,
+ DVD_SUBPICTURE_LANG_EXT_NormalCC = 5,
+ DVD_SUBPICTURE_LANG_EXT_BigCC = 6,
+ DVD_SUBPICTURE_LANG_EXT_ChildrensCC = 7,
+ DVD_SUBPICTURE_LANG_EXT_Forced = 9,
+ DVD_SUBPICTURE_LANG_EXT_NormalDirectorsComments = 13,
+ DVD_SUBPICTURE_LANG_EXT_BigDirectorsComments = 14,
+ DVD_SUBPICTURE_LANG_EXT_ChildrensDirectorsComments = 15,
+} DVDSubpictureLangExt_t;
+
+/* Karaoke Downmix mode */
+typedef enum {
+ DVD_KARAOKE_DOWNMIX_0to0 = 0x0001,
+ DVD_KARAOKE_DOWNMIX_1to0 = 0x0002,
+ DVD_KARAOKE_DOWNMIX_2to0 = 0x0004,
+ DVD_KARAOKE_DOWNMIX_3to0 = 0x0008,
+ DVD_KARAOKE_DOWNMIX_4to0 = 0x0010,
+ DVD_KARAOKE_DOWNMIX_Lto0 = 0x0020,
+ DVD_KARAOKE_DOWNMIX_Rto0 = 0x0040,
+ DVD_KARAOKE_DOWNMIX_0to1 = 0x0100,
+ DVD_KARAOKE_DOWNMIX_1to1 = 0x0200,
+ DVD_KARAOKE_DOWNMIX_2to1 = 0x0400,
+ DVD_KARAOKE_DOWNMIX_3to1 = 0x0800,
+ DVD_KARAOKE_DOWNMIX_4to1 = 0x1000,
+ DVD_KARAOKE_DOWNMIX_Lto1 = 0x2000,
+ DVD_KARAOKE_DOWNMIX_Rto1 = 0x4000
+} DVDKaraokeDownmix_t;
+typedef int DVDKaraokeDownmixMask_t;
+
+/* Display mode */
+typedef enum {
+ DVD_DISPLAY_MODE_ContentDefault = 0,
+ DVD_DISPLAY_MODE_16x9 = 1,
+ DVD_DISPLAY_MODE_4x3PanScan = 2,
+ DVD_DISPLAY_MODE_4x3Letterboxed = 3
+} DVDDisplayMode_t;
+
+/* Audio attributes */
+typedef struct {
+ DVDAudioAppMode_t AppMode;
+ DVDAudioFormat_t AudioFormat;
+ DVDLangID_t Language;
+ DVDAudioLangExt_t LanguageExtension;
+ DVDBool_t HasMultichannelInfo;
+ DVDAudioSampleFreq_t SampleFrequency;
+ DVDAudioSampleQuant_t SampleQuantization;
+ DVDChannelNumber_t NumberOfChannels;
+} DVDAudioAttributes_t;
+typedef int DVDAudioSampleFreq_t;
+typedef int DVDAudioSampleQuant_t;
+typedef int DVDChannelNumber_t;
+
+/* Subpicture attributes */
+typedef enum {
+ DVD_SUBPICTURE_TYPE_NotSpecified = 0,
+ DVD_SUBPICTURE_TYPE_Language = 1,
+ DVD_SUBPICTURE_TYPE_Other = 2
+} DVDSubpictureType_t;
+typedef enum {
+ DVD_SUBPICTURE_CODING_RunLength = 0,
+ DVD_SUBPICTURE_CODING_Extended = 1,
+ DVD_SUBPICTURE_CODING_Other = 2
+} DVDSubpictureCoding_t;
+typedef struct {
+ DVDSubpictureType_t Type;
+ DVDSubpictureCoding_t CodingMode;
+ DVDLangID_t Language;
+ DVDSubpictureLangExt_t LanguageExtension;
+} DVDSubpictureAttributes_t;
+
+/* Video attributes */
+typedef struct {
+ DVDBool_t PanscanPermitted;
+ DVDBool_t LetterboxPermitted;
+ int AspectX;
+ int AspectY;
+ int FrameRate;
+ int FrameHeight;
+ DVDVideoCompression_t Compression;
+ DVDBool_t Line21Field1InGop;
+ DVDBool_t Line21Field2InGop;
+ int more_to_come;
+} DVDVideoAttributes_t;
+typedef int DVDVideoCompression_t;
+
+#endif
+
+#endif /* LIBDVDNAV_DVD_TYPES_H */
diff --git a/libdvdnav-embedded/src/dvdnav/dvdnav.h b/libdvdnav-embedded/src/dvdnav/dvdnav.h
new file mode 100644
index 0000000..85136a4
--- /dev/null
+++ b/libdvdnav-embedded/src/dvdnav/dvdnav.h
@@ -0,0 +1,797 @@
+/*
+ * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * 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 libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * This is the main header file applications should include if they want
+ * to access dvdnav functionality.
+ */
+
+#ifndef LIBDVDNAV_DVDNAV_H
+#define LIBDVDNAV_DVDNAV_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "version.h"
+#include <dvdnav/dvd_types.h>
+#include <dvdread/dvd_reader.h>
+#include <dvdread/nav_types.h>
+#include <dvdnav/dvdnav_events.h>
+
+#include <stdarg.h>
+
+/*********************************************************************
+ * dvdnav data types *
+ *********************************************************************/
+
+/*
+ * Opaque data-type can be viewed as a 'DVD handle'. You should get
+ * a pointer to a dvdnav_t from the dvdnav_open() function.
+ * Never call free() on the pointer, you have to give it back with
+ * dvdnav_close().
+ */
+typedef struct dvdnav_s dvdnav_t;
+
+/* Status as reported by most of libdvdnav's functions */
+typedef int32_t dvdnav_status_t;
+
+typedef dvd_reader_stream_cb dvdnav_stream_cb;
+
+/*
+ * Unless otherwise stated, all functions return DVDNAV_STATUS_OK if
+ * they succeeded, otherwise DVDNAV_STATUS_ERR is returned and the error may
+ * be obtained by calling dvdnav_err_to_string().
+ */
+#define DVDNAV_STATUS_ERR 0
+#define DVDNAV_STATUS_OK 1
+
+/*********************************************************************
+ * initialisation & housekeeping functions *
+ *********************************************************************/
+
+/*
+ * Logger callback definition
+ */
+typedef enum
+{
+ DVDNAV_LOGGER_LEVEL_INFO,
+ DVDNAV_LOGGER_LEVEL_ERROR,
+ DVDNAV_LOGGER_LEVEL_WARN,
+ DVDNAV_LOGGER_LEVEL_DEBUG,
+} dvdnav_logger_level_t;
+
+typedef struct
+{
+ void ( *pf_log ) ( void *, dvdnav_logger_level_t, const char *, va_list );
+} dvdnav_logger_cb;
+
+/*
+ * These functions allow you to open a DVD device and associate it
+ * with a dvdnav_t.
+ */
+
+/*
+ * Attempts to open the DVD drive at the specified path or using external
+ * seek/read functions (dvdnav_open_stream) and pre-cache the CSS-keys.
+ * libdvdread is used to access the DVD, so any source supported by libdvdread
+ * can be given with "path" or "stream_cb". Currently, using dvdnav_open,
+ * libdvdread can access : DVD drives, DVD image files, DVD file-by-file
+ * copies. Using dvdnav_open_stream, libdvdread can access any kind of DVD
+ * storage via custom implementation of seek/read functions.
+ *
+ * The resulting dvdnav_t handle will be written to *dest.
+ */
+dvdnav_status_t dvdnav_open(dvdnav_t **dest, const char *path);
+dvdnav_status_t
+dvdnav_open_stream(dvdnav_t **dest, void *priv, dvdnav_stream_cb *stream_cb);
+
+dvdnav_status_t dvdnav_open2(dvdnav_t **dest,
+ void *, const dvdnav_logger_cb *,
+ const char *path);
+dvdnav_status_t dvdnav_open_stream2(dvdnav_t **dest,
+ void *priv, const dvdnav_logger_cb *,
+ dvdnav_stream_cb *stream_cb);
+
+dvdnav_status_t dvdnav_dup(dvdnav_t **dest, dvdnav_t *src);
+dvdnav_status_t dvdnav_free_dup(dvdnav_t * _this);
+
+/*
+ * Closes a dvdnav_t previously opened with dvdnav_open(), freeing any
+ * memory associated with it.
+ */
+dvdnav_status_t dvdnav_close(dvdnav_t *self);
+
+/*
+ * Resets the DVD virtual machine and cache buffers.
+ */
+dvdnav_status_t dvdnav_reset(dvdnav_t *self);
+
+/*
+ * Fills a pointer with a value pointing to a string describing
+ * the path associated with an open dvdnav_t. It assigns *path to NULL
+ * on error.
+ */
+dvdnav_status_t dvdnav_path(dvdnav_t *self, const char **path);
+
+/*
+ * Returns a human-readable string describing the last error.
+ */
+const char* dvdnav_err_to_string(dvdnav_t *self);
+
+const char* dvdnav_version(void);
+
+/*********************************************************************
+ * changing and reading DVD player characteristics *
+ *********************************************************************/
+
+/*
+ * These functions allow you to manipulate the various global characteristics
+ * of the DVD playback engine.
+ */
+
+/*
+ * Sets the region mask (bit 0 set implies region 1, bit 1 set implies
+ * region 2, etc) of the virtual machine. Generally you will only need to set
+ * this if you are playing RCE discs which query the virtual machine as to its
+ * region setting.
+ *
+ * This has _nothing_ to do with the region setting of the DVD drive.
+ */
+dvdnav_status_t dvdnav_set_region_mask(dvdnav_t *self, int32_t region_mask);
+
+/*
+ * Returns the region mask (bit 0 set implies region 1, bit 1 set implies
+ * region 2, etc) of the virtual machine.
+ *
+ * This has _nothing_ to do with the region setting of the DVD drive.
+ */
+dvdnav_status_t dvdnav_get_region_mask(dvdnav_t *self, int32_t *region_mask);
+
+/*
+ * Specify whether read-ahead caching should be used. You may not want this if your
+ * decoding engine does its own buffering.
+ *
+ * The default read-ahead cache does not use an additional thread for the reading
+ * (see read_cache.c for a threaded cache, but note that this code is currently
+ * unmaintained). It prebuffers on VOBU level by reading ahead several buffers
+ * on every read request. The speed of this prebuffering has been optimized to
+ * also work on slow DVD drives.
+ *
+ * If in addition you want to prevent memcpy's to improve performance, have a look
+ * at dvdnav_get_next_cache_block().
+ */
+dvdnav_status_t dvdnav_set_readahead_flag(dvdnav_t *self, int32_t read_ahead_flag);
+
+/*
+ * Query whether read-ahead caching/buffering will be used.
+ */
+dvdnav_status_t dvdnav_get_readahead_flag(dvdnav_t *self, int32_t *read_ahead_flag);
+
+/*
+ * Specify whether the positioning works PGC or PG based.
+ * Programs (PGs) on DVDs are similar to Chapters and a program chain (PGC)
+ * usually covers a whole feature. This affects the behaviour of the
+ * functions dvdnav_get_position() and dvdnav_sector_search(). See there.
+ * Default is PG based positioning.
+ */
+dvdnav_status_t dvdnav_set_PGC_positioning_flag(dvdnav_t *self, int32_t pgc_based_flag);
+
+/*
+ * Query whether positioning is PG or PGC based.
+ */
+dvdnav_status_t dvdnav_get_PGC_positioning_flag(dvdnav_t *self, int32_t *pgc_based_flag);
+
+
+/*********************************************************************
+ * reading data *
+ *********************************************************************/
+
+/*
+ * These functions are used to poll the playback engine and actually get data
+ * off the DVD.
+ */
+
+/*
+ * Attempts to get the next block off the DVD and copies it into the buffer 'buf'.
+ * If there is any special actions that may need to be performed, the value
+ * pointed to by 'event' gets set accordingly.
+ *
+ * If 'event' is DVDNAV_BLOCK_OK then 'buf' is filled with the next block
+ * (note that means it has to be at /least/ 2048 bytes big). 'len' is
+ * then set to 2048.
+ *
+ * Otherwise, buf is filled with an appropriate event structure and
+ * len is set to the length of that structure.
+ *
+ * See the dvdnav_events.h header for information on the various events.
+ */
+dvdnav_status_t dvdnav_get_next_block(dvdnav_t *self, uint8_t *buf,
+ int32_t *event, int32_t *len);
+
+/*
+ * This basically does the same as dvdnav_get_next_block. The only difference is
+ * that it avoids a memcopy, when the requested block was found in the cache.
+ * In 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 pointers must _never_ be freed but instead returned to the library via
+ * dvdnav_free_cache_block().
+ */
+dvdnav_status_t dvdnav_get_next_cache_block(dvdnav_t *self, uint8_t **buf,
+ int32_t *event, int32_t *len);
+
+/*
+ * 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.
+ */
+dvdnav_status_t dvdnav_free_cache_block(dvdnav_t *self, unsigned char *buf);
+
+/*
+ * If we are currently in a still-frame this function skips it.
+ *
+ * See also the DVDNAV_STILL_FRAME event.
+ */
+dvdnav_status_t dvdnav_still_skip(dvdnav_t *self);
+
+/*
+ * If we are currently in WAIT state, that is: the application is required to
+ * wait for its fifos to become empty, calling this signals libdvdnav that this
+ * is achieved and that it can continue.
+ *
+ * See also the DVDNAV_WAIT event.
+ */
+dvdnav_status_t dvdnav_wait_skip(dvdnav_t *self);
+
+/*
+ * Returns the still time from the currently playing cell.
+ * The still time is given in seconds with 0xff meaning an indefinite still.
+ *
+ * This function can be used to detect still frames before they are reached.
+ * Some players might need this to prepare for a frame to be shown for a
+ * longer time than usual.
+ */
+uint32_t dvdnav_get_next_still_flag(dvdnav_t *self);
+
+/*
+ * Stops playback. The next event obtained with one of the get_next_block
+ * functions will be a DVDNAV_STOP event.
+ *
+ * It is not required to call this before dvdnav_close().
+ */
+dvdnav_status_t dvdnav_stop(dvdnav_t *self);
+
+/*
+ * Returns the region mask (bit 0 set implies region 1, bit 1 set implies
+ * region 2, etc) reported by the dvd disc being played.
+ *
+ * Note this has no relation with the region setting of the DVD drive.
+ * Old DVD drives (RPC-I) used to delegate most of the RCE handling to the CPU and
+ * will actually call the virtual machine (VM) for its region setting. In those cases,
+ * changing the VM region mask via dvdnav_set_region_mask() will circunvent
+ * the region protection scheme. This is no longer the case with more recent (RPC-II) drives
+ * as RCE is handled internally by the drive firmware.
+ *
+ */
+dvdnav_status_t dvdnav_get_disk_region_mask(dvdnav_t *self, int32_t *region_mask);
+
+/*********************************************************************
+ * title/part navigation *
+ *********************************************************************/
+
+/*
+ * Returns the number of titles on the disk.
+ */
+dvdnav_status_t dvdnav_get_number_of_titles(dvdnav_t *self, int32_t *titles);
+
+/*
+ * Returns the number of parts within the given title.
+ */
+dvdnav_status_t dvdnav_get_number_of_parts(dvdnav_t *self, int32_t title, int32_t *parts);
+
+/*
+ * Returns the number of angles for the given title.
+ */
+dvdnav_status_t dvdnav_get_number_of_angles(dvdnav_t *self, int32_t title, int32_t *angles);
+
+/*
+ * Plays the specified title of the DVD from its beginning (that is: part 1).
+ */
+dvdnav_status_t dvdnav_title_play(dvdnav_t *self, int32_t title);
+
+/*
+ * Plays the specified title, starting from the specified part.
+ */
+dvdnav_status_t dvdnav_part_play(dvdnav_t *self, int32_t title, int32_t part);
+
+/*
+ * Plays the specified title, starting from the specified program
+ */
+dvdnav_status_t dvdnav_program_play(dvdnav_t *self, int32_t title, int32_t pgcn, int32_t pgn);
+
+/*
+ * Stores in *times an array (that the application *must* free) of
+ * dvdtimes corresponding to the chapter times for the chosen title.
+ * *duration will have the duration of the title
+ * The number of entries in *times is the result of the function.
+ * On error *times is NULL and the output is 0
+ */
+uint32_t dvdnav_describe_title_chapters(dvdnav_t *self, int32_t title, uint64_t **times, uint64_t *duration);
+
+/*
+ * Play the specified amount of parts of the specified title of
+ * the DVD then STOP.
+ *
+ * Currently unimplemented!
+ */
+dvdnav_status_t dvdnav_part_play_auto_stop(dvdnav_t *self, int32_t title,
+ int32_t part, int32_t parts_to_play);
+
+/*
+ * Play the specified title starting from the specified time.
+ *
+ * Currently unimplemented!
+ */
+dvdnav_status_t dvdnav_time_play(dvdnav_t *self, int32_t title,
+ uint64_t time);
+
+/*
+ * Stop playing the current position and jump to the specified menu.
+ *
+ * See also DVDMenuID_t from libdvdread
+ */
+dvdnav_status_t dvdnav_menu_call(dvdnav_t *self, DVDMenuID_t menu);
+
+/*
+ * Return the title number and part currently being played.
+ * A title of 0 indicates we are in a menu. In this case, part
+ * is set to the current menu's ID.
+ */
+dvdnav_status_t dvdnav_current_title_info(dvdnav_t *self, int32_t *title,
+ int32_t *part);
+
+/*
+ * Return the title number, pgcn and pgn currently being played.
+ * A title of 0 indicates, we are in a menu.
+ */
+dvdnav_status_t dvdnav_current_title_program(dvdnav_t *self, int32_t *title,
+ int32_t *pgcn, int32_t *pgn);
+
+/*
+ * Return the current position (in blocks) within the current
+ * title and the length (in blocks) of said title.
+ *
+ * Current implementation is wrong and likely to behave unpredictably!
+ * Use is discouraged!
+ */
+dvdnav_status_t dvdnav_get_position_in_title(dvdnav_t *self,
+ uint32_t *pos,
+ uint32_t *len);
+
+/*
+ * This function is only available for compatibility reasons.
+ *
+ * Stop playing the current position and start playback of the current title
+ * from the specified part.
+ */
+dvdnav_status_t dvdnav_part_search(dvdnav_t *self, int32_t part);
+
+
+/*********************************************************************
+ * program chain/program navigation *
+ *********************************************************************/
+
+/*
+ * Stop playing the current position and start playback from the last
+ * VOBU boundary before the given sector. The sector number is not
+ * meant to be an absolute physical DVD sector, but a relative sector
+ * in the current program. This function cannot leave the current
+ * program and will fail if asked to do so.
+ *
+ * If program chain based positioning is enabled
+ * (see dvdnav_set_PGC_positioning_flag()), this will seek to the relative
+ * sector inside the current program chain.
+ *
+ * 'origin' can be one of SEEK_SET, SEEK_CUR, SEEK_END as defined in
+ * fcntl.h.
+ */
+dvdnav_status_t dvdnav_sector_search(dvdnav_t *self,
+ int64_t offset, int32_t origin);
+
+/*
+ returns the current stream time in PTS ticks as reported by the IFO structures
+ divide it by 90000 to get the current play time in seconds
+ */
+int64_t dvdnav_get_current_time(dvdnav_t *self);
+
+/*
+ * Stop playing the current position and start playback of the title
+ * from the specified timecode.
+ *
+ * Currently implemented using interpolation. That interpolation is slightly
+ * inaccurate.
+ */
+dvdnav_status_t dvdnav_time_search(dvdnav_t *self,
+ uint64_t time);
+
+/*
+ * Find the nearest vobu and jump to it
+ *
+ * Alternative to dvdnav_time_search (see full documentation on searching.jump_to_time.readme)
+ * Jumps to the provided PTS (which is defined as time_in_ms * 90). mode means the navigation mode,
+ * currently only the Default (0) is implemented:
+ * 0: Default. Jump to a time which may be either <> time_in_pts_ticks
+ * 1: After. Always jump to a time that is > time_in_pts_ticks
+ * -1: Before. Always jump to a time that is < time_in_pts_ticks
+ */
+dvdnav_status_t dvdnav_jump_to_sector_by_time(dvdnav_t *self,
+ uint64_t time_in_pts_ticks, int32_t mode);
+
+/*
+ * Stop playing current position and play the "GoUp"-program chain.
+ * (which generally leads to the title menu or a higher-level menu).
+ */
+dvdnav_status_t dvdnav_go_up(dvdnav_t *self);
+
+/*
+ * Stop playing the current position and start playback at the
+ * previous program (if it exists).
+ */
+dvdnav_status_t dvdnav_prev_pg_search(dvdnav_t *self);
+
+/*
+ * Stop playing the current position and start playback at the
+ * first program.
+ */
+dvdnav_status_t dvdnav_top_pg_search(dvdnav_t *self);
+
+/*
+ * Stop playing the current position and start playback at the
+ * next program (if it exists).
+ */
+dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *self);
+
+/*
+ * Return the current position (in blocks) within the current
+ * program and the length (in blocks) of current program.
+ *
+ * If program chain based positioning is enabled
+ * (see dvdnav_set_PGC_positioning_flag()), this will return the
+ * relative position in and the length of the current program chain.
+ */
+dvdnav_status_t dvdnav_get_position(dvdnav_t *self, uint32_t *pos,
+ uint32_t *len);
+
+
+/*********************************************************************
+ * menu highlights *
+ *********************************************************************/
+
+/*
+ * Most functions related to highlights take a NAV PCI packet as a parameter.
+ * While you can get such a packet from libdvdnav, this will result in
+ * errors for players with internal FIFOs 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.
+ */
+
+/*
+ * Get the currently highlighted button
+ * number (1..36) or 0 if no button is highlighted.
+ */
+dvdnav_status_t dvdnav_get_current_highlight(dvdnav_t *self, int32_t *button);
+
+/*
+ * Returns the Presentation Control Information (PCI) structure associated
+ * with the current position.
+ *
+ * Read the general notes above.
+ * See also libdvdreads nav_types.h for definition of pci_t.
+ */
+pci_t* dvdnav_get_current_nav_pci(dvdnav_t *self);
+
+/*
+ * Returns the DSI (data search information) structure associated
+ * with the current position.
+ *
+ * Read the general notes above.
+ * See also libdvdreads nav_types.h for definition of dsi_t.
+ */
+dsi_t* dvdnav_get_current_nav_dsi(dvdnav_t *self);
+
+/*
+ * Get the area associated with a certain button.
+ */
+dvdnav_status_t dvdnav_get_highlight_area(pci_t *nav_pci , int32_t button, int32_t mode,
+ dvdnav_highlight_area_t *highlight);
+
+/*
+ * Move button highlight around as suggested by function name (e.g. with arrow keys).
+ */
+dvdnav_status_t dvdnav_upper_button_select(dvdnav_t *self, pci_t *pci);
+dvdnav_status_t dvdnav_lower_button_select(dvdnav_t *self, pci_t *pci);
+dvdnav_status_t dvdnav_right_button_select(dvdnav_t *self, pci_t *pci);
+dvdnav_status_t dvdnav_left_button_select(dvdnav_t *self, pci_t *pci);
+
+/*
+ * Activate ("press") the currently highlighted button.
+ */
+dvdnav_status_t dvdnav_button_activate(dvdnav_t *self, pci_t *pci);
+
+/*
+ * Highlight a specific button.
+ */
+dvdnav_status_t dvdnav_button_select(dvdnav_t *self, pci_t *pci, int32_t button);
+
+/*
+ * Activate ("press") specified button.
+ */
+dvdnav_status_t dvdnav_button_select_and_activate(dvdnav_t *self, pci_t *pci, int32_t button);
+
+/*
+ * Activate ("press") a button and execute specified command.
+ */
+dvdnav_status_t dvdnav_button_activate_cmd(dvdnav_t *self, int32_t button, vm_cmd_t *cmd);
+
+/*
+ * Select button at specified video frame coordinates.
+ */
+dvdnav_status_t dvdnav_mouse_select(dvdnav_t *self, pci_t *pci, int32_t x, int32_t y);
+
+/*
+ * Activate ("press") button at specified video frame coordinates.
+ */
+dvdnav_status_t dvdnav_mouse_activate(dvdnav_t *self, pci_t *pci, int32_t x, int32_t y);
+
+
+/*********************************************************************
+ * languages *
+ *********************************************************************/
+
+/*
+ * The language codes expected by these functions are two character
+ * codes as defined in ISO639.
+ */
+
+/*
+ * Set which menu language we should use per default.
+ */
+dvdnav_status_t dvdnav_menu_language_select(dvdnav_t *self,
+ char *code);
+
+/*
+ * Set which audio language we should use per default.
+ */
+dvdnav_status_t dvdnav_audio_language_select(dvdnav_t *self,
+ char *code);
+
+/*
+ * Set which spu language we should use per default.
+ */
+dvdnav_status_t dvdnav_spu_language_select(dvdnav_t *self,
+ char *code);
+
+
+/*********************************************************************
+ * obtaining stream attributes *
+ *********************************************************************/
+
+/*
+ * Return a string describing the title of the DVD.
+ * This is an ID string encoded on the disc by the author. In many cases
+ * this is a descriptive string such as `THE_MATRIX' but sometimes is singularly
+ * uninformative such as `PDVD-011421'. Some DVD authors even forget to set this,
+ * so you may also read the default of the authoring software they used, like
+ * `DVDVolume' (see also dvdnav_get_volid_string).
+ */
+dvdnav_status_t dvdnav_get_title_string(dvdnav_t *self, const char **title_str);
+
+/*
+ * Returns a string containing the serial number of the DVD.
+ * This has a max of 15 characters and should be more unique than the
+ * title string.
+ */
+dvdnav_status_t dvdnav_get_serial_string(dvdnav_t *self, const char **serial_str);
+
+/*
+ * Returns the VolumeIdentifier of the disc or NULL if it could
+ * not be obtained. The VolumeIdentifier might be latin-1 encoded
+ * (8bit unicode) null terminated and max 32 bytes (including '\0');
+ * or coded with '0-9','A-Z','_' null terminated and max 33 bytes
+ * (including '\0').
+ * See also dvdnav_get_title_string
+ *
+ * Note: The string is malloc'd so caller has to free() the returned
+ * string when done with it.
+ */
+const char * dvdnav_get_volid_string(dvdnav_t *self);
+
+/*
+ * Get video aspect code.
+ * The aspect code does only change on VTS boundaries.
+ * See the DVDNAV_VTS_CHANGE event.
+ *
+ * 0 -- 4:3, 2 -- 16:9
+ */
+uint8_t dvdnav_get_video_aspect(dvdnav_t *self);
+
+/*
+ * Get video resolution.
+ */
+dvdnav_status_t dvdnav_get_video_resolution(dvdnav_t *self, uint32_t *width, uint32_t *height);
+
+/*
+ * Get video scaling permissions.
+ * The scaling permission does only change on VTS boundaries.
+ * See the DVDNAV_VTS_CHANGE event.
+ *
+ * bit0 set = deny letterboxing, bit1 set = deny pan&scan
+ */
+uint8_t dvdnav_get_video_scale_permission(dvdnav_t *self);
+
+/*
+ * Converts a *logical* audio stream id into language code
+ * (returns 0xffff if no such stream).
+ */
+uint16_t dvdnav_audio_stream_to_lang(dvdnav_t *self, uint8_t stream);
+
+/*
+ * Returns the format of *logical* audio stream 'stream'
+ * (returns 0xffff if no such stream).
+ */
+uint16_t dvdnav_audio_stream_format(dvdnav_t *self, uint8_t stream);
+
+/*
+ * Returns number of channels in *logical* audio stream 'stream'
+ * (returns 0xffff if no such stream).
+ */
+uint16_t dvdnav_audio_stream_channels(dvdnav_t *self, uint8_t stream);
+
+/*
+ * Converts a *logical* subpicture stream id into country code
+ * (returns 0xffff if no such stream).
+ */
+uint16_t dvdnav_spu_stream_to_lang(dvdnav_t *self, uint8_t stream);
+
+/*
+ * Converts a *physical* (MPEG) audio stream id into a logical stream number.
+ */
+int8_t dvdnav_get_audio_logical_stream(dvdnav_t *self, uint8_t audio_num);
+
+#define HAVE_GET_AUDIO_ATTR
+/*
+ * Get audio attr
+ */
+dvdnav_status_t dvdnav_get_audio_attr(dvdnav_t *self, uint8_t audio_mum, audio_attr_t *audio_attr);
+
+/*
+ * Converts a *physical* (MPEG) subpicture stream id into a logical stream number.
+ */
+int8_t dvdnav_get_spu_logical_stream(dvdnav_t *self, uint8_t subp_num);
+
+#define HAVE_GET_SPU_ATTR
+/*
+ * Get spu attr
+ */
+dvdnav_status_t dvdnav_get_spu_attr(dvdnav_t *self, uint8_t audio_mum, subp_attr_t *subp_attr);
+
+/*
+ * Get active audio stream.
+ */
+int8_t dvdnav_get_active_audio_stream(dvdnav_t *self);
+
+/*
+ * Get active spu stream.
+ */
+int8_t dvdnav_get_active_spu_stream(dvdnav_t *self);
+
+/*
+ * Get the set of user operations that are currently prohibited.
+ * There are potentially new restrictions right after
+ * DVDNAV_CHANNEL_HOP and DVDNAV_NAV_PACKET.
+ */
+user_ops_t dvdnav_get_restrictions(dvdnav_t *self);
+
+/*
+ * Returns the number of streams provided its type (e.g. subtitles, audio, etc)
+ */
+int8_t dvdnav_get_number_of_streams(dvdnav_t *self, dvdnav_stream_type_t stream_type);
+
+
+/*********************************************************************
+ * setting stream attributes *
+ *********************************************************************/
+
+/*
+ * Set the visible (enable) status of the current spu stream
+ * (to enable/disable subtitles)
+ * visibility defines if the spu stream should be enabled/visible (1) or disabled (0)
+ */
+dvdnav_status_t dvdnav_toggle_spu_stream(dvdnav_t *self, uint8_t visibility);
+
+/*
+ * Set the given stream id and stream type as active
+ * stream_num - the physical index of the stream
+ * stream_type - the stream type (audio or subtitles)
+ */
+dvdnav_status_t dvdnav_set_active_stream(dvdnav_t *self, uint8_t stream_num,
+ dvdnav_stream_type_t stream_type);
+
+/*********************************************************************
+ * multiple angles *
+ *********************************************************************/
+
+/*
+ * The libdvdnav library abstracts away the difference between seamless and
+ * non-seamless angles. From the point of view of the programmer you just set the
+ * angle number and all is well in the world. You will always see only the
+ * selected angle coming from the get_next_block functions.
+ *
+ * Note:
+ * It is quite possible that some tremendously strange DVD feature might change the
+ * angle number from under you. Generally you should always view the results from
+ * dvdnav_get_angle_info() as definitive only up to the next time you call
+ * dvdnav_get_next_block().
+ */
+
+/*
+ * Sets the current angle. If you try to follow a non existent angle
+ * the call fails.
+ */
+dvdnav_status_t dvdnav_angle_change(dvdnav_t *self, int32_t angle);
+
+/*
+ * Returns the current angle and number of angles present.
+ */
+dvdnav_status_t dvdnav_get_angle_info(dvdnav_t *self, int32_t *current_angle,
+ int32_t *number_of_angles);
+
+/*********************************************************************
+ * domain queries *
+ *********************************************************************/
+
+/*
+ * Are we in the First Play domain?
+ */
+int8_t dvdnav_is_domain_fp(dvdnav_t *self);
+
+/*
+ * Are we in the Video management Menu domain?
+ */
+int8_t dvdnav_is_domain_vmgm(dvdnav_t *self);
+
+/*
+ * Are we in the Video Title Menu domain?
+ */
+int8_t dvdnav_is_domain_vtsm(dvdnav_t *self);
+
+/*
+ * Are we in the Video Title Set domain?
+ */
+int8_t dvdnav_is_domain_vts(dvdnav_t *self);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LIBDVDNAV_DVDNAV_H */
diff --git a/libdvdnav-embedded/src/dvdnav/dvdnav_events.h b/libdvdnav-embedded/src/dvdnav/dvdnav_events.h
new file mode 100644
index 0000000..a7dc6da
--- /dev/null
+++ b/libdvdnav-embedded/src/dvdnav/dvdnav_events.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * 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 libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * This header defines events and event types
+ */
+
+#ifndef LIBDVDNAV_DVDNAV_EVENTS_H
+#define LIBDVDNAV_DVDNAV_EVENTS_H
+
+/*
+ * DVDNAV_BLOCK_OK
+ *
+ * A regular data block from the DVD has been returned.
+ * This one should be demuxed and decoded for playback.
+ */
+#define DVDNAV_BLOCK_OK 0
+
+
+/*
+ * DVDNAV_NOP
+ *
+ * Just ignore this.
+ */
+#define DVDNAV_NOP 1
+
+
+/*
+ * DVDNAV_STILL_FRAME
+ *
+ * We have reached a still frame. The player application should wait
+ * the amount of time specified by the still's length while still handling
+ * user input to make menus and other interactive stills work.
+ * The last delivered frame should be kept showing.
+ * Once the still has timed out, call dvdnav_skip_still().
+ * A length of 0xff means an infinite still which has to be skipped
+ * indirectly by some user interaction.
+ */
+#define DVDNAV_STILL_FRAME 2
+
+typedef struct {
+ /* The length (in seconds) the still frame should be displayed for,
+ * or 0xff if infinite. */
+ int length;
+} dvdnav_still_event_t;
+
+
+/*
+ * DVDNAV_SPU_STREAM_CHANGE
+ *
+ * Inform the SPU decoding/overlaying engine to switch SPU channels.
+ */
+#define DVDNAV_SPU_STREAM_CHANGE 3
+
+typedef struct {
+ /* The physical (MPEG) stream number for widescreen SPU display.
+ * Use this, if you blend the SPU on an anamorphic image before
+ * unsqueezing it. */
+ int physical_wide;
+
+ /* The physical (MPEG) stream number for letterboxed display.
+ * Use this, if you blend the SPU on an anamorphic image after
+ * unsqueezing it. */
+ int physical_letterbox;
+
+ /* The physical (MPEG) stream number for pan&scan display.
+ * Use this, if you blend the SPU on an anamorphic image after
+ * unsqueezing it the pan&scan way. */
+ int physical_pan_scan;
+
+ /* The logical (DVD) stream number. */
+ int logical;
+} dvdnav_spu_stream_change_event_t;
+
+
+/*
+ * DVDNAV_AUDIO_STREAM_CHANGE
+ *
+ * Inform the audio decoder to switch channels.
+ */
+#define DVDNAV_AUDIO_STREAM_CHANGE 4
+
+typedef struct {
+ /* The physical (MPEG) stream number. */
+ int physical;
+
+ /* The logical (DVD) stream number. */
+ int logical;
+} dvdnav_audio_stream_change_event_t;
+
+
+/*
+ * DVDNAV_VTS_CHANGE
+ *
+ * Some status information like video aspect and video scale permissions do
+ * not change inside a VTS. Therefore this event can be used to query such
+ * information only when necessary and update the decoding/displaying
+ * accordingly.
+ */
+#define DVDNAV_VTS_CHANGE 5
+
+typedef struct {
+ int old_vtsN; /* the old VTS number */
+ DVDDomain_t old_domain; /* the old domain */
+ int new_vtsN; /* the new VTS number */
+ DVDDomain_t new_domain; /* the new domain */
+} dvdnav_vts_change_event_t;
+
+
+/*
+ * DVDNAV_CELL_CHANGE
+ *
+ * Some status information like the current Title and Part numbers do not
+ * change inside a cell. Therefore this event can be used to query such
+ * information only when necessary and update the decoding/displaying
+ * accordingly.
+ * Some useful information for accurate time display is also reported
+ * together with this event.
+ */
+#define DVDNAV_CELL_CHANGE 6
+
+typedef struct {
+ int cellN; /* the new cell number */
+ int pgN; /* the current program number */
+ int64_t cell_length; /* the length of the current cell in sectors */
+ int64_t pg_length; /* the length of the current program in sectors */
+ int64_t pgc_length; /* the length of the current program chain in PTS ticks */
+ int64_t cell_start; /* the start offset of the current cell relatively to the PGC in sectors */
+ int64_t pg_start; /* the start offset of the current PG relatively to the PGC in sectors */
+} dvdnav_cell_change_event_t;
+
+
+/*
+ * DVDNAV_NAV_PACKET
+ *
+ * NAV packets are useful for various purposes. They define the button
+ * highlight areas and VM commands of DVD menus, so they should in any
+ * case be sent to the SPU decoder/overlaying engine for the menus to work.
+ * NAV packets also provide a way to detect PTS discontinuities, because
+ * they carry the start and end PTS values for the current VOBU.
+ * (pci.vobu_s_ptm and pci.vobu_e_ptm) Whenever the start PTS of the
+ * current NAV does not match the end PTS of the previous NAV, a PTS
+ * discontinuity has occurred.
+ * NAV packets can also be used for time display, because they are
+ * timestamped relatively to the current Cell.
+ */
+#define DVDNAV_NAV_PACKET 7
+
+
+/*
+ * DVDNAV_STOP
+ *
+ * Applications should end playback here. A subsequent dvdnav_get_next_block()
+ * call will restart the VM from the beginning of the DVD.
+ */
+#define DVDNAV_STOP 8
+
+
+/*
+ * DVDNAV_HIGHLIGHT
+ *
+ * The current button highlight changed. Inform the overlaying engine to
+ * highlight a different button. Please note, that at the moment only mode 1
+ * highlights are reported this way. That means, when the button highlight
+ * has been moved around by some function call, you will receive an event
+ * telling you the new button. But when a button gets activated, you have
+ * to handle the mode 2 highlighting (that is some different colour the
+ * button turns to on activation) in your application.
+ */
+#define DVDNAV_HIGHLIGHT 9
+
+typedef struct {
+ /* highlight mode: 0 - hide, 1 - show, 2 - activate, currently always 1 */
+ int display;
+
+ /* FIXME: these fields are currently not set */
+ 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 */
+
+ /* button number for the SPU decoder/overlaying engine */
+ uint32_t buttonN;
+} dvdnav_highlight_event_t;
+
+
+/*
+ * DVDNAV_SPU_CLUT_CHANGE
+ *
+ * Inform the SPU decoder/overlaying engine to update its colour lookup table.
+ * The CLUT is given as 16 uint32_t's in the buffer.
+ */
+#define DVDNAV_SPU_CLUT_CHANGE 10
+
+
+/*
+ * DVDNAV_HOP_CHANNEL
+ *
+ * A non-seamless operation has been performed. Applications can drop all
+ * their internal fifo's content, which will speed up the response.
+ */
+#define DVDNAV_HOP_CHANNEL 12
+
+
+/*
+ * DVDNAV_WAIT
+ *
+ * We have reached a point in DVD playback, where timing is critical.
+ * Player application with internal fifos can introduce state
+ * inconsistencies, because libdvdnav is always the fifo's length
+ * ahead in the stream compared to what the application sees.
+ * Such applications should wait until their fifos are empty
+ * when they receive this type of event.
+ * Once this is achieved, call dvdnav_skip_wait().
+ */
+#define DVDNAV_WAIT 13
+
+
+#endif /* LIBDVDNAV_DVDNAV_EVENTS_H */
diff --git a/libdvdnav-embedded/src/dvdnav/version.h.in b/libdvdnav-embedded/src/dvdnav/version.h.in
new file mode 100644
index 0000000..9304e8d
--- /dev/null
+++ b/libdvdnav-embedded/src/dvdnav/version.h.in
@@ -0,0 +1,35 @@
+/*
+* This file is part of libdvdnav, a DVD navigation library.
+*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+*/
+#ifndef LIBDVDNAV_VERSION_H
+#define LIBDVDNAV_VERSION_H
+
+#define DVDNAV_VERSION_CODE(major, minor, micro) \
+ (((major) * 10000) + \
+ ((minor) * 100) + \
+ ((micro) * 1))
+
+#define DVDNAV_VERSION_MAJOR @DVDNAV_MAJOR@
+#define DVDNAV_VERSION_MINOR @DVDNAV_MINOR@
+#define DVDNAV_VERSION_MICRO @DVDNAV_SUB@
+
+#define DVDNAV_VERSION_STRING "@DVDNAV_MAJOR@.@DVDNAV_MINOR@.@DVDNAV_SUB@"
+
+#define DVDNAV_VERSION \
+ DVDNAV_VERSION_CODE(DVDNAV_VERSION_MAJOR, DVDNAV_VERSION_MINOR, DVDNAV_VERSION_MICRO)
+
+#endif
diff --git a/libdvdnav-embedded/src/dvdnav_internal.h b/libdvdnav-embedded/src/dvdnav_internal.h
new file mode 100644
index 0000000..3b49aa2
--- /dev/null
+++ b/libdvdnav-embedded/src/dvdnav_internal.h
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2001-2004 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * 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 libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef LIBDVDNAV_DVDNAV_INTERNAL_H
+#define LIBDVDNAV_DVDNAV_INTERNAL_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef _WIN32
+
+/* pthread_mutex_* wrapper for win32 */
+#include <windows.h>
+#include <process.h>
+typedef CRITICAL_SECTION pthread_mutex_t;
+#define pthread_mutex_init(a, b) InitializeCriticalSection(a)
+#define pthread_mutex_lock(a) EnterCriticalSection(a)
+#define pthread_mutex_unlock(a) LeaveCriticalSection(a)
+#define pthread_mutex_destroy(a) DeleteCriticalSection(a)
+
+#ifndef HAVE_GETTIMEOFDAY
+/* replacement gettimeofday implementation */
+#include <sys/timeb.h>
+static inline int _private_gettimeofday( struct timeval *tv, void *tz )
+{
+ struct timeb t;
+ ftime( &t );
+ tv->tv_sec = t.time;
+ tv->tv_usec = t.millitm * 1000;
+ return 0;
+}
+#define gettimeofday(TV, TZ) _private_gettimeofday((TV), (TZ))
+#endif
+
+#include <io.h> /* read() */
+#define lseek64 _lseeki64
+
+#else
+
+#include <pthread.h>
+
+#endif /* _WIN32 */
+
+#ifdef __ANDROID__
+# undef lseek
+# define lseek lseek64
+# undef off_t
+# define off_t off64_t
+#endif
+
+/* where should libdvdnav write its messages (stdout/stderr) */
+#define MSG_OUT stderr
+
+/* Maximum length of an error string */
+#define MAX_ERR_LEN 255
+
+#ifndef DVD_VIDEO_LB_LEN
+#define DVD_VIDEO_LB_LEN 2048
+#endif
+
+typedef enum {
+ DSI_ILVU_PRE = 1 << 15, /* set during the last 3 VOBU preceding an interleaved block. */
+ DSI_ILVU_BLOCK = 1 << 14, /* set for all VOBU in an interleaved block */
+ DSI_ILVU_FIRST = 1 << 13, /* set for the first VOBU for a given angle or scene within a ILVU, or the first VOBU in the preparation (PREU) sequence */
+ DSI_ILVU_LAST = 1 << 12, /* set for the last VOBU for a given angle or scene within a ILVU, or the last VOBU in the preparation (PREU) sequence */
+ DSI_ILVU_MASK = 0xf000
+} DSI_ILVU;
+
+typedef struct read_cache_s read_cache_t;
+
+/*
+ * These are defined here because they are
+ * not in ifo_types.h, they maybe one day
+ */
+
+#ifndef audio_status_t
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+ unsigned int available : 1;
+ unsigned int zero1 : 4;
+ unsigned int stream_number : 3;
+ uint8_t zero2;
+#else
+ uint8_t zero2;
+ unsigned int stream_number : 3;
+ unsigned int zero1 : 4;
+ unsigned int available : 1;
+#endif
+} ATTRIBUTE_PACKED audio_status_t;
+#endif
+
+#ifndef spu_status_t
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+ unsigned int available : 1;
+ unsigned int zero1 : 2;
+ unsigned int stream_number_4_3 : 5;
+ unsigned int zero2 : 3;
+ unsigned int stream_number_wide : 5;
+ unsigned int zero3 : 3;
+ unsigned int stream_number_letterbox : 5;
+ unsigned int zero4 : 3;
+ unsigned int stream_number_pan_scan : 5;
+#else
+ unsigned int stream_number_pan_scan : 5;
+ unsigned int zero4 : 3;
+ unsigned int stream_number_letterbox : 5;
+ unsigned int zero3 : 3;
+ unsigned int stream_number_wide : 5;
+ unsigned int zero2 : 3;
+ unsigned int stream_number_4_3 : 5;
+ unsigned int zero1 : 2;
+ unsigned int available : 1;
+#endif
+} ATTRIBUTE_PACKED spu_status_t;
+#endif
+
+/*
+ * Describes a given time, and the closest sector, vobu and tmap index
+ */
+typedef struct {
+ uint64_t time;
+ uint32_t sector;
+ uint32_t vobu_idx;
+ int32_t tmap_idx;
+} dvdnav_pos_data_t;
+
+/*
+ * Encapsulates cell data
+ */
+typedef struct {
+ int32_t idx;
+ dvdnav_pos_data_t *bgn;
+ dvdnav_pos_data_t *end;
+} dvdnav_cell_data_t;
+
+/*
+ * Encapsulates common variables used by internal functions of jump_to_time
+ */
+typedef struct {
+ vobu_admap_t *admap;
+ int32_t admap_len;
+ const vts_tmap_t *tmap;
+ int32_t tmap_len;
+ int32_t tmap_interval;
+} dvdnav_jump_args_t;
+
+/*
+ * Utility constants for jump_to_time
+ */
+#define TMAP_IDX_EDGE_BGN -1
+#define TMAP_IDX_EDGE_END -2
+#define JUMP_MODE_TIME_AFTER 1
+#define JUMP_MODE_TIME_DEFAULT 0
+#define JUMP_MODE_TIME_BEFORE -1
+
+typedef struct dvdnav_vobu_s {
+ 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 **/
+
+struct dvdnav_s {
+ /* General data */
+ char *path; /* Path to DVD device/dir */
+ dvd_file_t *file; /* Currently opened file */
+
+ /* Position data */
+ vm_position_t position_next;
+ vm_position_t position_current;
+ dvdnav_vobu_t vobu;
+
+ /* NAV data */
+ pci_t pci;
+ dsi_t dsi;
+ uint32_t last_cmd_nav_lbn; /* detects when a command is issued on an already left NAV */
+
+ /* Flags */
+ int skip_still; /* Set when skipping a still */
+ int sync_wait; /* applications should wait till they are in sync with us */
+ int sync_wait_skip; /* Set when skipping wait state */
+ 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 pgc_based; /* positioning works PGC based instead of PG based */
+ int cur_cell_time; /* time expired since the beginning of the current cell, read from the dsi */
+
+ /* VM */
+ vm_t *vm;
+ pthread_mutex_t vm_lock;
+
+ /* private context and logger*/
+ void *priv;
+ dvdnav_logger_cb logcb;
+
+ /* Read-ahead cache */
+ read_cache_t *cache;
+
+ /* Errors */
+ char err_str[MAX_ERR_LEN];
+};
+
+/** HELPER FUNCTIONS **/
+
+/* converts a dvd_time_t to PTS ticks */
+int64_t dvdnav_convert_time(const dvd_time_t *time);
+
+/** USEFUL MACROS **/
+
+#ifdef __GNUC__
+#define printerrf(format, args...) \
+ do { if (this) snprintf(this->err_str, MAX_ERR_LEN, format, ## args); } while (0)
+#else
+#ifdef _MSC_VER
+#define printerrf(str) \
+ do { if (this) snprintf(this->err_str, MAX_ERR_LEN, str); } while (0)
+#else
+#define printerrf(...) \
+ do { if (this) snprintf(this->err_str, MAX_ERR_LEN, __VA_ARGS__); } while (0)
+#endif /* _MSC_VER */
+#endif
+#define printerr(str) \
+ do { if (this) strncpy(this->err_str, str, MAX_ERR_LEN - 1); } while (0)
+
+#endif /* LIBDVDNAV_DVDNAV_INTERNAL_H */
diff --git a/libdvdnav-embedded/src/highlight.c b/libdvdnav-embedded/src/highlight.c
new file mode 100644
index 0000000..9954347
--- /dev/null
+++ b/libdvdnav-embedded/src/highlight.c
@@ -0,0 +1,514 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * 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 libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include <sys/time.h>
+#include <dvdread/nav_types.h>
+#include "dvdnav/dvdnav.h"
+#include "vm/decoder.h"
+#include "vm/vm.h"
+#include "vm/vmcmd.h"
+#include "dvdnav_internal.h"
+
+/*
+#define BUTTON_TESTING
+*/
+
+#ifdef BUTTON_TESTING
+
+#include <dvdread/nav_print.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);
+ assert((dtime->frame_u&0xf) < 0xa);
+
+ fprintf(MSG_OUT,"%02x:%02x:%02x.%02x",
+ dtime->hour,
+ dtime->minute,
+ dtime->second,
+ dtime->frame_u & 0x3f);
+ switch((dtime->frame_u & 0xc0) >> 6) {
+ case 1:
+ rate = "25.00";
+ break;
+ case 3:
+ rate = "29.97";
+ break;
+ default:
+ rate = "(please send a bug report)";
+ break;
+ }
+ fprintf(MSG_OUT," @ %s fps", rate);
+}
+
+static void nav_print_PCI_GI(pci_gi_t *pci_gi) {
+ int32_t i;
+
+ 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,"libdvdnav: vobu_isrc \"");
+ for(i = 0; i < 32; i++) {
+ char c = pci_gi->vobu_isrc[i];
+ if((c >= ' ') && (c <= '~'))
+ fprintf(MSG_OUT,"%c", c);
+ else
+ fprintf(MSG_OUT,".");
+ }
+ fprintf(MSG_OUT,"\"\n");
+}
+
+static void nav_print_NSML_AGLI(nsml_agli_t *nsml_agli) {
+ int32_t i, j = 0;
+
+ for(i = 0; i < 9; i++)
+ j |= nsml_agli->nsml_agl_dsta[i];
+ if(j == 0)
+ return;
+
+ fprintf(MSG_OUT,"libdvdnav: nsml_agli:\n");
+ for(i = 0; i < 9; i++)
+ if(nsml_agli->nsml_agl_dsta[i])
+ fprintf(MSG_OUT,"libdvdnav: nsml_agl_c%d_dsta 0x%08x\n", i + 1,
+ nsml_agli->nsml_agl_dsta[i]);
+}
+
+static void nav_print_HL_GI(hl_gi_t *hl_gi, int32_t *btngr_ns, int32_t *btn_ns) {
+
+ if((hl_gi->hli_ss & 0x03) == 0)
+ return;
+
+ 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,"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,"libdvdnav: btn_ofn %d\n", hl_gi->btn_ofn);
+ *btn_ns = hl_gi->btn_ns;
+ 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) {
+ int32_t i, j;
+
+ j = 0;
+ for(i = 0; i < 6; i++)
+ j |= btn_colit->btn_coli[i/2][i&1];
+ if(j == 0)
+ return;
+
+ fprintf(MSG_OUT,"libdvdnav: btn_colit:\n");
+ for(i = 0; i < 3; i++)
+ for(j = 0; j < 2; j++)
+ fprintf(MSG_OUT,"libdvdnav: btn_cqoli %d %s_coli: %08x\n",
+ i, (j == 0) ? "sl" : "ac",
+ btn_colit->btn_coli[i][j]);
+}
+
+static void nav_print_BTNIT(btni_t *btni_table, int32_t btngr_ns, int32_t btn_ns) {
+ int32_t i, j, k;
+
+ 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;
+
+ for(i = 0; i < btngr_ns; i++) {
+ for(j = 0; j < (36 / btngr_ns); j++) {
+ if(j < btn_ns) {
+ btni_t *btni = &btni_table[(36 / btngr_ns) * i + j];
+
+ 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,"libdvdnav: coords (%d, %d) .. (%d, %d)\n",
+ btni->x_start, btni->y_start, btni->x_end, btni->y_end);
+
+ 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, "libdvdnav: %02x ", btni->cmd.bytes[k]);
+ }
+ fprintf(MSG_OUT, "| ");
+#ifdef TRACE
+ vm_print_mnemonic(&btni->cmd);
+#endif
+ fprintf(MSG_OUT, "\n");
+ }
+ }
+ }
+}
+
+static void nav_print_HLI(hli_t *hli) {
+ int32_t btngr_ns = 0, btn_ns = 0;
+
+ 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,"libdvdnav: pci packet:\n");
+ nav_print_PCI_GI(&pci->pci_gi);
+ nav_print_NSML_AGLI(&pci->nsml_agli);
+ nav_print_HLI(&pci->hli);
+}
+
+#endif
+
+
+/* Highlighting API calls */
+
+dvdnav_status_t dvdnav_get_current_highlight(dvdnav_t *this, int32_t *button) {
+ /* Simply return the appropriate value based on the SPRM */
+ if(((*button) = this->position_current.button) == -1)
+ (*button) = this->vm->state.HL_BTNN_REG >> 10;
+
+ return DVDNAV_STATUS_OK;
+}
+
+static const btni_t *get_current_button(dvdnav_t *this, pci_t *pci) {
+ int32_t button = 0;
+
+ if(!pci->hli.hl_gi.hli_ss) {
+ printerr("Not in a menu.");
+ return NULL;
+ }
+ if(this->last_cmd_nav_lbn == pci->pci_gi.nv_pck_lbn) {
+ printerr("This NAV has already been left.");
+ return NULL;
+ }
+
+ button = this->vm->state.HL_BTNN_REG >> 10;
+#ifdef BUTTON_TESTING
+ nav_print_PCI(pci);
+#endif
+
+ return &(pci->hli.btnit[button-1]);
+}
+
+static dvdnav_status_t button_auto_action(dvdnav_t *this, pci_t *pci) {
+ const btni_t *button_ptr;
+ if ((button_ptr = get_current_button(this, pci)) == NULL)
+ return DVDNAV_STATUS_ERR;
+
+ if (button_ptr->auto_action_mode)
+ return dvdnav_button_activate(this, pci);
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_upper_button_select(dvdnav_t *this, pci_t *pci) {
+ const btni_t *button_ptr;
+
+ if(!(button_ptr = get_current_button(this, pci)))
+ return DVDNAV_STATUS_ERR;
+
+ dvdnav_button_select(this, pci, button_ptr->up);
+ return button_auto_action(this, pci);
+}
+
+dvdnav_status_t dvdnav_lower_button_select(dvdnav_t *this, pci_t *pci) {
+ const btni_t *button_ptr;
+
+ if(!(button_ptr = get_current_button(this, pci)))
+ return DVDNAV_STATUS_ERR;
+
+ dvdnav_button_select(this, pci, button_ptr->down);
+ return button_auto_action(this, pci);
+}
+
+dvdnav_status_t dvdnav_right_button_select(dvdnav_t *this, pci_t *pci) {
+ const btni_t *button_ptr;
+
+ if(!(button_ptr = get_current_button(this, pci)))
+ return DVDNAV_STATUS_ERR;
+
+ dvdnav_button_select(this, pci, button_ptr->right);
+ return button_auto_action(this, pci);
+}
+
+dvdnav_status_t dvdnav_left_button_select(dvdnav_t *this, pci_t *pci) {
+ const btni_t *button_ptr;
+
+ if(!(button_ptr = get_current_button(this, pci)))
+ return DVDNAV_STATUS_ERR;
+
+ dvdnav_button_select(this, pci, button_ptr->left);
+ return button_auto_action(this, pci);
+}
+
+dvdnav_status_t dvdnav_get_highlight_area(pci_t *nav_pci , int32_t button, int32_t mode,
+ dvdnav_highlight_area_t *highlight) {
+ const btni_t *button_ptr;
+
+#ifdef BUTTON_TESTING
+ fprintf(MSG_OUT, "libdvdnav: Button get_highlight_area %i\n", button);
+#endif
+
+ if(!nav_pci->hli.hl_gi.hli_ss)
+ return DVDNAV_STATUS_ERR;
+ if((button <= 0) || (button > nav_pci->hli.hl_gi.btn_ns))
+ return DVDNAV_STATUS_ERR;
+
+
+ button_ptr = &nav_pci->hli.btnit[button-1];
+
+ highlight->sx = button_ptr->x_start;
+ highlight->sy = button_ptr->y_start;
+ highlight->ex = button_ptr->x_end;
+ highlight->ey = button_ptr->y_end;
+ if(button_ptr->btn_coln != 0) {
+ highlight->palette = nav_pci->hli.btn_colit.btn_coli[button_ptr->btn_coln-1][mode];
+ } else {
+ highlight->palette = 0;
+ }
+ highlight->pts = nav_pci->hli.hl_gi.hli_s_ptm;
+ highlight->buttonN = button;
+#ifdef BUTTON_TESTING
+ 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,
+ button);
+#endif
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_button_activate(dvdnav_t *this, pci_t *pci) {
+ int32_t button;
+ const btni_t *button_ptr = NULL;
+
+ if(!pci->hli.hl_gi.hli_ss) {
+ printerr("Not in a menu.");
+ return DVDNAV_STATUS_ERR;
+ }
+ if(this->last_cmd_nav_lbn == pci->pci_gi.nv_pck_lbn) {
+ printerr("This NAV has already been left.");
+ return DVDNAV_STATUS_ERR;
+ }
+ pthread_mutex_lock(&this->vm_lock);
+
+ if(!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+ button = this->vm->state.HL_BTNN_REG >> 10;
+
+ 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 application that a STILL is
+ * underway. In turn, the application is supposed to report to the user
+ * that the playback is paused. The user is then expected to undo the pause,
+ * ie: hit play. At that point, the navigation should release the still and
+ * go to the next Cell.
+ * Explanation by Mathieu Lacage <mathieu_lacage@realmagic.fr>
+ * Code added by jcdutton.
+ */
+ if (this->position_current.still != 0) {
+ /* In still, but no buttons. */
+ vm_get_next_cell(this->vm);
+ this->position_current.still = 0;
+ this->sync_wait = 0;
+ this->last_cmd_nav_lbn = pci->pci_gi.nv_pck_lbn;
+ pthread_mutex_unlock(&this->vm_lock);
+ /* clear error message */
+ printerr("");
+ return DVDNAV_STATUS_OK;
+ }
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+ if ((button_ptr = get_current_button(this, pci)) == NULL) {
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+ /* Finally, make the VM execute the appropriate code and probably
+ * schedule a jump */
+#ifdef BUTTON_TESTING
+ fprintf(MSG_OUT, "libdvdnav: Evaluating Button Activation commands.\n");
+#endif
+ if(vm_exec_cmd(this->vm, &(button_ptr->cmd)) == 1) {
+ /* Command caused a jump */
+ this->vm->hop_channel++;
+ this->position_current.still = 0;
+ this->last_cmd_nav_lbn = pci->pci_gi.nv_pck_lbn;
+ }
+
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_button_activate_cmd(dvdnav_t *this, int32_t button, vm_cmd_t *cmd)
+{
+ pthread_mutex_lock(&this->vm_lock);
+
+ if(!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+ /* 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");
+#endif
+ if(button > 0) {
+ this->vm->state.HL_BTNN_REG = (button << 10);
+ if(vm_exec_cmd(this->vm, cmd) == 1) {
+ /* Command caused a jump */
+ this->vm->hop_channel++;
+ }
+ }
+ /* Always remove still, because some still menus have no buttons. */
+ this->position_current.still = 0;
+ this->sync_wait = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_button_select(dvdnav_t *this, pci_t *pci, int32_t button) {
+ if(!pci->hli.hl_gi.hli_ss) {
+ printerr("Not in a menu.");
+ return DVDNAV_STATUS_ERR;
+ }
+ if(this->last_cmd_nav_lbn == pci->pci_gi.nv_pck_lbn) {
+ printerr("This NAV has already been left.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+#ifdef BUTTON_TESTING
+ fprintf(MSG_OUT, "libdvdnav: Button select %i\n", button);
+#endif
+
+ if((button <= 0) || (button > pci->hli.hl_gi.btn_ns)) {
+ printerr("Button does not exist.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ this->vm->state.HL_BTNN_REG = (button << 10);
+ this->position_current.button = -1; /* Force Highlight change */
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_button_select_and_activate(dvdnav_t *this, pci_t *pci,
+ int32_t button) {
+ /* A trivial function */
+ if(dvdnav_button_select(this, pci, button) != DVDNAV_STATUS_ERR)
+ return dvdnav_button_activate(this, pci);
+ return DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_mouse_select(dvdnav_t *this, pci_t *pci, int32_t x, int32_t y) {
+ int32_t button, cur_button;
+ int32_t best,dist,d;
+ int32_t mx,my,dx,dy;
+
+ if(!pci->hli.hl_gi.hli_ss) {
+ printerr("Not in a menu.");
+ return DVDNAV_STATUS_ERR;
+ }
+ if(this->last_cmd_nav_lbn == pci->pci_gi.nv_pck_lbn) {
+ printerr("This NAV has already been left.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ cur_button = this->vm->state.HL_BTNN_REG >> 10;
+
+ best = 0;
+ dist = 0x08000000; /* >> than (720*720)+(567*567); */
+
+ /* Loop through all buttons */
+ for(button = 1; button <= pci->hli.hl_gi.btn_ns; button++) {
+ const btni_t *button_ptr = &(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;
+ 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;
+ }
+ }
+ }
+ /* 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 DVDNAV_STATUS_OK only if we actually found a matching button */
+ return best ? DVDNAV_STATUS_OK : DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_mouse_activate(dvdnav_t *this, pci_t *pci, int32_t x, int32_t y) {
+ /* A trivial function */
+ if(dvdnav_mouse_select(this, pci, x,y) != DVDNAV_STATUS_ERR)
+ return dvdnav_button_activate(this, pci);
+ return DVDNAV_STATUS_ERR;
+}
diff --git a/libdvdnav-embedded/src/logger.c b/libdvdnav-embedded/src/logger.c
new file mode 100644
index 0000000..ae06ff6
--- /dev/null
+++ b/libdvdnav-embedded/src/logger.c
@@ -0,0 +1,41 @@
+/*
+* This file is part of libdvdnav, a DVD navigation library.
+*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "dvdnav/dvdnav.h"
+#include "logger.h"
+
+void dvdnav_log( void *priv, const dvdnav_logger_cb *logcb,
+ dvdnav_logger_level_t level, const char *fmt, ... )
+{
+ va_list list;
+ va_start(list, fmt);
+ if(logcb && logcb->pf_log)
+ logcb->pf_log(priv, level, fmt, list);
+ else
+ {
+ FILE *stream = (level == DVDNAV_LOGGER_LEVEL_ERROR) ? stderr : stdout;
+ fprintf(stream, "libdvdnav: ");
+ vfprintf(stream, fmt, list);
+ fprintf(stream, "\n");
+ }
+ va_end(list);
+}
diff --git a/libdvdnav-embedded/src/logger.h b/libdvdnav-embedded/src/logger.h
new file mode 100644
index 0000000..ca4c538
--- /dev/null
+++ b/libdvdnav-embedded/src/logger.h
@@ -0,0 +1,32 @@
+/*
+* This file is part of libdvdnav, a DVD navigation library.
+*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+*/
+
+#ifndef LIBDVDNAV_LOGGER_H
+#define LIBDVDNAV_LOGGER_H
+
+void dvdnav_log( void *priv, const dvdnav_logger_cb *logcb,
+ dvdnav_logger_level_t level, const char *fmt, ... );
+
+#define LOG(ctx, level, ...) \
+ dvdnav_log(ctx->priv, &ctx->logcb, level, __VA_ARGS__)
+#define Log0(ctx, ...) LOG(ctx, DVDNAV_LOGGER_LEVEL_ERROR, __VA_ARGS__)
+#define Log1(ctx, ...) LOG(ctx, DVDNAV_LOGGER_LEVEL_WARN, __VA_ARGS__)
+#define Log2(ctx, ...) LOG(ctx, DVDNAV_LOGGER_LEVEL_INFO, __VA_ARGS__)
+#define Log3(ctx, ...) LOG(ctx, DVDNAV_LOGGER_LEVEL_DEBUG, __VA_ARGS__)
+
+#endif
diff --git a/libdvdnav-embedded/src/navigation.c b/libdvdnav-embedded/src/navigation.c
new file mode 100644
index 0000000..ba61102
--- /dev/null
+++ b/libdvdnav-embedded/src/navigation.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * 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 libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <inttypes.h>
+#include <limits.h>
+#include <string.h>
+#include <sys/time.h>
+#include "dvdnav/dvdnav.h"
+#include <dvdread/nav_types.h>
+#include "vm/decoder.h"
+#include "vm/vm.h"
+#include "dvdnav_internal.h"
+
+/* Navigation API calls */
+
+dvdnav_status_t dvdnav_still_skip(dvdnav_t *this) {
+ pthread_mutex_lock(&this->vm_lock);
+ this->position_current.still = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ this->skip_still = 1;
+ this->sync_wait = 0;
+ this->sync_wait_skip = 1;
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_wait_skip(dvdnav_t *this) {
+ this->sync_wait = 0;
+ this->sync_wait_skip = 1;
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_get_number_of_titles(dvdnav_t *this, int32_t *titles) {
+ if (!this->vm->vmgi) {
+ printerr("Bad VM state.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ (*titles) = vm_get_vmgi(this->vm)->tt_srpt->nr_of_srpts;
+
+ return DVDNAV_STATUS_OK;
+}
+
+static dvdnav_status_t get_title_by_number(dvdnav_t *this, int32_t title,
+ const title_info_t **pp_title)
+{
+ int32_t titlescount;
+ dvdnav_status_t status = dvdnav_get_number_of_titles(this, &titlescount);
+ if(status == DVDNAV_STATUS_OK)
+ {
+ if ((title < 1) || (title > titlescount)) {
+ printerr("Passed a title number out of range.");
+ status = DVDNAV_STATUS_ERR;
+ }
+ else {
+ *pp_title = &vm_get_vmgi(this->vm)->tt_srpt->title[title-1];
+ }
+ }
+ return status;
+}
+
+dvdnav_status_t dvdnav_get_number_of_parts(dvdnav_t *this, int32_t title, int32_t *parts) {
+ const title_info_t *info;
+ dvdnav_status_t status = get_title_by_number(this, title, &info);
+ if(status == DVDNAV_STATUS_OK)
+ (*parts) = info->nr_of_ptts;
+ return status;
+}
+
+dvdnav_status_t dvdnav_get_number_of_angles(dvdnav_t *this, int32_t title, int32_t *angles) {
+ const title_info_t *info;
+ dvdnav_status_t status = get_title_by_number(this, title, &info);
+ if(status == DVDNAV_STATUS_OK)
+ (*angles) = info->nr_of_angles;
+ return status;
+}
+
+dvdnav_status_t dvdnav_current_title_info(dvdnav_t *this, int32_t *title, int32_t *part) {
+ int32_t retval;
+
+ pthread_mutex_lock(&this->vm_lock);
+ if (!this->vm->vtsi || !this->vm->vmgi) {
+ printerr("Bad VM state.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ if (!this->started) {
+ printerr("Virtual DVD machine not started.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ if (!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ if ( (this->vm->state.domain == DVD_DOMAIN_VTSMenu)
+ || (this->vm->state.domain == DVD_DOMAIN_VMGM) ) {
+ /* Get current Menu ID: into *part. */
+ if(! vm_get_current_menu(this->vm, part)) {
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ if (*part > -1) {
+ *title = 0;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+ }
+ if (this->vm->state.domain == DVD_DOMAIN_VTSTitle) {
+ retval = vm_get_current_title_part(this->vm, title, part);
+ pthread_mutex_unlock(&this->vm_lock);
+ return retval ? DVDNAV_STATUS_OK : DVDNAV_STATUS_ERR;
+ }
+ printerr("Not in a title or menu.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_current_title_program(dvdnav_t *this, int32_t *title, int32_t *pgcn, int32_t *pgn) {
+ int32_t retval;
+ int32_t part;
+
+ pthread_mutex_lock(&this->vm_lock);
+ if (!this->vm->vtsi || !this->vm->vmgi) {
+ printerr("Bad VM state.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ if (!this->started) {
+ printerr("Virtual DVD machine not started.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ if (!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ if ( (this->vm->state.domain == DVD_DOMAIN_VTSMenu)
+ || (this->vm->state.domain == DVD_DOMAIN_VMGM) ) {
+ /* Get current Menu ID: into *part. */
+ if(! vm_get_current_menu(this->vm, &part)) {
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ if (part > -1) {
+ *title = 0;
+ *pgcn = this->vm->state.pgcN;
+ *pgn = this->vm->state.pgN;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+ }
+ if (this->vm->state.domain == DVD_DOMAIN_VTSTitle) {
+ retval = vm_get_current_title_part(this->vm, title, &part);
+ *pgcn = this->vm->state.pgcN;
+ *pgn = this->vm->state.pgN;
+ pthread_mutex_unlock(&this->vm_lock);
+ return retval ? DVDNAV_STATUS_OK : DVDNAV_STATUS_ERR;
+ }
+ printerr("Not in a title or menu.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_title_play(dvdnav_t *this, int32_t title) {
+ return dvdnav_part_play(this, title, 1);
+}
+
+dvdnav_status_t dvdnav_program_play(dvdnav_t *this, int32_t title, int32_t pgcn, int32_t pgn) {
+ int32_t retval;
+
+ pthread_mutex_lock(&this->vm_lock);
+ if (!this->vm->vmgi) {
+ printerr("Bad VM state.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ if (!this->started) {
+ /* don't report an error but be nice */
+ vm_start(this->vm);
+ this->started = 1;
+ }
+ if (!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_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 DVDNAV_STATUS_ERR;
+ }
+
+ retval = vm_jump_title_program(this->vm, title, pgcn, pgn);
+ if (retval)
+ this->vm->hop_channel++;
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return retval ? DVDNAV_STATUS_OK : DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_part_play(dvdnav_t *this, int32_t title, int32_t part) {
+ int32_t retval;
+
+ pthread_mutex_lock(&this->vm_lock);
+ if (!this->vm->vmgi) {
+ printerr("Bad VM state.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ if (!this->started) {
+ /* don't report an error but be nice */
+ vm_start(this->vm);
+ this->started = 1;
+ }
+ if (!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_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 DVDNAV_STATUS_ERR;
+ }
+ if((part < 1) || (part > this->vm->vmgi->tt_srpt->title[title-1].nr_of_ptts)) {
+ printerr("Part out of range.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+ retval = vm_jump_title_part(this->vm, title, part);
+ if (retval)
+ this->vm->hop_channel++;
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return retval ? DVDNAV_STATUS_OK : DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_part_play_auto_stop(dvdnav_t *this, int32_t title,
+ int32_t part, int32_t parts_to_play) {
+ /* FIXME: Implement auto-stop */
+ if (dvdnav_part_play(this, title, part) == DVDNAV_STATUS_OK)
+ printerr("Not implemented yet.");
+ return DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_time_play(dvdnav_t *this, int32_t title,
+ uint64_t time) {
+ /* FIXME: Implement */
+ printerr("Not implemented yet.");
+ return DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_stop(dvdnav_t *this) {
+ pthread_mutex_lock(&this->vm_lock);
+ this->vm->stopped = 1;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_go_up(dvdnav_t *this) {
+ /* A nice easy function... delegate to the VM */
+ int retval;
+ pthread_mutex_lock(&this->vm_lock);
+ if (!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+ retval = vm_jump_up(this->vm);
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return retval ? DVDNAV_STATUS_OK : DVDNAV_STATUS_ERR;
+}
diff --git a/libdvdnav-embedded/src/read_cache.c b/libdvdnav-embedded/src/read_cache.c
new file mode 100644
index 0000000..4af4056
--- /dev/null
+++ b/libdvdnav-embedded/src/read_cache.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ * 2001-2004 the dvdnav project
+ *
+ * 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 libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+/*
+ * There was a multithreaded read ahead cache in here for some time, but
+ * it had only been used for a short time. If you want to have a look at it,
+ * search the CVS attic.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <time.h>
+#include "dvdnav/dvdnav.h"
+#include <dvdread/nav_types.h>
+#include "vm/decoder.h"
+#include "vm/vm.h"
+#include "dvdnav_internal.h"
+#include "read_cache.h"
+
+#define READ_CACHE_CHUNKS 10
+
+/* all cache chunks must be memory aligned to allow use of raw devices */
+#define ALIGNMENT 2048
+
+#define READ_AHEAD_SIZE_MIN 4
+#define READ_AHEAD_SIZE_MAX 512
+
+typedef struct read_cache_chunk_s {
+ uint8_t *cache_buffer;
+ uint8_t *cache_buffer_base; /* used in malloc and free for alignment */
+ int32_t cache_start_sector; /* -1 means cache invalid */
+ int32_t cache_read_count; /* this many sectors are already read */
+ size_t cache_block_count; /* this many sectors will go in this chunk */
+ size_t cache_malloc_size;
+ int cache_valid;
+ int usage_count; /* counts how many buffers where issued from this chunk */
+} read_cache_chunk_t;
+
+struct read_cache_s {
+ read_cache_chunk_t chunk[READ_CACHE_CHUNKS];
+ int current;
+ int freeing; /* is set to one when we are about to dispose the cache */
+ uint32_t read_ahead_size;
+ int read_ahead_incr;
+ int last_sector;
+ pthread_mutex_t lock;
+
+ /* Bit of strange cross-linking going on here :) -- Gotta love C :) */
+ dvdnav_t *dvd_self;
+};
+
+/*
+#define READ_CACHE_TRACE 0
+*/
+
+#ifdef __GNUC__
+# if READ_CACHE_TRACE
+# 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__)
+# else
+#ifdef _MSC_VER
+# define dprintf(fmt, str) /* Nowt */
+#else
+# define dprintf(fmt, ...) /* Nowt */
+#endif /* _MSC_VER */
+# endif
+#endif
+
+
+read_cache_t *dvdnav_read_cache_new(dvdnav_t* dvd_self) {
+ read_cache_t *self;
+ int i;
+
+ self = (read_cache_t *)calloc(1, sizeof(read_cache_t));
+
+ if(!self)
+ return NULL;
+
+ self->dvd_self = dvd_self;
+ self->read_ahead_size = READ_AHEAD_SIZE_MIN;
+ pthread_mutex_init(&self->lock, NULL);
+ dvdnav_read_cache_clear(self);
+ for (i = 0; i < READ_CACHE_CHUNKS; i++) {
+ self->chunk[i].cache_buffer = NULL;
+ self->chunk[i].usage_count = 0;
+ }
+
+ return self;
+}
+
+void dvdnav_read_cache_free(read_cache_t* self) {
+ dvdnav_t *tmp;
+ int i;
+
+ pthread_mutex_lock(&self->lock);
+ self->freeing = 1;
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ if (self->chunk[i].cache_buffer && self->chunk[i].usage_count == 0) {
+ free(self->chunk[i].cache_buffer_base);
+ self->chunk[i].cache_buffer = NULL;
+ }
+ pthread_mutex_unlock(&self->lock);
+
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ if (self->chunk[i].cache_buffer) return;
+
+ /* all buffers returned, free everything */
+ tmp = self->dvd_self;
+ pthread_mutex_destroy(&self->lock);
+ free(self);
+ free(tmp);
+}
+
+/* This function MUST be called whenever self->file changes. */
+void dvdnav_read_cache_clear(read_cache_t *self) {
+ int i;
+
+ if(!self)
+ return;
+
+ pthread_mutex_lock(&self->lock);
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ self->chunk[i].cache_valid = 0;
+ pthread_mutex_unlock(&self->lock);
+}
+
+/* This function is called just after reading the NAV packet. */
+void dvdnav_pre_cache_blocks(read_cache_t *self, int sector, size_t block_count) {
+ int i, use;
+
+ if(!self)
+ return;
+
+ if(!self->dvd_self->use_read_ahead)
+ return;
+
+ pthread_mutex_lock(&self->lock);
+
+ /* find a free cache chunk that best fits the required size */
+ use = -1;
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ if (self->chunk[i].usage_count == 0 && self->chunk[i].cache_buffer &&
+ self->chunk[i].cache_malloc_size >= block_count &&
+ (use == -1 || self->chunk[use].cache_malloc_size > self->chunk[i].cache_malloc_size))
+ use = i;
+
+ if (use == -1) {
+ /* we haven't found a cache chunk, so we try to reallocate an existing one */
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ if (self->chunk[i].usage_count == 0 && self->chunk[i].cache_buffer &&
+ (use == -1 || self->chunk[use].cache_malloc_size < self->chunk[i].cache_malloc_size))
+ use = i;
+ if (use >= 0) {
+ self->chunk[use].cache_buffer_base = realloc(self->chunk[use].cache_buffer_base,
+ block_count * DVD_VIDEO_LB_LEN + ALIGNMENT);
+ self->chunk[use].cache_buffer =
+ (uint8_t *)(((uintptr_t)self->chunk[use].cache_buffer_base & ~((uintptr_t)(ALIGNMENT - 1))) + ALIGNMENT);
+ dprintf("pre_cache DVD read realloc happened\n");
+ self->chunk[use].cache_malloc_size = block_count;
+ } else {
+ /* we still haven't found a cache chunk, let's allocate a new one */
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ if (!self->chunk[i].cache_buffer) {
+ use = i;
+ break;
+ }
+ if (use >= 0) {
+ /* We start with a sensible figure for the first malloc of 500 blocks.
+ * Some DVDs I have seen venture to 450 blocks.
+ * This is so that fewer realloc's happen if at all.
+ */
+ self->chunk[i].cache_buffer_base =
+ malloc((block_count > 500 ? block_count : 500) * DVD_VIDEO_LB_LEN + ALIGNMENT);
+ self->chunk[i].cache_buffer =
+ (uint8_t *)(((uintptr_t)self->chunk[i].cache_buffer_base & ~((uintptr_t)(ALIGNMENT - 1))) + ALIGNMENT);
+ self->chunk[i].cache_malloc_size = block_count > 500 ? block_count : 500;
+ dprintf("pre_cache DVD read malloc %d blocks\n",
+ (block_count > 500 ? block_count : 500 ));
+ }
+ }
+ }
+
+ if (use >= 0) {
+ self->chunk[use].cache_start_sector = sector;
+ self->chunk[use].cache_block_count = block_count;
+ self->chunk[use].cache_read_count = 0;
+ self->chunk[use].cache_valid = 1;
+ self->current = use;
+ } else {
+ dprintf("pre_caching was impossible, no cache chunk available\n");
+ }
+ pthread_mutex_unlock(&self->lock);
+}
+
+int dvdnav_read_cache_block(read_cache_t *self, int sector, size_t block_count, uint8_t **buf) {
+ int i, use;
+ int start;
+ int size;
+ int incr;
+ uint8_t *read_ahead_buf;
+ int32_t res;
+
+ if(!self)
+ return 0;
+
+ use = -1;
+
+ if(self->dvd_self->use_read_ahead) {
+ /* first check, if sector is in current chunk */
+ read_cache_chunk_t cur = self->chunk[self->current];
+ if (cur.cache_valid && sector >= cur.cache_start_sector &&
+ sector <= (cur.cache_start_sector + cur.cache_read_count) &&
+ sector + block_count <= cur.cache_start_sector + cur.cache_block_count)
+ use = self->current;
+ else
+ for (i = 0; i < READ_CACHE_CHUNKS; i++)
+ if (self->chunk[i].cache_valid &&
+ sector >= self->chunk[i].cache_start_sector &&
+ sector <= (self->chunk[i].cache_start_sector + self->chunk[i].cache_read_count) &&
+ sector + block_count <= self->chunk[i].cache_start_sector + self->chunk[i].cache_block_count)
+ use = i;
+ }
+
+ if (use >= 0) {
+ read_cache_chunk_t *chunk;
+
+ /* Increment read-ahead size if sector follows the last sector */
+ if (sector == (self->last_sector + 1)) {
+ if (self->read_ahead_incr < READ_AHEAD_SIZE_MAX)
+ self->read_ahead_incr++;
+ } else {
+ self->read_ahead_size = READ_AHEAD_SIZE_MIN;
+ self->read_ahead_incr = 0;
+ }
+ self->last_sector = sector;
+
+ /* The following resources need to be protected by a mutex :
+ * self->chunk[*].cache_buffer
+ * self->chunk[*].cache_malloc_size
+ * self->chunk[*].usage_count
+ */
+ pthread_mutex_lock(&self->lock);
+ chunk = &self->chunk[use];
+ read_ahead_buf = chunk->cache_buffer + chunk->cache_read_count * DVD_VIDEO_LB_LEN;
+ *buf = chunk->cache_buffer + (sector - chunk->cache_start_sector) * DVD_VIDEO_LB_LEN;
+ chunk->usage_count++;
+ pthread_mutex_unlock(&self->lock);
+
+ dprintf("libdvdnav: sector=%d, start_sector=%d, last_sector=%d\n", sector, chunk->cache_start_sector, chunk->cache_start_sector + chunk->cache_block_count);
+
+ /* read_ahead_size */
+ incr = self->read_ahead_incr >> 1;
+ if ((self->read_ahead_size + incr) > READ_AHEAD_SIZE_MAX) {
+ self->read_ahead_size = READ_AHEAD_SIZE_MAX;
+ } else {
+ self->read_ahead_size += incr;
+ }
+
+ /* real read size */
+ start = chunk->cache_start_sector + chunk->cache_read_count;
+ if (chunk->cache_read_count + self->read_ahead_size > chunk->cache_block_count) {
+ size = chunk->cache_block_count - chunk->cache_read_count;
+ } else {
+ size = self->read_ahead_size;
+ /* ensure that the sector we want will be read */
+ if (sector >= chunk->cache_start_sector + chunk->cache_read_count + size)
+ size = sector - chunk->cache_start_sector - chunk->cache_read_count;
+ }
+ dprintf("libdvdnav: read_ahead_size=%d, size=%d\n", self->read_ahead_size, size);
+
+ if (size)
+ chunk->cache_read_count += DVDReadBlocks(self->dvd_self->file,
+ start,
+ size,
+ read_ahead_buf);
+
+ res = DVD_VIDEO_LB_LEN * block_count;
+
+ } else {
+
+ if (self->dvd_self->use_read_ahead) {
+ dprintf("cache miss on sector %d\n", sector);
+ }
+
+ res = DVDReadBlocks(self->dvd_self->file,
+ sector,
+ block_count,
+ *buf) * DVD_VIDEO_LB_LEN;
+ }
+
+ return res;
+
+}
+
+dvdnav_status_t dvdnav_free_cache_block(dvdnav_t *self, unsigned char *buf) {
+ read_cache_t *cache;
+ int i;
+
+ if (!self)
+ return DVDNAV_STATUS_ERR;
+
+ cache = self->cache;
+ if (!cache)
+ return DVDNAV_STATUS_ERR;
+
+ pthread_mutex_lock(&cache->lock);
+ for (i = 0; i < READ_CACHE_CHUNKS; i++) {
+ if (cache->chunk[i].cache_buffer && buf >= cache->chunk[i].cache_buffer &&
+ buf < cache->chunk[i].cache_buffer + cache->chunk[i].cache_malloc_size * DVD_VIDEO_LB_LEN) {
+ assert(cache->chunk[i].usage_count > 0);
+ cache->chunk[i].usage_count--;
+ }
+ }
+ pthread_mutex_unlock(&cache->lock);
+
+ if (cache->freeing)
+ /* when we want to dispose the cache, try freeing it now */
+ dvdnav_read_cache_free(cache);
+
+ return DVDNAV_STATUS_OK;
+}
diff --git a/libdvdnav-embedded/src/read_cache.h b/libdvdnav-embedded/src/read_cache.h
new file mode 100644
index 0000000..3a0e48c
--- /dev/null
+++ b/libdvdnav-embedded/src/read_cache.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * 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 libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef LIBDVDNAV_READ_CACHE_H
+#define LIBDVDNAV_READ_CACHE_H
+
+/* Opaque cache type -- defined in dvdnav_internal.h */
+/* typedef struct read_cache_s read_cache_t; */
+
+/* EXPERIMENTAL: Setting the following to 1 will use an experimental multi-threaded
+ * read-ahead cache.
+ */
+#define _MULTITHREAD_ 0
+
+/* Constructor/destructors */
+read_cache_t *dvdnav_read_cache_new(dvdnav_t* dvd_self);
+void dvdnav_read_cache_free(read_cache_t* self);
+
+/* This function MUST be called whenever self->file changes. */
+void dvdnav_read_cache_clear(read_cache_t *self);
+/* This function is called just after reading the NAV packet. */
+void dvdnav_pre_cache_blocks(read_cache_t *self, int sector, size_t block_count);
+/* This function will do the cache read.
+ * The buffer handed in must be malloced to take one dvd block.
+ * On a cache hit, a different buffer will be returned though.
+ * Those buffers must _never_ be freed. */
+int dvdnav_read_cache_block(read_cache_t *self, int sector, size_t block_count, uint8_t **buf);
+
+#endif /* LIBDVDNAV_READ_CACHE_H */
diff --git a/libdvdnav-embedded/src/searching.c b/libdvdnav-embedded/src/searching.c
new file mode 100644
index 0000000..7c8e6e6
--- /dev/null
+++ b/libdvdnav-embedded/src/searching.c
@@ -0,0 +1,1356 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * 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 libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include "dvdnav/dvdnav.h"
+#include <dvdread/nav_types.h>
+#include "vm/decoder.h"
+#include "vm/vm.h"
+#include "dvdnav_internal.h"
+#include "logger.h"
+#include <dvdread/ifo_read.h>
+
+/*
+#define LOG_DEBUG
+*/
+
+/* Searching API calls */
+
+/* Scan the ADMAP for a particular block number. */
+/* Return placed in vobu. */
+/* Returns error status */
+/* FIXME: Maybe need to handle seeking outside current cell. */
+static dvdnav_status_t dvdnav_scan_admap(dvdnav_t *this, int32_t domain, uint32_t seekto_block, int next, uint32_t *vobu) {
+ vobu_admap_t *admap = NULL;
+
+#ifdef LOG_DEBUG
+ Log3(this, "Seeking to target %u ...", seekto_block);
+#endif
+ *vobu = -1;
+
+ /* Search through the VOBU_ADMAP for the nearest VOBU
+ * to the target block */
+ switch(domain) {
+ case DVD_DOMAIN_FirstPlay:
+ case DVD_DOMAIN_VMGM:
+ admap = this->vm->vmgi->menu_vobu_admap;
+ break;
+ case DVD_DOMAIN_VTSMenu:
+ admap = this->vm->vtsi->menu_vobu_admap;
+ break;
+ case DVD_DOMAIN_VTSTitle:
+ admap = this->vm->vtsi->vts_vobu_admap;
+ break;
+ default:
+ Log0(this, "Error: Unknown domain for seeking.");
+ }
+ if(admap) {
+ uint32_t address = 0;
+ uint32_t vobu_start, next_vobu = 0;
+ uint32_t admap_entries = (admap->last_byte + 1 - VOBU_ADMAP_SIZE)/VOBU_ADMAP_SIZE;
+
+ /* Search through ADMAP for best sector */
+ vobu_start = SRI_END_OF_CELL;
+ /* FIXME: Implement a faster search algorithm */
+ while(address < admap_entries) {
+ next_vobu = admap->vobu_start_sectors[address];
+
+ /* fprintf(MSG_OUT, "libdvdnav: Found block %u\n", next_vobu); */
+
+ if(vobu_start <= seekto_block && next_vobu > seekto_block) {
+ /* We expect the ADMAP to be sorted in order of increasing
+ * sector number, but some discs store out-of-order values in
+ * cells that would not normally be playable, cells which
+ * themselves contain bad sectors. The straightforward linear
+ * search would get "stuck" on those values, essentially
+ * forcing playback of the bad sectors on any sector seek
+ * anywhere within the title. To prevent this, scan the next
+ * few entries to make sure they're all really later than the
+ * current entry, and skip over the out-of-order entries if
+ * not. */
+ uint32_t ooo_address = 0;
+ uint32_t ooo_vobu = SRI_END_OF_CELL;
+ uint32_t check;
+ for( check = 1 ; check <= 32 && address+check < admap_entries ; check++ ) {
+ uint32_t check_vobu = admap->vobu_start_sectors[address+check];
+ if (check_vobu < next_vobu && check_vobu < ooo_vobu) {
+ ooo_address = address+check;
+ ooo_vobu = check_vobu;
+ }
+ }
+ if (ooo_vobu < next_vobu) {
+ address = ooo_address;
+ next_vobu = ooo_vobu;
+ } else {
+ break;
+ }
+ }
+ vobu_start = next_vobu;
+ address++;
+ }
+ *vobu = next ? next_vobu : vobu_start;
+ return DVDNAV_STATUS_OK;
+ }
+ Log0(this, "admap not located");
+ return DVDNAV_STATUS_ERR;
+}
+
+/* FIXME: right now, this function does not use the time tables but interpolates
+ only the cell times */
+dvdnav_status_t dvdnav_time_search(dvdnav_t *this,
+ uint64_t time) {
+
+ uint64_t target = time;
+ uint64_t length = 0;
+ uint32_t first_cell_nr, last_cell_nr, cell_nr;
+ int32_t found;
+ const cell_playback_t *cell;
+ dvd_state_t *state;
+
+ if(this->position_current.still != 0) {
+ printerr("Cannot seek in a still frame.");
+ return DVDNAV_STATUS_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 DVDNAV_STATUS_ERR;
+ }
+
+
+ this->cur_cell_time = 0;
+ if (this->pgc_based) {
+ first_cell_nr = 1;
+ last_cell_nr = state->pgc->nr_of_cells;
+ } else {
+ /* Find start cell of program. */
+ first_cell_nr = state->pgc->program_map[state->pgN-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
+ last_cell_nr = state->pgc->nr_of_cells;
+ }
+
+ found = 0;
+ for(cell_nr = first_cell_nr; (cell_nr <= last_cell_nr) && !found; cell_nr ++) {
+ cell = &(state->pgc->cell_playback[cell_nr-1]);
+ if(cell->block_type == BLOCK_TYPE_ANGLE_BLOCK && cell->block_mode != BLOCK_MODE_FIRST_CELL)
+ continue;
+ length = dvdnav_convert_time(&cell->playback_time);
+ if (target >= length) {
+ target -= length;
+ } else {
+ /* FIXME: there must be a better way than interpolation */
+ target = target * (cell->last_sector - cell->first_sector + 1) / length;
+ target += cell->first_sector;
+
+ found = 1;
+ break;
+ }
+ }
+
+ if(found) {
+ uint32_t vobu;
+#ifdef LOG_DEBUG
+ Log3(this, "Seeking to cell %i from choice of %i to %i",
+ cell_nr, first_cell_nr, last_cell_nr);
+#endif
+ if (dvdnav_scan_admap(this, state->domain, target, 0, &vobu) == DVDNAV_STATUS_OK) {
+ uint32_t start = state->pgc->cell_playback[cell_nr-1].first_sector;
+
+ if (vm_jump_cell_block(this->vm, cell_nr, vobu - start)) {
+#ifdef LOG_DEBUG
+ Log3(this, "After cellN=%u blockN=%u target=%x vobu=%x start=%x" ,
+ state->cellN, state->blockN, target, vobu, start);
+#endif
+ this->vm->hop_channel += HOP_SEEK;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+ }
+ }
+
+ Log0(this, "Error when seeking");
+ printerr("Error when seeking.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_sector_search(dvdnav_t *this,
+ int64_t offset, int32_t origin) {
+ uint32_t target = 0;
+ uint32_t current_pos;
+ uint32_t cur_sector;
+ uint32_t cur_cell_nr;
+ uint32_t length = 0;
+ uint32_t first_cell_nr, last_cell_nr, cell_nr;
+ int32_t found;
+ int forward = 0;
+ const cell_playback_t *cell;
+ dvd_state_t *state;
+ dvdnav_status_t result;
+
+ if(this->position_current.still != 0) {
+ printerr("Cannot seek in a still frame.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ result = dvdnav_get_position(this, &target, &length);
+ if(!result) {
+ printerr("Cannot get current position.");
+ return DVDNAV_STATUS_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 DVDNAV_STATUS_ERR;
+ }
+#ifdef LOG_DEBUG
+ Log3(this, "seeking to offset=%lld pos=%u length=%u", offset, target, length);
+ Log3(this, "Before cellN=%u blockN=%u", state->cellN, state->blockN);
+#endif
+
+ current_pos = target;
+ cur_sector = this->vobu.vobu_start + this->vobu.blockN;
+ cur_cell_nr = state->cellN;
+
+ switch(origin) {
+ case SEEK_SET:
+ if(offset >= length) {
+ printerr("Request to seek behind end.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ target = offset;
+ break;
+ case SEEK_CUR:
+ if((signed)target + offset >= length) {
+ printerr("Request to seek behind end.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ if((signed)target + offset < 0) {
+ printerr("Request to seek before start.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ target += offset;
+ break;
+ case SEEK_END:
+ if(length < offset) {
+ printerr("Request to seek before start.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ target = length - offset;
+ break;
+ default:
+ /* Error occurred */
+ printerr("Illegal seek mode.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ forward = target > current_pos;
+
+ this->cur_cell_time = 0;
+ if (this->pgc_based) {
+ first_cell_nr = 1;
+ last_cell_nr = state->pgc->nr_of_cells;
+ } else {
+ /* Find start cell of program. */
+ first_cell_nr = state->pgc->program_map[state->pgN-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
+ last_cell_nr = state->pgc->nr_of_cells;
+ }
+
+ found = 0;
+ for(cell_nr = first_cell_nr; (cell_nr <= last_cell_nr) && !found; cell_nr ++) {
+ cell = &(state->pgc->cell_playback[cell_nr-1]);
+ if(cell->block_type == BLOCK_TYPE_ANGLE_BLOCK && cell->block_mode != BLOCK_MODE_FIRST_CELL)
+ continue;
+ length = cell->last_sector - cell->first_sector + 1;
+ if (target >= length) {
+ target -= length;
+ } else {
+ /* convert the target sector from Cell-relative to absolute physical sector */
+ target += cell->first_sector;
+ if (forward && (cell_nr == cur_cell_nr)) {
+ uint32_t vobu;
+ /* if we are seeking forward from the current position, make sure
+ * we move to a new position that is after our current position.
+ * simply truncating to the vobu will go backwards */
+ if (dvdnav_scan_admap(this, state->domain, target, 0, &vobu) != DVDNAV_STATUS_OK)
+ break;
+ if (vobu <= cur_sector) {
+ if (dvdnav_scan_admap(this, state->domain, target, 1, &vobu) != DVDNAV_STATUS_OK)
+ break;
+ if (vobu > cell->last_sector) {
+ if (cell_nr == last_cell_nr)
+ break;
+ cell_nr++;
+ cell = &(state->pgc->cell_playback[cell_nr-1]);
+ target = cell->first_sector;
+ } else {
+ target = vobu;
+ }
+ }
+ }
+ found = 1;
+ break;
+ }
+ }
+
+ if(found) {
+ uint32_t vobu;
+#ifdef LOG_DEBUG
+ Log3(this, "Seeking to cell %i from choice of %i to %i",
+ cell_nr, first_cell_nr, last_cell_nr);
+#endif
+ if (dvdnav_scan_admap(this, state->domain, target, 0, &vobu) == DVDNAV_STATUS_OK) {
+ int32_t start = state->pgc->cell_playback[cell_nr-1].first_sector;
+
+ if (vm_jump_cell_block(this->vm, cell_nr, vobu - start)) {
+#ifdef LOG_DEBUG
+ Log3(this, "After cellN=%u blockN=%u target=%x vobu=%x start=%x" ,
+ state->cellN, state->blockN, target, vobu, start);
+#endif
+ this->vm->hop_channel += HOP_SEEK;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+ }
+ }
+
+ Log0(this, "Error when seeking");
+ Log1(this, "FIXME: Implement seeking to location %u", target);
+ printerr("Error when seeking.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_part_search(dvdnav_t *this, int32_t part) {
+ int32_t title, old_part;
+
+ if (dvdnav_current_title_info(this, &title, &old_part) == DVDNAV_STATUS_OK)
+ return dvdnav_part_play(this, title, part);
+ return DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_prev_pg_search(dvdnav_t *this) {
+ pthread_mutex_lock(&this->vm_lock);
+ if(!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+#ifdef LOG_DEBUG
+ Log3(this, "previous chapter");
+#endif
+ if (!vm_jump_prev_pg(this->vm)) {
+ Log0(this, "previous chapter failed.");
+ printerr("Skip to previous chapter failed.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ this->cur_cell_time = 0;
+ this->position_current.still = 0;
+ this->vm->hop_channel++;
+#ifdef LOG_DEBUG
+ Log3(this, "previous chapter done");
+#endif
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_top_pg_search(dvdnav_t *this) {
+ pthread_mutex_lock(&this->vm_lock);
+ if(!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+#ifdef LOG_DEBUG
+ Log3(this, "top chapter");
+#endif
+ if (!vm_jump_top_pg(this->vm)) {
+ Log0(this, "top chapter failed.");
+ printerr("Skip to top chapter failed.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ this->cur_cell_time = 0;
+ this->position_current.still = 0;
+ this->vm->hop_channel++;
+#ifdef LOG_DEBUG
+ Log3(this, "top chapter done");
+#endif
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_next_pg_search(dvdnav_t *this) {
+ vm_t *try_vm;
+
+ pthread_mutex_lock(&this->vm_lock);
+ if(!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ goto fail;
+ }
+
+#ifdef LOG_DEBUG
+ Log3(this, "next chapter");
+#endif
+ /* make a copy of current VM and try to navigate the copy to the next PG */
+ try_vm = vm_new_copy(this->vm);
+ if (try_vm == NULL) {
+ printerr("Unable to copy the VM.");
+ goto fail;
+ }
+
+ if (!vm_jump_next_pg(try_vm) || try_vm->stopped) {
+ vm_free_copy(try_vm);
+ /* next_pg failed, try to jump at least to the next cell */
+ try_vm = vm_new_copy(this->vm);
+ if (try_vm == NULL) {
+ printerr("Unable to copy the VM.");
+ goto fail;
+ }
+ vm_get_next_cell(try_vm);
+ if (try_vm->stopped) {
+ vm_free_copy(try_vm);
+ Log0(this, "next chapter failed.");
+ printerr("Skip to next chapter failed.");
+ goto fail;
+ }
+ }
+ this->cur_cell_time = 0;
+ /* merge changes on success */
+ vm_merge(this->vm, try_vm);
+ vm_free_copy(try_vm);
+ this->position_current.still = 0;
+ this->vm->hop_channel++;
+#ifdef LOG_DEBUG
+ Log3(this, "next chapter done");
+#endif
+ pthread_mutex_unlock(&this->vm_lock);
+
+ return DVDNAV_STATUS_OK;
+
+fail:
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_menu_call(dvdnav_t *this, DVDMenuID_t menu) {
+ vm_t *try_vm;
+
+ pthread_mutex_lock(&this->vm_lock);
+ if(!this->vm->state.pgc) {
+ printerr("No current PGC.");
+ goto fail;
+ }
+
+ this->cur_cell_time = 0;
+ /* make a copy of current VM and try to navigate the copy to the menu */
+ try_vm = vm_new_copy(this->vm);
+ if (try_vm == NULL) {
+ printerr("Unable to copy VM.");
+ goto fail;
+ }
+
+ if ( (menu == DVD_MENU_Escape) && (this->vm->state.domain != DVD_DOMAIN_VTSTitle)) {
+ /* Try resume */
+ if (vm_jump_resume(try_vm) && !try_vm->stopped) {
+ /* merge changes on success */
+ vm_merge(this->vm, try_vm);
+ vm_free_copy(try_vm);
+ this->position_current.still = 0;
+ this->vm->hop_channel++;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ }
+ }
+ if (menu == DVD_MENU_Escape) menu = DVD_MENU_Root;
+
+ if (vm_jump_menu(try_vm, menu) && !try_vm->stopped) {
+ /* merge changes on success */
+ vm_merge(this->vm, try_vm);
+ vm_free_copy(try_vm);
+ this->position_current.still = 0;
+ this->vm->hop_channel++;
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+ } else {
+ vm_free_copy(try_vm);
+ printerr("No such menu or menu not reachable.");
+ goto fail;
+ }
+
+fail:
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+}
+
+dvdnav_status_t dvdnav_get_position(dvdnav_t *this, uint32_t *pos,
+ uint32_t *len) {
+ uint32_t cur_sector;
+ int32_t cell_nr, first_cell_nr, last_cell_nr;
+ const cell_playback_t *cell;
+ dvd_state_t *state;
+
+ if(!this->started) {
+ printerr("Virtual DVD machine not started.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ state = &(this->vm->state);
+ if(!state->pgc || this->vm->stopped) {
+ printerr("No current PGC.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+ if (this->position_current.hop_channel != this->vm->hop_channel ||
+ this->position_current.domain != state->domain ||
+ this->position_current.vts != state->vtsN ||
+ this->position_current.cell_restart != state->cell_restart) {
+ printerr("New position not yet determined.");
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_ERR;
+ }
+
+ /* Get current sector */
+ cur_sector = this->vobu.vobu_start + this->vobu.blockN;
+
+ if (this->pgc_based) {
+ first_cell_nr = 1;
+ last_cell_nr = state->pgc->nr_of_cells;
+ } else {
+ /* Find start cell of program. */
+ first_cell_nr = state->pgc->program_map[state->pgN-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
+ last_cell_nr = state->pgc->nr_of_cells;
+ }
+
+ *pos = -1;
+ *len = 0;
+ for (cell_nr = first_cell_nr; cell_nr <= last_cell_nr; cell_nr++) {
+ cell = &(state->pgc->cell_playback[cell_nr-1]);
+ if(cell->block_type == BLOCK_TYPE_ANGLE_BLOCK && cell->block_mode != BLOCK_MODE_FIRST_CELL)
+ continue;
+ if (cell_nr == state->cellN) {
+ /* the current sector is in this cell,
+ * pos is length of PG up to here + sector's offset in this cell */
+ *pos = *len + cur_sector - cell->first_sector;
+ }
+ *len += cell->last_sector - cell->first_sector + 1;
+ }
+
+ pthread_mutex_unlock(&this->vm_lock);
+
+ if((signed)*pos == -1)
+ return DVDNAV_STATUS_ERR;
+
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_get_position_in_title(dvdnav_t *this,
+ uint32_t *pos,
+ uint32_t *len) {
+ uint32_t cur_sector;
+ uint32_t first_cell_nr;
+ uint32_t last_cell_nr;
+ const cell_playback_t *first_cell;
+ const cell_playback_t *last_cell;
+ dvd_state_t *state;
+
+ state = &(this->vm->state);
+ if(!state->pgc) {
+ printerr("No current PGC.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ if (state->pgc->program_map == NULL) {
+ printerr("Program map missing.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ /* Get current sector */
+ cur_sector = this->vobu.vobu_start + this->vobu.blockN;
+
+ /* Now find first and last cells in title. */
+ first_cell_nr = state->pgc->program_map[0];
+ first_cell = &(state->pgc->cell_playback[first_cell_nr-1]);
+ last_cell_nr = state->pgc->nr_of_cells;
+ last_cell = &(state->pgc->cell_playback[last_cell_nr-1]);
+
+ *pos = cur_sector - first_cell->first_sector;
+ *len = last_cell->last_sector - first_cell->first_sector;
+
+ return DVDNAV_STATUS_OK;
+}
+
+uint32_t dvdnav_describe_title_chapters(dvdnav_t *this, int32_t title, uint64_t **times, uint64_t *duration) {
+ int32_t retval=0;
+ uint16_t parts, i;
+ const title_info_t *ptitle = NULL;
+ const ptt_info_t *ptt = NULL;
+ ifo_handle_t *ifo = NULL;
+ const pgc_t *pgc;
+ const cell_playback_t *cell;
+ uint64_t length, *tmp=NULL;
+
+ *times = NULL;
+ *duration = 0;
+ pthread_mutex_lock(&this->vm_lock);
+ if(!this->vm->vmgi) {
+ printerr("Bad VM state or missing VTSI.");
+ goto fail;
+ }
+ if(!this->started) {
+ /* don't report an error but be nice */
+ vm_start(this->vm);
+ this->started = 1;
+ }
+ ifo = vm_get_title_ifo(this->vm, title);
+ if(!ifo || !ifo->vts_pgcit) {
+ printerr("Couldn't open IFO for chosen title, exit.");
+ retval = 0;
+ goto fail;
+ }
+
+ ptitle = &this->vm->vmgi->tt_srpt->title[title-1];
+ parts = ptitle->nr_of_ptts;
+ if(ptitle->vts_ttn == 0)
+ goto fail;
+ ptt = ifo->vts_ptt_srpt->title[ptitle->vts_ttn-1].ptt;
+
+ tmp = calloc(1, sizeof(uint64_t)*parts);
+ if(!tmp)
+ goto fail;
+
+ if(!ptt) {
+ printerr("ptt NULL");
+ goto fail;
+ }
+
+ length = 0;
+ for(i=0; i<parts; i++) {
+ uint32_t cellnr, endcellnr;
+ if (ptt[i].pgcn == 0 || ptt[i].pgcn > ifo->vts_pgcit->nr_of_pgci_srp) {
+ printerr("PGCN out of bounds.");
+ continue;
+ }
+ if (ifo->vts_pgcit->pgci_srp[ptt[i].pgcn-1].pgc_start_byte >= ifo->vts_pgcit->last_byte) {
+ printerr("PGC start out of bounds");
+ continue;
+ }
+ if (0 == ifo->vts_pgcit->pgci_srp[ptt[i].pgcn-1].pgc_start_byte) {
+ printerr("PGC start zero.");
+ continue;
+ }
+ if (0 != (ifo->vts_pgcit->pgci_srp[ptt[i].pgcn-1].pgc_start_byte & 1)) {
+ printerr("PGC start unaligned.");
+ continue;
+ }
+ if (0 != ((uintptr_t)(ifo->vts_pgcit->pgci_srp[ptt[i].pgcn-1].pgc) & 1)) {
+ printerr("PGC pointer unaligned.");
+ continue;
+ }
+ pgc = ifo->vts_pgcit->pgci_srp[ptt[i].pgcn-1].pgc;
+ if (pgc == NULL) {
+ printerr("PGC missing.");
+ continue;
+ }
+ if (pgc->program_map == NULL) {
+ printerr("Program map missing.");
+ continue;
+ }
+ if(ptt[i].pgn == 0 || ptt[i].pgn > pgc->nr_of_programs) {
+ printerr("WRONG part number.");
+ goto fail;
+ }
+
+ if (pgc->nr_of_cells == 0) {
+ printerr("Number of cells cannot be 0");
+ continue;
+ }
+ if ((cellnr = pgc->program_map[ptt[i].pgn-1]) == 0) {
+ printerr("Cell new row cannot be 0");
+ continue;
+ }
+ if (pgc->cell_playback == NULL) {
+ printerr("Cell missing");
+ continue;
+ }
+
+ if(ptt[i].pgn < pgc->nr_of_programs)
+ endcellnr = pgc->program_map[ptt[i].pgn];
+ else
+ endcellnr = 0;
+
+ do {
+ cell = &pgc->cell_playback[cellnr-1];
+ if(!(cell->block_type == BLOCK_TYPE_ANGLE_BLOCK &&
+ cell->block_mode != BLOCK_MODE_FIRST_CELL
+ ))
+ {
+ tmp[i] = length + dvdnav_convert_time(&cell->playback_time);
+ length = tmp[i];
+ }
+ cellnr++;
+ } while(cellnr < endcellnr);
+ }
+ *duration = length;
+ vm_ifo_close(ifo);
+ ifo = NULL;
+ retval = parts;
+ *times = tmp;
+
+fail:
+ pthread_mutex_unlock(&this->vm_lock);
+ if(!retval && ifo)
+ vm_ifo_close(ifo);
+ if(!retval && tmp)
+ free(tmp);
+ return retval;
+}
+
+/* Get an admap and admap_len */
+static vobu_admap_t* dvdnav_admap_get(dvdnav_t *this, dvd_state_t *state,
+ int32_t *admap_len) {
+ vobu_admap_t *admap = NULL;
+ switch(state->domain) {
+ case DVD_DOMAIN_FirstPlay:
+ case DVD_DOMAIN_VMGM:
+ admap = this->vm->vmgi->menu_vobu_admap;
+ break;
+ case DVD_DOMAIN_VTSMenu:
+ admap = this->vm->vtsi->menu_vobu_admap;
+ break;
+ case DVD_DOMAIN_VTSTitle:
+ admap = this->vm->vtsi->vts_vobu_admap;
+ break;
+ default: {
+ Log1(this, "Unknown domain");
+ return NULL;
+ }
+ }
+ if (admap == NULL) return NULL;
+
+ *admap_len = (admap->last_byte + 1 - VOBU_ADMAP_SIZE) / VOBU_ADMAP_SIZE;
+ if (*admap_len <= 0) {
+ Log1(this, "admap_len <= 0");
+ return NULL;
+ }
+ return admap;
+}
+
+/* Get a tmap, tmap_len and tmap_interval */
+static const vts_tmap_t* dvdnav_tmap_get(dvdnav_t *this, dvd_state_t *state,
+ int32_t *tmap_len, int32_t *tmap_interval) {
+ int32_t domain;
+ ifo_handle_t *ifo = NULL;
+ vts_tmapt_t *tmapt = NULL;
+ uint16_t tmap_count = 0;
+ int32_t pgcN = 0;
+ const vts_tmap_t *tmap = NULL;
+ int32_t result = 0;
+
+ domain = state->domain;
+ switch(domain) {
+ case DVD_DOMAIN_FirstPlay:
+ case DVD_DOMAIN_VTSMenu:
+ case DVD_DOMAIN_VMGM: {
+ ifo = this->vm->vmgi;
+ break;
+ }
+ case DVD_DOMAIN_VTSTitle: {
+ ifo = this->vm->vtsi;
+ break;
+ }
+ default: {
+ Log1(this, "unknown domain for tmap");
+ return NULL;
+ }
+ }
+ if (ifo == NULL) return NULL;
+ tmapt = ifo->vts_tmapt;
+ /* HACK: ifo->vts_tmapt is NULL b/c ifo_read.c never loads it
+ * load ifo->vts_tmapt directly*/
+ if (tmapt == NULL) {
+ result = ifoRead_VTS_TMAPT(ifo);
+ if (!result) {
+ return NULL;
+ }
+ tmapt = ifo->vts_tmapt;
+ if (tmapt == NULL) return NULL;
+ }
+
+ tmap_count = tmapt->nr_of_tmaps;
+ pgcN = state->pgcN - 1; /* -1 b/c pgcN is base1 */
+ if (pgcN < 0) {
+ Log1(this, "pgcN < 0");
+ return NULL;
+ }
+
+ /* get tmap */
+ switch(domain) {
+ case DVD_DOMAIN_FirstPlay:
+ case DVD_DOMAIN_VMGM:
+ case DVD_DOMAIN_VTSMenu: {
+ if (tmap_count == 0) {
+ Log1(this, "tmap_count == 0");
+ return NULL;
+ }
+ tmap = &tmapt->tmap[0]; /* ASSUME: vmgi only has one time map */
+ break;
+ }
+ case DVD_DOMAIN_VTSTitle: {
+ if (pgcN >= tmap_count) {
+ Log1(this, "pgcN >= tmap_count; pgcN=%i tmap_count=%i",
+ pgcN, tmap_count);
+ return NULL;
+ }
+ tmap = &tmapt->tmap[pgcN];
+ break;
+ }
+ }
+ if (tmap == NULL) return NULL;
+
+ /* tmap->tmu is in seconds; convert to millisecs */
+ *tmap_interval = tmap->tmu * 1000;
+ if (*tmap_interval == 0) {
+ Log1(this, "tmap_interval == 0");
+ return NULL;
+ }
+ *tmap_len = tmap->nr_of_entries;
+ if (*tmap_len == 0) {
+ Log1(this, "tmap_len == 0");
+ return NULL;
+ }
+ return tmap;
+}
+
+/* Get a sector from a tmap */
+static int32_t dvdnav_tmap_get_entry(dvdnav_t *this,
+ const vts_tmap_t *tmap, uint16_t tmap_len,
+ int32_t idx, uint32_t *sector) {
+ /* tmaps start at idx 0 which represents a sector at time 1 * tmap_interval
+ * this creates a "fake" tmap index at idx -1 for sector 0 */
+ if (idx == TMAP_IDX_EDGE_BGN) {
+ *sector = 0;
+ return 1;
+ }
+ if (idx < TMAP_IDX_EDGE_BGN || idx >= tmap_len) {
+ Log1(this, "idx out of bounds idx=%i %i", idx, tmap_len);
+ return 0;
+ }
+ /* 0x7fffffff unsets discontinuity bit if present */
+ *sector = tmap->map_ent[idx] & 0x7fffffff;
+ return 1;
+}
+
+/* Do a binary search for earlier admap index near find_sector */
+static int32_t dvdnav_admap_search(vobu_admap_t *admap, uint32_t admap_len,
+ uint32_t find_sector, uint32_t *vobu) {
+ int32_t adj = 1;
+ int32_t prv_pos = 0;
+ int32_t prv_len = admap_len;
+ int32_t cur_len = 0;
+ int32_t cur_idx = 0;
+ uint32_t cur_sector = 0;
+ while (1) {
+ cur_len = prv_len / 2;
+ /* need to add 1 when prv_len == 3 (cur_len should go to 2, not 1) */
+ if (prv_len % 2 == 1) ++cur_len;
+ cur_idx = prv_pos + (cur_len * adj);
+ if (cur_idx < 0)
+ cur_idx = 0;
+ else if (cur_idx >= (int32_t)admap_len)
+ cur_idx = admap_len - 1;
+
+ cur_sector = admap->vobu_start_sectors[cur_idx];
+ if (find_sector < cur_sector) adj = -1;
+ else if (find_sector > cur_sector) adj = 1;
+ else if (find_sector == cur_sector) {
+ *vobu = cur_idx;
+ return 1;
+ }
+ if (cur_len == 1) {/* no smaller intervals left */
+ if (adj == -1) {/* last comparison was greater; take lesser */
+ cur_idx -= 1;
+ cur_sector = admap->vobu_start_sectors[cur_idx];
+ }
+ *vobu = cur_idx;
+ return 1;
+ }
+ prv_len = cur_len;
+ prv_pos = cur_idx;
+ }
+}
+
+/* Do a binary search for the earlier tmap entry near find_sector */
+static int32_t dvdnav_tmap_search(dvdnav_t *this,
+ const vts_tmap_t *tmap, uint32_t tmap_len,
+ uint32_t find_sector, int32_t *tmap_idx, uint32_t *sector) {
+ int32_t adj = 1;
+ int32_t prv_pos = 0;
+ int32_t prv_len = tmap_len;
+ int32_t result = 0;
+ int32_t cur_len = 0;
+ int32_t cur_idx = 0;
+ uint32_t cur_sector = 0;
+ while (1) {
+ cur_len = prv_len / 2;
+ /* need to add 1 when prv_len == 3 (cur_len should go to 2, not 1) */
+ if (prv_len % 2 == 1) ++cur_len;
+ cur_idx = prv_pos + (cur_len * adj);
+ if (cur_idx < 0)
+ cur_idx = 0;
+ else if (cur_idx >= (int32_t)tmap_len)
+ cur_idx = tmap_len - 1;
+ cur_sector = 0;
+ result = dvdnav_tmap_get_entry(this, tmap, tmap_len, cur_idx, &cur_sector);
+ if (!result) return 0;
+ if (find_sector < cur_sector) adj = -1;
+ else if (find_sector > cur_sector) adj = 1;
+ else if (find_sector == cur_sector) {
+ *tmap_idx = cur_idx;
+ *sector = cur_sector;
+ return 1;
+ }
+ if (cur_len == 1) {/* no smaller intervals left */
+ if (adj == -1) {/* last comparison was greater; take lesser */
+ if (cur_idx == 0) { /* fake tmap index for sector 0 */
+ cur_idx = TMAP_IDX_EDGE_BGN;
+ cur_sector = 0;
+ }
+ else {
+ cur_idx -= 1;
+ result = dvdnav_tmap_get_entry(this, tmap, tmap_len, cur_idx, &cur_sector);
+ if (!result) return 0;
+ }
+ }
+ *tmap_idx = cur_idx;
+ *sector = cur_sector;
+ return 1;
+ }
+ prv_len = cur_len;
+ prv_pos = cur_idx;
+ }
+}
+
+/* Find the cell for a given time */
+static int32_t dvdnav_cell_find(dvdnav_t *this, dvd_state_t *state,
+ uint64_t find_val, dvdnav_cell_data_t *cell_data) {
+ uint32_t cells_len = 0;
+ uint32_t cells_bgn = 0;
+ uint32_t cells_end = 0;
+ uint32_t cell_idx = 0;
+ const pgc_t *pgc = NULL;
+ int pgN = 0;
+ const cell_playback_t *cell = NULL;
+ int found = 0;
+
+ pgc = state->pgc;
+ if (pgc == NULL) return 0;
+ cells_len = pgc->nr_of_cells;
+ if (cells_len == 0) {
+ Log1(this, "cells_len == 0");
+ return 0;
+ }
+
+ /* get cells_bgn, cells_end */
+ if (this->pgc_based) {
+ cells_bgn = 1;
+ cells_end = cells_len;
+ }
+ else {
+ pgN = state->pgN;
+ cells_bgn = pgc->program_map[pgN - 1]; /* -1 b/c pgN is 1 based? */
+ if (pgN < pgc->nr_of_programs) {
+ cells_end = pgc->program_map[pgN] - 1;
+ }
+ else {
+ cells_end = cells_len;
+ }
+ }
+
+ /* search cells */
+ for (cell_idx = cells_bgn; cell_idx <= cells_end; cell_idx++) {
+ cell = &(pgc->cell_playback[cell_idx - 1]); /* -1 b/c cell is base1 */
+ /* if angle block, only consider first angleBlock
+ * (others are "redundant" for purpose of search) */
+ if ( cell->block_type == BLOCK_TYPE_ANGLE_BLOCK
+ && cell->block_mode != BLOCK_MODE_FIRST_CELL) {
+ continue;
+ }
+ cell_data->bgn->sector = cell->first_sector;
+ cell_data->end->sector = cell->last_sector;
+
+ /* 90 pts to ms */
+ cell_data->end->time += (dvdnav_convert_time(&cell->playback_time) / 90);
+ if ( find_val >= cell_data->bgn->time
+ && find_val <= cell_data->end->time) {
+ found = 1;
+ break;
+ }
+ cell_data->bgn->time = cell_data->end->time;
+ }
+
+ /* found cell: set var */
+ if (found) {
+ cell_data->idx = cell_idx;
+ }
+ else
+ Log1(this, "cell not found; find=%"PRId64, find_val);
+ return found;
+}
+
+/* Given two sectors and a fraction, calc the corresponding vobu */
+static int32_t dvdnav_admap_interpolate_vobu(dvdnav_t *this,
+ dvdnav_jump_args_t *args,
+ dvdnav_pos_data_t *bgn, dvdnav_pos_data_t *end, uint32_t fraction,
+ uint32_t *jump_sector) {
+ int32_t result = 0;
+ uint32_t vobu_len = 0;
+ uint32_t vobu_adj = 0;
+ uint32_t vobu_idx = 0;
+
+ /* get bgn->vobu_idx */
+ result = dvdnav_admap_search(args->admap, args->admap_len,
+ bgn->sector, &bgn->vobu_idx);
+ if (!result) {
+ Log1(this, "admap_interpolate: could not find sector_bgn");
+ return 0;
+ }
+
+ /* get end->vobu_idx */
+ result = dvdnav_admap_search(args->admap, args->admap_len,
+ end->sector, &end->vobu_idx);
+ if (!result) {
+ Log1(this, "admap_interpolate: could not find sector_end");
+ return 0;
+ }
+
+ vobu_len = end->vobu_idx - bgn->vobu_idx;
+ /* +500 to round up else 74% of a 4 sec interval = 2 sec */
+ vobu_adj = ((fraction * vobu_len) + 500) / 1000;
+ /* HACK: need to add +1, or else will land too soon (not sure why) */
+ vobu_adj++;
+ vobu_idx = bgn->vobu_idx + vobu_adj;
+ if ((int32_t)vobu_idx >= args->admap_len) {
+ Log1(this, "admap_interpolate: vobu_idx >= admap_len");
+ return 0;
+ }
+ *jump_sector = args->admap->vobu_start_sectors[vobu_idx];
+ return 1;
+}
+
+/* Given two tmap entries and a time, calc the time for the lo tmap entry */
+static int32_t dvdnav_tmap_calc_time_for_tmap_entry(dvdnav_t *this,
+ dvdnav_jump_args_t *args,
+ dvdnav_pos_data_t *lo, dvdnav_pos_data_t *hi,
+ dvdnav_pos_data_t *pos, uint64_t *out_time) {
+ int32_t result = 0;
+ int32_t vobu_pct = 0;
+ uint64_t time_adj = 0;
+
+ if (lo->sector == hi->sector) {
+ Log1(this, "lo->sector == hi->sector: %i", lo->sector);
+ return 0;
+ }
+
+ /* get vobus corresponding to lo, hi, pos */
+ result = dvdnav_admap_search(args->admap, args->admap_len,
+ lo->sector, &lo->vobu_idx);
+ if (!result) {
+ Log1(this, "lo->vobu: lo->sector=%i", lo->sector);
+ return 0;
+ }
+ result = dvdnav_admap_search(args->admap, args->admap_len,
+ hi->sector, &hi->vobu_idx);
+ if (!result) {
+ Log1(this, "hi->vobu: hi->sector=%i", hi->sector);
+ return 0;
+ }
+ result = dvdnav_admap_search(args->admap, args->admap_len,
+ pos->sector, &pos->vobu_idx);
+ if (!result) {
+ Log1(this, "pos->vobu: pos->sector=%i", pos->sector);
+ return 0;
+ }
+
+ /* calc position of cell relative to lo */
+ if (hi->vobu_idx == lo->vobu_idx) {
+ /* We are at the very end - pos should also equal lo so force that
+ * rather than hit the divide-by-zero. */
+ vobu_pct = 0;
+ } else {
+ vobu_pct = ((pos->vobu_idx - lo->vobu_idx) * 1000)
+ / ( hi->vobu_idx - lo->vobu_idx);
+ }
+ if (vobu_pct < 0 || vobu_pct > 1000) {
+ Log1(this, "vobu_pct must be between 0 and 1000");
+ return 0;
+ }
+
+ /* calc time of lo */
+ time_adj = ((uint64_t)args->tmap_interval * vobu_pct) / 1000;
+ *out_time = pos->time - time_adj;
+ return 1;
+}
+
+/* Find the tmap entries on either side of a given sector */
+static int32_t dvdnav_tmap_get_entries_for_sector(
+ dvdnav_t *this,
+ dvdnav_jump_args_t *args,
+ dvdnav_cell_data_t *cell_data, uint32_t find_sector,
+ dvdnav_pos_data_t *lo, dvdnav_pos_data_t *hi) {
+ int32_t result = 0;
+
+ result = dvdnav_tmap_search(this,
+ args->tmap, args->tmap_len, find_sector,
+ &lo->tmap_idx, &lo->sector);
+ if (!result) {
+ Log1(this, "could not find lo idx: %i", find_sector);
+ return 0;
+ }
+
+ /* HACK: Most DVDs have a tmap that starts at sector 0
+ * However, some have initial dummy cells that are not seekable
+ * (restricted = y).
+ * These cells will throw off the tmap calcs when in the first playable cell.
+ * For now, assume that lo->sector is equal to the cell->bgn->sector
+ * Note that for most DVDs this will be 0
+ * (Since they will have no dummy cells and cell 1 will start at sector 0)
+ */
+ if (lo->tmap_idx == TMAP_IDX_EDGE_BGN) {
+ lo->sector = cell_data->bgn->sector;
+ }
+
+ if (lo->tmap_idx == args->tmap_len - 1) {
+ /* lo is last tmap entry; "fake" entry for one beyond
+ * and mark it with cell_end_sector */
+ hi->tmap_idx = TMAP_IDX_EDGE_END;
+ hi->sector = cell_data->end->sector;
+ }
+ else {
+ hi->tmap_idx = lo->tmap_idx + 1;
+ result = dvdnav_tmap_get_entry(this, args->tmap, args->tmap_len,
+ hi->tmap_idx, &hi->sector);
+ if (!result) {
+ Log1(this, "could not find hi idx: %i", find_sector);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* Find the nearest vobu by using the tmap */
+static int32_t dvdnav_find_vobu_by_tmap(dvdnav_t *this, dvd_state_t *state,
+ dvdnav_jump_args_t *args, dvdnav_cell_data_t *cell_data,
+ dvdnav_pos_data_t *jump) {
+ uint64_t seek_offset = 0;
+ uint32_t seek_idx = 0;
+ int32_t result = 0;
+ dvdnav_pos_data_t *cell_bgn_lo = NULL;
+ dvdnav_pos_data_t *cell_bgn_hi = NULL;
+ dvdnav_pos_data_t *jump_lo = NULL;
+ dvdnav_pos_data_t *jump_hi = NULL;
+
+ /* get tmap, tmap_len, tmap_interval */
+ args->tmap = dvdnav_tmap_get(this, state,
+ &args->tmap_len, &args->tmap_interval);
+ if (args->tmap == NULL) return 0;
+
+ /* get tmap entries on either side of cell_bgn */
+ cell_bgn_lo = &(dvdnav_pos_data_t){0};
+ cell_bgn_hi = &(dvdnav_pos_data_t){0};
+ result = dvdnav_tmap_get_entries_for_sector(this, args, cell_data,
+ cell_data->bgn->sector, cell_bgn_lo, cell_bgn_hi);
+ if (!result) return 0;
+
+ /* calc time of cell_bgn_lo */
+ result = dvdnav_tmap_calc_time_for_tmap_entry(this,
+ args, cell_bgn_lo, cell_bgn_hi,
+ cell_data->bgn, &cell_bgn_lo->time);
+ if (!result) return 0;
+
+ /* calc time of jump_time relative to cell_bgn_lo */
+ seek_offset = jump->time - cell_bgn_lo->time;
+ seek_idx = (uint32_t)(seek_offset / args->tmap_interval);
+ uint32_t seek_remainder = seek_offset - (seek_idx * args->tmap_interval);
+ uint32_t seek_pct = (seek_remainder * 1000) / args->tmap_interval;
+
+ /* get tmap entries on either side of jump_time */
+ jump_lo = &(dvdnav_pos_data_t){0};
+ jump_hi = &(dvdnav_pos_data_t){0};
+
+ /* if seek_idx == 0, then tmap_indexes are the same, do not re-get
+ * also, note cell_bgn_lo will already have sector if TMAP_IDX_EDGE_BGN */
+ if (seek_idx == 0) {
+ jump_lo = cell_bgn_lo;
+ jump_hi = cell_bgn_hi;
+ }
+ else {
+ jump_lo->tmap_idx = (uint32_t)(cell_bgn_lo->tmap_idx + seek_idx);
+ result = dvdnav_tmap_get_entry(this, args->tmap, args->tmap_len,
+ jump_lo->tmap_idx, &jump_lo->sector);
+ if (!result) return 0;
+
+ /* +1 handled by dvdnav_tmap_get_entry */
+ jump_hi->tmap_idx = jump_lo->tmap_idx + 1;
+ result = dvdnav_tmap_get_entry(this, args->tmap, args->tmap_len,
+ jump_hi->tmap_idx, &jump_hi->sector);
+ if (!result) return 0;
+ }
+
+ /* interpolate sector */
+ result = dvdnav_admap_interpolate_vobu(this, args, jump_lo, jump_hi,
+ seek_pct, &jump->sector);
+
+ return result;
+}
+
+/* Find the nearest vobu by using the cell boundaries */
+static int32_t dvdnav_find_vobu_by_cell_boundaries(dvdnav_t *this,
+ dvdnav_jump_args_t *args, dvdnav_cell_data_t *cell_data,
+ dvdnav_pos_data_t *jump) {
+ int64_t jump_offset = 0;
+ int64_t cell_len = 0;
+ uint32_t jump_pct = 0;
+ int32_t result = 0;
+
+ /* get jump_offset */
+ jump_offset = jump->time - cell_data->bgn->time;
+ if (jump_offset < 0) {
+ Log1(this, "jump_offset < 0");
+ return 0;
+ }
+ cell_len = cell_data->end->time - cell_data->bgn->time;
+ if (cell_len < 0) {
+ Log1(this, "cell_len < 0");
+ return 0;
+ }
+ jump_pct = (jump_offset * 1000) / cell_len;
+
+ /* get sector */
+ /* NOTE: end cell sector in VTS_PGC is last sector of cell
+ * this last sector is not the start of a VOBU
+ * +1 to get sector that is the start of a VOBU
+ * start of a VOBU is needed in order to index into admap */
+ cell_data->end->sector += 1;
+ result = dvdnav_admap_interpolate_vobu(this, args,
+ cell_data->bgn, cell_data->end, jump_pct, &jump->sector);
+ if (!result) {
+ Log1(this, "find_by_admap.interpolate");
+ return 0;
+ }
+ return 1;
+}
+
+/* Jump to sector by time */
+/* NOTE: Mode is currently unimplemented. Only 0 should be passed. */
+/* 1 and -1 are for future implementation */
+/* 0: Default. Jump to a time which may be either <> time_in_pts_ticks */
+/* 1: After. Always jump to a time that is > time_in_pts_ticks */
+/* -1: Before. Always jump to a time that is < time_in_pts_ticks */
+dvdnav_status_t dvdnav_jump_to_sector_by_time(dvdnav_t *this,
+ uint64_t time_in_pts_ticks, int32_t mode) {
+ if (mode != JUMP_MODE_TIME_DEFAULT) return DVDNAV_STATUS_ERR;
+ int32_t result = DVDNAV_STATUS_ERR;
+ dvd_state_t *state = NULL;
+ uint32_t sector_off = 0;
+ dvdnav_pos_data_t *jump = NULL;
+ dvdnav_cell_data_t *cell_data = NULL;
+ dvdnav_jump_args_t *args = NULL;
+
+ jump = &(dvdnav_pos_data_t){0};
+ /* convert time to milliseconds */
+ jump->time = time_in_pts_ticks / 90;
+
+ /* get variables that will be used across both functions */
+ state = &(this->vm->state);
+ if (state == NULL) goto exit;
+
+ /* get cell info */
+ cell_data = &(dvdnav_cell_data_t){0};
+ cell_data->bgn = &(dvdnav_pos_data_t){0};
+ cell_data->end = &(dvdnav_pos_data_t){0};
+ result = dvdnav_cell_find(this, state, jump->time, cell_data);
+ if (!result) goto exit;
+
+ /* get admap */
+ args = &(dvdnav_jump_args_t){0};
+ args->admap = dvdnav_admap_get(this, state, &args->admap_len);
+ if (args->admap == NULL) goto exit;
+
+ /* find sector */
+ result = dvdnav_find_vobu_by_tmap(this, state, args, cell_data, jump);
+ if (!result) {/* bad tmap; interpolate over cell */
+ result = dvdnav_find_vobu_by_cell_boundaries(this, args, cell_data, jump);
+ if (!result) {
+ goto exit;
+ }
+ }
+
+ /* jump to sector */
+ sector_off = jump->sector - cell_data->bgn->sector;
+ result = vm_jump_cell_block(this->vm, cell_data->idx, sector_off);
+ pthread_mutex_lock(&this->vm_lock);
+ this->cur_cell_time = 0;
+ if (result) { /* vm_jump_cell_block was successful */
+ this->vm->hop_channel += HOP_SEEK;
+ }
+ pthread_mutex_unlock(&this->vm_lock);
+
+exit:
+ return result;
+}
+
diff --git a/libdvdnav-embedded/src/settings.c b/libdvdnav-embedded/src/settings.c
new file mode 100644
index 0000000..297b812
--- /dev/null
+++ b/libdvdnav-embedded/src/settings.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2000 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * 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 libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <inttypes.h>
+#include <limits.h>
+#include <string.h>
+#include <sys/time.h>
+#include "dvdnav/dvdnav.h"
+#include <dvdread/nav_types.h>
+#include "vm/decoder.h"
+#include "vm/vm.h"
+#include "dvdnav_internal.h"
+
+/* Characteristics/setting API calls */
+
+dvdnav_status_t dvdnav_get_region_mask(dvdnav_t *this, int32_t *region) {
+ (*region) = this->vm->state.registers.SPRM[20];
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_set_region_mask(dvdnav_t *this, int32_t mask) {
+ pthread_mutex_lock(&this->vm_lock);
+ this->vm->state.registers.SPRM[20] = (mask & 0xff);
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_set_readahead_flag(dvdnav_t *this, int32_t use_readahead) {
+ this->use_read_ahead = use_readahead;
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_get_readahead_flag(dvdnav_t *this, int32_t *flag) {
+ (*flag) = this->use_read_ahead;
+ return DVDNAV_STATUS_OK;
+}
+
+static dvdnav_status_t set_language_register(dvdnav_t *this, char *code, int reg) {
+ if(!code[0] || !code[1]) {
+ printerr("Passed illegal language code.");
+ return DVDNAV_STATUS_ERR;
+ }
+
+ pthread_mutex_lock(&this->vm_lock);
+ this->vm->state.registers.SPRM[reg] = (code[0] << 8) | code[1];
+ pthread_mutex_unlock(&this->vm_lock);
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_menu_language_select(dvdnav_t *this, char *code) {
+ return set_language_register(this, code, 0);
+}
+
+dvdnav_status_t dvdnav_audio_language_select(dvdnav_t *this, char *code) {
+ return set_language_register(this, code, 16);
+}
+
+dvdnav_status_t dvdnav_spu_language_select(dvdnav_t *this, char *code) {
+ return set_language_register(this, code, 18);
+}
+
+dvdnav_status_t dvdnav_set_PGC_positioning_flag(dvdnav_t *this, int32_t pgc) {
+ this->pgc_based = pgc;
+ return DVDNAV_STATUS_OK;
+}
+
+dvdnav_status_t dvdnav_get_PGC_positioning_flag(dvdnav_t *this, int32_t *flag) {
+ (*flag) = this->pgc_based;
+ return DVDNAV_STATUS_OK;
+}
diff --git a/libdvdnav-embedded/src/vm/decoder.c b/libdvdnav-embedded/src/vm/decoder.c
new file mode 100644
index 0000000..7dd8da2
--- /dev/null
+++ b/libdvdnav-embedded/src/vm/decoder.c
@@ -0,0 +1,783 @@
+/*
+ * Copyright (C) 2000, 2001 Martin Norbäck, Håkan Hjort
+ * 2002-2004 the dvdnav project
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <string.h> /* For memset */
+#include <sys/time.h>
+#include <assert.h>
+
+#include <dvdread/nav_types.h>
+
+#include "dvdnav/dvdnav.h"
+#include "decoder.h"
+#include "vm.h"
+#include "vmcmd.h"
+#include "dvdnav_internal.h"
+
+uint32_t vm_getbits(command_t *command, int32_t start, int32_t count) {
+ uint64_t result = 0;
+ uint64_t bit_mask = 0;
+ uint64_t examining = 0;
+ int32_t bits;
+
+ if (count == 0) return 0;
+
+ if ( ((start - count) < -1) ||
+ (count > 32) ||
+ (start > 63) ||
+ (count < 0) ||
+ (start < 0) ) {
+ fprintf(MSG_OUT, "libdvdnav: Bad call to vm_getbits. Parameter out of range\n");
+ abort();
+ }
+ /* all ones, please */
+ bit_mask = ~bit_mask;
+ bit_mask >>= 63 - start;
+ bits = start + 1 - count;
+ examining = ((bit_mask >> bits) << bits );
+ command->examined |= examining;
+ result = (command->instruction & bit_mask) >> bits;
+ return (uint32_t) result;
+}
+
+static uint16_t get_GPRM(registers_t* registers, uint8_t reg) {
+ if (registers->GPRM_mode[reg] & 0x01) {
+ struct timeval current_time, time_offset;
+ uint16_t result;
+ /* Counter mode */
+ /* fprintf(MSG_OUT, "libdvdnav: Getting counter %d\n",reg);*/
+ gettimeofday(&current_time, NULL);
+ time_offset.tv_sec = current_time.tv_sec - registers->GPRM_time[reg].tv_sec;
+ time_offset.tv_usec = current_time.tv_usec - registers->GPRM_time[reg].tv_usec;
+ if (time_offset.tv_usec < 0) {
+ time_offset.tv_sec--;
+ time_offset.tv_usec += 1000000;
+ }
+ result = (uint16_t) (time_offset.tv_sec & 0xffff);
+ registers->GPRM[reg]=result;
+ return result;
+
+ } else {
+ /* Register mode */
+ return registers->GPRM[reg];
+ }
+
+}
+
+static void set_GPRM(registers_t* registers, uint8_t reg, uint16_t value) {
+ if (registers->GPRM_mode[reg] & 0x01) {
+ struct timeval current_time;
+ /* Counter mode */
+ /* fprintf(MSG_OUT, "libdvdnav: Setting counter %d\n",reg); */
+ gettimeofday(&current_time, NULL);
+ registers->GPRM_time[reg] = current_time;
+ registers->GPRM_time[reg].tv_sec -= value;
+ }
+ registers->GPRM[reg] = value;
+}
+
+/* Eval register code, can either be system or general register.
+ SXXX_XXXX, where S is 1 if it is system register. */
+static uint16_t eval_reg(command_t* command, uint8_t reg) {
+ if(reg & 0x80) {
+ if ((reg & 0x1f) == 20) {
+ fprintf(MSG_OUT, "libdvdnav: Suspected RCE Region Protection!!!\n");
+ }
+ return command->registers->SPRM[reg & 0x1f]; /* FIXME max 24 not 32 */
+ } else {
+ return get_GPRM(command->registers, reg & 0x0f) ;
+ }
+}
+
+#ifdef TRACE
+
+static char *linkcmd2str(link_cmd_t cmd) {
+ switch(cmd) {
+ case LinkNoLink:
+ return "LinkNoLink";
+ case LinkTopC:
+ return "LinkTopC";
+ case LinkNextC:
+ return "LinkNextC";
+ case LinkPrevC:
+ return "LinkPrevC";
+ case LinkTopPG:
+ return "LinkTopPG";
+ case LinkNextPG:
+ return "LinkNextPG";
+ case LinkPrevPG:
+ return "LinkPrevPG";
+ case LinkTopPGC:
+ return "LinkTopPGC";
+ case LinkNextPGC:
+ return "LinkNextPGC";
+ case LinkPrevPGC:
+ return "LinkPrevPGC";
+ case LinkGoUpPGC:
+ return "LinkGoUpPGC";
+ case LinkTailPGC:
+ return "LinkTailPGC";
+ case LinkRSM:
+ return "LinkRSM";
+ case LinkPGCN:
+ return "LinkPGCN";
+ case LinkPTTN:
+ return "LinkPTTN";
+ case LinkPGN:
+ return "LinkPGN";
+ case LinkCN:
+ return "LinkCN";
+ case Exit:
+ return "Exit";
+ case JumpTT:
+ return "JumpTT";
+ case JumpVTS_TT:
+ return "JumpVTS_TT";
+ case JumpVTS_PTT:
+ return "JumpVTS_PTT";
+ case JumpSS_FP:
+ return "JumpSS_FP";
+ case JumpSS_VMGM_MENU:
+ return "JumpSS_VMGM_MENU";
+ case JumpSS_VTSM:
+ return "JumpSS_VTSM";
+ case JumpSS_VMGM_PGC:
+ return "JumpSS_VMGM_PGC";
+ case CallSS_FP:
+ return "CallSS_FP";
+ case CallSS_VMGM_MENU:
+ return "CallSS_VMGM_MENU";
+ case CallSS_VTSM:
+ return "CallSS_VTSM";
+ case CallSS_VMGM_PGC:
+ return "CallSS_VMGM_PGC";
+ case PlayThis:
+ return "PlayThis";
+ }
+ return "*** (bug)";
+}
+
+void vm_print_link(link_t value) {
+ char *cmd = linkcmd2str(value.command);
+
+ switch(value.command) {
+ case LinkNoLink:
+ case LinkTopC:
+ case LinkNextC:
+ case LinkPrevC:
+ case LinkTopPG:
+ case LinkNextPG:
+ case LinkPrevPG:
+ case LinkTopPGC:
+ case LinkNextPGC:
+ case LinkPrevPGC:
+ case LinkGoUpPGC:
+ case LinkTailPGC:
+ case LinkRSM:
+ fprintf(MSG_OUT, "libdvdnav: %s (button %d)\n", cmd, value.data1);
+ break;
+ case LinkPGCN:
+ case JumpTT:
+ case JumpVTS_TT:
+ case JumpSS_VMGM_MENU: /* == 2 -> Title Menu */
+ case JumpSS_VMGM_PGC:
+ fprintf(MSG_OUT, "libdvdnav: %s %d\n", cmd, value.data1);
+ break;
+ case LinkPTTN:
+ case LinkPGN:
+ case LinkCN:
+ fprintf(MSG_OUT, "libdvdnav: %s %d (button %d)\n", cmd, value.data1, value.data2);
+ break;
+ case Exit:
+ case JumpSS_FP:
+ case PlayThis: /* Humm.. should we have this at all.. */
+ fprintf(MSG_OUT, "libdvdnav: %s\n", cmd);
+ break;
+ case JumpVTS_PTT:
+ fprintf(MSG_OUT, "libdvdnav: %s %d:%d\n", cmd, value.data1, value.data2);
+ break;
+ case JumpSS_VTSM:
+ fprintf(MSG_OUT, "libdvdnav: %s vts %d title %d menu %d\n",
+ cmd, value.data1, value.data2, value.data3);
+ break;
+ case CallSS_FP:
+ fprintf(MSG_OUT, "libdvdnav: %s resume cell %d\n", cmd, value.data1);
+ break;
+ case CallSS_VMGM_MENU: /* == 2 -> Title Menu */
+ case CallSS_VTSM:
+ fprintf(MSG_OUT, "libdvdnav: %s %d resume cell %d\n", cmd, value.data1, value.data2);
+ break;
+ case CallSS_VMGM_PGC:
+ fprintf(MSG_OUT, "libdvdnav: %s %d resume cell %d\n", cmd, value.data1, value.data2);
+ break;
+ }
+ }
+
+void vm_print_registers( registers_t *registers ) {
+ int32_t i;
+ fprintf(MSG_OUT, "libdvdnav: # ");
+ for(i = 0; i < 24; i++)
+ fprintf(MSG_OUT, " %2d |", i);
+ fprintf(MSG_OUT, "\nlibdvdnav: SRPMS: ");
+ for(i = 0; i < 24; i++)
+ fprintf(MSG_OUT, "%04x|", registers->SPRM[i]);
+ fprintf(MSG_OUT, "\nlibdvdnav: GRPMS: ");
+ for(i = 0; i < 16; i++)
+ fprintf(MSG_OUT, "%04x|", get_GPRM(registers, i) );
+ fprintf(MSG_OUT, "\nlibdvdnav: Gmode: ");
+ for(i = 0; i < 16; i++)
+ fprintf(MSG_OUT, "%04x|", registers->GPRM_mode[i]);
+ fprintf(MSG_OUT, "\nlibdvdnav: Gtime: ");
+ for(i = 0; i < 16; i++)
+ fprintf(MSG_OUT, "%04lx|", registers->GPRM_time[i].tv_sec & 0xffff);
+ fprintf(MSG_OUT, "\n");
+}
+
+#endif
+
+/* Eval register or immediate data.
+ AAAA_AAAA BBBB_BBBB, if immediate use all 16 bits for data else use
+ lower eight bits for the system or general purpose register. */
+static uint16_t eval_reg_or_data(command_t* command, int32_t imm, int32_t start) {
+ if(imm) { /* immediate */
+ return vm_getbits(command, start, 16);
+ } else {
+ return eval_reg(command, vm_getbits(command, (start - 8), 8));
+ }
+}
+
+/* Eval register or immediate data.
+ xBBB_BBBB, if immediate use all 7 bits for data else use
+ lower four bits for the general purpose register number. */
+/* Evaluates gprm or data depending on bit, data is in byte n */
+static uint16_t eval_reg_or_data_2(command_t* command,
+ int32_t imm, int32_t start) {
+ if(imm) /* immediate */
+ return vm_getbits(command, (start - 1), 7);
+ else
+ return get_GPRM(command->registers, (vm_getbits(command, (start - 4), 4)) );
+}
+
+
+/* Compare data using operation, return result from comparison.
+ Helper function for the different if functions. */
+static int32_t eval_compare(uint8_t operation, uint16_t data1, uint16_t data2) {
+ switch(operation) {
+ case 1:
+ return data1 & data2;
+ case 2:
+ return data1 == data2;
+ case 3:
+ return data1 != data2;
+ case 4:
+ return data1 >= data2;
+ case 5:
+ return data1 > data2;
+ case 6:
+ return data1 <= data2;
+ case 7:
+ return data1 < data2;
+ }
+ fprintf(MSG_OUT, "libdvdnav: eval_compare: Invalid comparison code\n");
+ return 0;
+}
+
+
+/* Evaluate if version 1.
+ Has comparison data in byte 3 and 4-5 (immediate or register) */
+static int32_t eval_if_version_1(command_t* command) {
+ uint8_t op = vm_getbits(command, 54, 3);
+ if(op) {
+ return eval_compare(op, eval_reg(command, vm_getbits(command, 39, 8)),
+ eval_reg_or_data(command, vm_getbits(command, 55, 1), 31));
+ }
+ return 1;
+}
+
+/* Evaluate if version 2.
+ This version only compares register which are in byte 6 and 7 */
+static int32_t eval_if_version_2(command_t* command) {
+ uint8_t op = vm_getbits(command, 54, 3);
+ if(op) {
+ return eval_compare(op, eval_reg(command, vm_getbits(command, 15, 8)),
+ eval_reg(command, vm_getbits(command, 7, 8)));
+ }
+ return 1;
+}
+
+/* Evaluate if version 3.
+ Has comparison data in byte 2 and 6-7 (immediate or register) */
+static int32_t eval_if_version_3(command_t* command) {
+ uint8_t op = vm_getbits(command, 54, 3);
+ if(op) {
+ return eval_compare(op, eval_reg(command, vm_getbits(command, 47, 8)),
+ eval_reg_or_data(command, vm_getbits(command, 55, 1), 15));
+ }
+ return 1;
+}
+
+/* Evaluate if version 4.
+ Has comparison data in byte 1 and 4-5 (immediate or register)
+ The register in byte 1 is only the lowe nibble (4 bits) */
+static int32_t eval_if_version_4(command_t* command) {
+ uint8_t op = vm_getbits(command, 54, 3);
+ if(op) {
+ return eval_compare(op, eval_reg(command, vm_getbits(command, 51, 4)),
+ eval_reg_or_data(command, vm_getbits(command, 55, 1), 31));
+ }
+ return 1;
+}
+
+/* Evaluate special instruction.... returns the new row/line number,
+ 0 if no new row and 256 if Break. */
+static int32_t eval_special_instruction(command_t* command, int32_t cond) {
+ int32_t line, level;
+
+ switch(vm_getbits(command, 51, 4)) {
+ case 0: /* NOP */
+ line = 0;
+ return cond ? line : 0;
+ case 1: /* Goto line */
+ line = vm_getbits(command, 7, 8);
+ return cond ? line : 0;
+ case 2: /* Break */
+ /* max number of rows < 256, so we will end this set */
+ line = 256;
+ return cond ? 256 : 0;
+ case 3: /* Set temporary parental level and goto */
+ line = vm_getbits(command, 7, 8);
+ level = vm_getbits(command, 11, 4);
+ if(cond) {
+ /* This always succeeds now, if we want real parental protection */
+ /* we need to ask the user and have passwords and stuff. */
+ command->registers->SPRM[13] = level;
+ }
+ return cond ? line : 0;
+ }
+ return 0;
+}
+
+/* Evaluate link by subinstruction.
+ Return 1 if link, or 0 if no link
+ Actual link instruction is in return_values parameter */
+static int32_t eval_link_subins(command_t* command, int32_t cond, link_t *return_values) {
+ uint16_t button = vm_getbits(command, 15, 6);
+ uint8_t linkop = vm_getbits(command, 4, 5);
+
+ if(linkop > 0x10)
+ return 0; /* Unknown Link by Sub-Instruction command */
+
+ /* Assumes that the link_cmd_t enum has the same values as the LinkSIns codes */
+ return_values->command = linkop;
+ return_values->data1 = button;
+ return cond;
+}
+
+
+/* Evaluate link instruction.
+ Return 1 if link, or 0 if no link
+ Actual link instruction is in return_values parameter */
+static int32_t eval_link_instruction(command_t* command, int32_t cond, link_t *return_values) {
+ uint8_t op = vm_getbits(command, 51, 4);
+
+ switch(op) {
+ case 1:
+ return eval_link_subins(command, cond, return_values);
+ case 4:
+ return_values->command = LinkPGCN;
+ return_values->data1 = vm_getbits(command, 14, 15);
+ return cond;
+ case 5:
+ return_values->command = LinkPTTN;
+ return_values->data1 = vm_getbits(command, 9, 10);
+ return_values->data2 = vm_getbits(command, 15, 6);
+ return cond;
+ case 6:
+ return_values->command = LinkPGN;
+ return_values->data1 = vm_getbits(command, 6, 7);
+ return_values->data2 = vm_getbits(command, 15, 6);
+ return cond;
+ case 7:
+ return_values->command = LinkCN;
+ return_values->data1 = vm_getbits(command, 7, 8);
+ return_values->data2 = vm_getbits(command, 15, 6);
+ return cond;
+ }
+ return 0;
+}
+
+
+/* Evaluate a jump instruction.
+ returns 1 if jump or 0 if no jump
+ actual jump instruction is in return_values parameter */
+static int32_t eval_jump_instruction(command_t* command, int32_t cond, link_t *return_values) {
+
+ switch(vm_getbits(command, 51, 4)) {
+ case 1:
+ return_values->command = Exit;
+ return cond;
+ case 2:
+ return_values->command = JumpTT;
+ return_values->data1 = vm_getbits(command, 22, 7);
+ return cond;
+ case 3:
+ return_values->command = JumpVTS_TT;
+ return_values->data1 = vm_getbits(command, 22, 7);
+ return cond;
+ case 5:
+ return_values->command = JumpVTS_PTT;
+ return_values->data1 = vm_getbits(command, 22, 7);
+ return_values->data2 = vm_getbits(command, 41, 10);
+ return cond;
+ case 6:
+ switch(vm_getbits(command, 23, 2)) {
+ case 0:
+ return_values->command = JumpSS_FP;
+ return cond;
+ case 1:
+ return_values->command = JumpSS_VMGM_MENU;
+ return_values->data1 = vm_getbits(command, 19, 4);
+ return cond;
+ case 2:
+ return_values->command = JumpSS_VTSM;
+ return_values->data1 = vm_getbits(command, 31, 8);
+ return_values->data2 = vm_getbits(command, 39, 8);
+ return_values->data3 = vm_getbits(command, 19, 4);
+ return cond;
+ case 3:
+ return_values->command = JumpSS_VMGM_PGC;
+ return_values->data1 = vm_getbits(command, 46, 15);
+ return cond;
+ }
+ break;
+ case 8:
+ switch(vm_getbits(command, 23, 2)) {
+ case 0:
+ return_values->command = CallSS_FP;
+ return_values->data1 = vm_getbits(command, 31, 8);
+ return cond;
+ case 1:
+ return_values->command = CallSS_VMGM_MENU;
+ return_values->data1 = vm_getbits(command, 19, 4);
+ return_values->data2 = vm_getbits(command, 31, 8);
+ return cond;
+ case 2:
+ return_values->command = CallSS_VTSM;
+ return_values->data1 = vm_getbits(command, 19, 4);
+ return_values->data2 = vm_getbits(command, 31, 8);
+ return cond;
+ case 3:
+ return_values->command = CallSS_VMGM_PGC;
+ return_values->data1 = vm_getbits(command, 46, 15);
+ return_values->data2 = vm_getbits(command, 31, 8);
+ return cond;
+ }
+ break;
+ }
+ return 0;
+}
+
+/* Evaluate a set system register instruction
+ May contain a link so return the same as eval_link */
+static int32_t eval_system_set(command_t* command, int32_t cond, link_t *return_values) {
+ int32_t i;
+ uint16_t data, data2;
+
+ switch(vm_getbits(command, 59, 4)) {
+ case 1: /* Set system reg 1 &| 2 &| 3 (Audio, Subp. Angle) */
+ for(i = 1; i <= 3; i++) {
+ if(vm_getbits(command, 63 - ((2 + i)*8), 1)) {
+ data = eval_reg_or_data_2(command, vm_getbits(command, 60, 1), (47 - (i*8)));
+ if(cond) {
+ command->registers->SPRM[i] = data;
+ }
+ }
+ }
+ break;
+ case 2: /* Set system reg 9 & 10 (Navigation timer, Title PGC number) */
+ data = eval_reg_or_data(command, vm_getbits(command, 60, 1), 47);
+ data2 = vm_getbits(command, 23, 8); /* ?? size */
+ if(cond) {
+ command->registers->SPRM[9] = data; /* time */
+ command->registers->SPRM[10] = data2; /* pgcN */
+ }
+ break;
+ case 3: /* Mode: Counter / Register + Set */
+ data = eval_reg_or_data(command, vm_getbits(command, 60, 1), 47);
+ data2 = vm_getbits(command, 19, 4);
+ if(vm_getbits(command, 23, 1)) {
+ command->registers->GPRM_mode[data2] |= 1; /* Set bit 0 */
+ } else {
+ command->registers->GPRM_mode[data2] &= ~ 0x01; /* Reset bit 0 */
+ }
+ if(cond) {
+ set_GPRM(command->registers, data2, data);
+ }
+ break;
+ case 6: /* Set system reg 8 (Highlighted button) */
+ data = eval_reg_or_data(command, vm_getbits(command, 60, 1), 31); /* Not system reg!! */
+ if(cond) {
+ command->registers->SPRM[8] = data;
+ }
+ break;
+ }
+ if(vm_getbits(command, 51, 4)) {
+ return eval_link_instruction(command, cond, return_values);
+ }
+ return 0;
+}
+
+
+/* Evaluate set operation
+ Sets the register given to the value indicated by op and data.
+ For the swap case the contents of reg is stored in reg2.
+*/
+static void eval_set_op(command_t* command, int32_t op, int32_t reg, int32_t reg2, int32_t data) {
+ static const int32_t shortmax = 0xffff;
+ int32_t tmp;
+ switch(op) {
+ case 1:
+ set_GPRM(command->registers, reg, data);
+ break;
+ case 2: /* SPECIAL CASE - SWAP! */
+ set_GPRM(command->registers, reg2, get_GPRM(command->registers, reg));
+ set_GPRM(command->registers, reg, data);
+ break;
+ case 3:
+ tmp = get_GPRM(command->registers, reg) + data;
+ if(tmp > shortmax) tmp = shortmax;
+ set_GPRM(command->registers, reg, (uint16_t)tmp);
+ break;
+ case 4:
+ tmp = get_GPRM(command->registers, reg) - data;
+ if(tmp < 0) tmp = 0;
+ set_GPRM(command->registers, reg, (uint16_t)tmp);
+ break;
+ case 5:
+ tmp = get_GPRM(command->registers, reg) * data;
+ if(tmp > shortmax) tmp = shortmax;
+ set_GPRM(command->registers, reg, (uint16_t)tmp);
+ break;
+ case 6:
+ if (data != 0) {
+ set_GPRM(command->registers, reg, (get_GPRM(command->registers, reg) / data) );
+ } else {
+ set_GPRM(command->registers, reg, 0xffff); /* Avoid that divide by zero! */
+ }
+ break;
+ case 7:
+ if (data != 0) {
+ set_GPRM(command->registers, reg, (get_GPRM(command->registers, reg) % data) );
+ } else {
+ set_GPRM(command->registers, reg, 0xffff); /* Avoid that divide by zero! */
+ }
+ break;
+ case 8: /* SPECIAL CASE - RND! Return numbers between 1 and data. */
+ set_GPRM(command->registers, reg, 1 + ((uint16_t) ((float) data * rand()/(RAND_MAX+1.0))) );
+ break;
+ case 9:
+ set_GPRM(command->registers, reg, (get_GPRM(command->registers, reg) & data) );
+ break;
+ case 10:
+ set_GPRM(command->registers, reg, (get_GPRM(command->registers, reg) | data) );
+ break;
+ case 11:
+ set_GPRM(command->registers, reg, (get_GPRM(command->registers, reg) ^ data) );
+ break;
+ }
+}
+
+/* Evaluate set instruction, combined with either Link or Compare. */
+static void eval_set_version_1(command_t* command, int32_t cond) {
+ uint8_t op = vm_getbits(command, 59, 4);
+ uint8_t reg = vm_getbits(command, 35, 4); /* FIXME: This is different from vmcmd.c!!! */
+ uint8_t reg2 = vm_getbits(command, 19, 4);
+ uint16_t data = eval_reg_or_data(command, vm_getbits(command, 60, 1), 31);
+
+ if(cond) {
+ eval_set_op(command, op, reg, reg2, data);
+ }
+}
+
+
+/* Evaluate set instruction, combined with both Link and Compare. */
+static void eval_set_version_2(command_t* command, int32_t cond) {
+ uint8_t op = vm_getbits(command, 59, 4);
+ uint8_t reg = vm_getbits(command, 51, 4);
+ uint8_t reg2 = vm_getbits(command, 35, 4); /* FIXME: This is different from vmcmd.c!!! */
+ uint16_t data = eval_reg_or_data(command, vm_getbits(command, 60, 1), 47);
+
+ if(cond) {
+ eval_set_op(command, op, reg, reg2, data);
+ }
+}
+
+
+/* Evaluate a command
+ returns row number of goto, 0 if no goto, -1 if link.
+ Link command in return_values */
+static int32_t eval_command(const uint8_t *bytes, registers_t* registers, link_t *return_values) {
+ int32_t cond, res = 0;
+ command_t command;
+ command.instruction =( (uint64_t) bytes[0] << 56 ) |
+ ( (uint64_t) bytes[1] << 48 ) |
+ ( (uint64_t) bytes[2] << 40 ) |
+ ( (uint64_t) bytes[3] << 32 ) |
+ ( (uint64_t) bytes[4] << 24 ) |
+ ( (uint64_t) bytes[5] << 16 ) |
+ ( (uint64_t) bytes[6] << 8 ) |
+ (uint64_t) bytes[7] ;
+ command.examined = 0;
+ command.registers = registers;
+ memset(return_values, 0, sizeof(link_t));
+
+ switch(vm_getbits(&command, 63, 3)) { /* three first old_bits */
+ case 0: /* Special instructions */
+ cond = eval_if_version_1(&command);
+ res = eval_special_instruction(&command, cond);
+ if(res == -1) {
+ fprintf(MSG_OUT, "libdvdnav: Unknown Instruction!\n");
+ assert(0);
+ }
+ break;
+ case 1: /* Link/jump instructions */
+ if(vm_getbits(&command, 60, 1)) {
+ cond = eval_if_version_2(&command);
+ res = eval_jump_instruction(&command, cond, return_values);
+ } else {
+ cond = eval_if_version_1(&command);
+ res = eval_link_instruction(&command, cond, return_values);
+ }
+ if(res)
+ res = -1;
+ break;
+ case 2: /* System set instructions */
+ cond = eval_if_version_2(&command);
+ res = eval_system_set(&command, cond, return_values);
+ if(res)
+ res = -1;
+ break;
+ case 3: /* Set instructions, either Compare or Link may be used */
+ cond = eval_if_version_3(&command);
+ eval_set_version_1(&command, cond);
+ if(vm_getbits(&command, 51, 4)) {
+ res = eval_link_instruction(&command, cond, return_values);
+ }
+ if(res)
+ res = -1;
+ break;
+ case 4: /* Set, Compare -> Link Sub-Instruction */
+ eval_set_version_2(&command, /*True*/ 1);
+ cond = eval_if_version_4(&command);
+ res = eval_link_subins(&command, cond, return_values);
+ if(res)
+ res = -1;
+ break;
+ case 5: /* Compare -> (Set and Link Sub-Instruction) */
+ /* FIXME: These are wrong. Need to be updated from vmcmd.c */
+ cond = eval_if_version_4(&command);
+ eval_set_version_2(&command, cond);
+ res = eval_link_subins(&command, cond, return_values);
+ if(res)
+ res = -1;
+ break;
+ case 6: /* Compare -> Set, always Link Sub-Instruction */
+ /* FIXME: These are wrong. Need to be updated from vmcmd.c */
+ cond = eval_if_version_4(&command);
+ eval_set_version_2(&command, cond);
+ res = eval_link_subins(&command, /*True*/ 1, return_values);
+ if(res)
+ res = -1;
+ break;
+ default: /* Unknown command type */
+ fprintf(MSG_OUT, "libdvdnav: WARNING: Unknown Command Type=%x\n", vm_getbits(&command, 63, 3));
+ }
+ /* Check if there are bits not yet examined */
+
+ if(command.instruction & ~ command.examined) {
+ fprintf(MSG_OUT, "libdvdnav: decoder.c: [WARNING, unknown bits:");
+ fprintf(MSG_OUT, " %08"PRIx64, (command.instruction & ~ command.examined) );
+ fprintf(MSG_OUT, "]\n");
+ }
+
+ return res;
+}
+
+/* Evaluate a set of commands in the given register set (which is modified) */
+int32_t vmEval_CMD(const vm_cmd_t commands[], int32_t num_commands,
+ registers_t *registers, link_t *return_values) {
+ int32_t i = 0;
+ int32_t total = 0;
+
+#ifdef TRACE
+ /* DEBUG */
+ fprintf(MSG_OUT, "libdvdnav: Registers before transaction\n");
+ vm_print_registers( registers );
+ fprintf(MSG_OUT, "libdvdnav: Full list of commands to execute\n");
+ for(i = 0; i < num_commands; i++)
+ vm_print_cmd(i, &commands[i]);
+ 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;
+
+#ifdef TRACE
+ vm_print_cmd(i, &commands[i]);
+#endif
+
+ line = eval_command(&commands[i].bytes[0], registers, return_values);
+
+ if (line < 0) { /* Link command */
+#ifdef TRACE
+ fprintf(MSG_OUT, "libdvdnav: Registers after transaction\n");
+ vm_print_registers( registers );
+ fprintf(MSG_OUT, "libdvdnav: eval: Doing Link/Jump/Call\n");
+#endif
+ return 1;
+ }
+
+ if (line > 0) /* Goto command */
+ i = line - 1;
+ else /* Just continue on the next line */
+ i++;
+
+ total++;
+ }
+
+ memset(return_values, 0, sizeof(link_t));
+#ifdef TRACE
+ fprintf(MSG_OUT, "libdvdnav: Registers after transaction\n");
+ vm_print_registers( registers );
+#endif
+ return 0;
+}
+
diff --git a/libdvdnav-embedded/src/vm/decoder.h b/libdvdnav-embedded/src/vm/decoder.h
new file mode 100644
index 0000000..82cd6ed
--- /dev/null
+++ b/libdvdnav-embedded/src/vm/decoder.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2000, 2001 Martin Norbäck, Håkan Hjort
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef LIBDVDNAV_DECODER_H
+#define LIBDVDNAV_DECODER_H
+
+#include <sys/time.h>
+
+/* link command types */
+typedef enum {
+ LinkNoLink = 0,
+
+ LinkTopC = 1,
+ LinkNextC = 2,
+ LinkPrevC = 3,
+
+ LinkTopPG = 5,
+ LinkNextPG = 6,
+ LinkPrevPG = 7,
+
+ LinkTopPGC = 9,
+ LinkNextPGC = 10,
+ LinkPrevPGC = 11,
+ LinkGoUpPGC = 12,
+ LinkTailPGC = 13,
+
+ LinkRSM = 16,
+
+ LinkPGCN,
+ LinkPTTN,
+ LinkPGN,
+ LinkCN,
+
+ Exit,
+
+ JumpTT, /* 22 */
+ JumpVTS_TT,
+ JumpVTS_PTT,
+
+ JumpSS_FP,
+ JumpSS_VMGM_MENU,
+ JumpSS_VTSM,
+ JumpSS_VMGM_PGC,
+
+ CallSS_FP, /* 29 */
+ CallSS_VMGM_MENU,
+ CallSS_VTSM,
+ CallSS_VMGM_PGC,
+
+ PlayThis
+} link_cmd_t;
+
+/* a link's data set */
+typedef struct {
+ link_cmd_t command;
+ uint16_t data1;
+ uint16_t data2;
+ uint16_t data3;
+} link_t;
+
+/* the VM registers */
+typedef struct {
+ uint16_t SPRM[24];
+ uint16_t GPRM[16];
+ uint8_t GPRM_mode[16]; /* Need to have some thing to indicate normal/counter mode for every GPRM */
+ struct timeval GPRM_time[16]; /* For counter mode */
+} registers_t;
+
+/* 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 */
+int32_t vmEval_CMD(const vm_cmd_t commands[], int32_t num_commands,
+ registers_t *registers, link_t *return_values);
+
+/* extracts some bits from the command */
+uint32_t vm_getbits(command_t* command, int32_t start, int32_t count);
+
+#ifdef TRACE
+/* for debugging: prints a link in readable form */
+void vm_print_link(link_t value);
+
+/* for debugging: dumps VM registers */
+void vm_print_registers( registers_t *registers );
+#endif
+
+#endif /* LIBDVDNAV_DECODER_H */
diff --git a/libdvdnav-embedded/src/vm/getset.c b/libdvdnav-embedded/src/vm/getset.c
new file mode 100644
index 0000000..4132b1c
--- /dev/null
+++ b/libdvdnav-embedded/src/vm/getset.c
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2000, 2001 Håkan Hjort
+ * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+ * 2002-2004 the dvdnav project
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <inttypes.h>
+
+#include <dvdread/nav_types.h>
+#include <dvdread/ifo_read.h>
+#include "dvdnav/dvdnav.h"
+
+#include "decoder.h"
+#include "vm.h"
+#include "getset.h"
+#include "dvdnav_internal.h"
+#include "logger.h"
+
+#include "getset.h"
+/* Set functions */
+
+int set_TT(vm_t *vm, int tt) {
+ return set_PTT(vm, tt, 1);
+}
+
+int set_PTT(vm_t *vm, int tt, int ptt) {
+ assert(tt <= vm->vmgi->tt_srpt->nr_of_srpts);
+ return set_VTS_PTT(vm, vm->vmgi->tt_srpt->title[tt - 1].title_set_nr,
+ vm->vmgi->tt_srpt->title[tt - 1].vts_ttn, ptt);
+}
+
+int set_VTS_TT(vm_t *vm, int vtsN, int vts_ttn) {
+ return set_VTS_PTT(vm, vtsN, vts_ttn, 1);
+}
+
+int set_VTS_PTT(vm_t *vm, int vtsN, int vts_ttn, int part) {
+ int pgcN, pgN, res;
+
+ (vm->state).domain = DVD_DOMAIN_VTSTitle;
+
+ if (vtsN != (vm->state).vtsN)
+ if (!ifoOpenNewVTSI(vm, vm->dvd, vtsN)) /* Also sets (vm->state).vtsN */
+ return 0;
+
+ 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 = part;
+ (vm->state).TTN_REG = get_TT(vm, vtsN, vts_ttn);
+ if( (vm->state.TTN_REG) == 0 )
+ return 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;
+}
+
+int set_PROG(vm_t *vm, int tt, int pgcn, int pgn) {
+ assert(tt <= vm->vmgi->tt_srpt->nr_of_srpts);
+ return set_VTS_PROG(vm, vm->vmgi->tt_srpt->title[tt - 1].title_set_nr,
+ vm->vmgi->tt_srpt->title[tt - 1].vts_ttn, pgcn, pgn);
+}
+
+int set_VTS_PROG(vm_t *vm, int vtsN, int vts_ttn, int pgcn, int pgn) {
+ int pgcN, pgN, res, title, part = 0;
+
+ (vm->state).domain = DVD_DOMAIN_VTSTitle;
+
+ if (vtsN != (vm->state).vtsN)
+ if (!ifoOpenNewVTSI(vm, vm->dvd, vtsN)) /* Also sets (vm->state).vtsN */
+ return 0;
+
+ if ((vts_ttn < 1) || (vts_ttn > vm->vtsi->vts_ptt_srpt->nr_of_srpts)) {
+ return 0;
+ }
+
+ pgcN = pgcn;
+ pgN = pgn;
+
+ (vm->state).TT_PGCN_REG = pgcN;
+ (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;
+ vm_get_current_title_part(vm, &title, &part);
+ (vm->state).PTTN_REG = part;
+ return res;
+}
+
+int set_FP_PGC(vm_t *vm) {
+ if (!vm || !vm->vmgi)
+ return 1;
+ (vm->state).domain = DVD_DOMAIN_FirstPlay;
+ if (!vm->vmgi->first_play_pgc) {
+ return set_PGCN(vm, 1);
+ }
+ (vm->state).pgc = vm->vmgi->first_play_pgc;
+ (vm->state).pgcN = vm->vmgi->vmgi_mat->first_play_pgc;
+ return 1;
+}
+
+
+int set_MENU(vm_t *vm, int menu) {
+ assert((vm->state).domain == DVD_DOMAIN_VMGM || (vm->state).domain == DVD_DOMAIN_VTSMenu);
+ return set_PGCN(vm, get_ID(vm, menu));
+}
+
+int set_PGCN(vm_t *vm, int pgcN) {
+ const pgcit_t *pgcit;
+
+ pgcit = get_PGCIT(vm);
+ if (pgcit == NULL)
+ return 0;
+
+ if(pgcN < 1 || pgcN > pgcit->nr_of_pgci_srp) {
+#ifdef TRACE
+ Log3(vm, "** No such pgcN = %d", pgcN);
+#endif
+ return 0;
+ }
+
+ (vm->state).pgc = pgcit->pgci_srp[pgcN - 1].pgc;
+ (vm->state).pgcN = pgcN;
+ (vm->state).pgN = 1;
+
+ if((vm->state).domain == DVD_DOMAIN_VTSTitle)
+ (vm->state).TT_PGCN_REG = pgcN;
+
+ return 1;
+}
+
+/* Figure out the correct pgN from the cell and update (vm->state). */
+int set_PGN(vm_t *vm) {
+ int new_pgN = 0;
+ int dummy, part = 0;
+
+ if ((vm->state).pgc == NULL)
+ return 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 == DVD_DOMAIN_VTSTitle) {
+ if((vm->state).TTN_REG > vm->vmgi->tt_srpt->nr_of_srpts)
+ return 0; /* ?? */
+
+ vm_get_current_title_part(vm, &dummy, &part);
+ (vm->state).PTTN_REG = part;
+ }
+ return 1;
+}
+
+/* Must be called before domain is changed (set_PGCN()) */
+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 DVD_DOMAIN_VTSTitle */
+
+ for(i = 0; i < 5; i++) {
+ (vm->state).rsm_regs[i] = (vm->state).registers.SPRM[4 + i];
+ }
+}
+
+
+/* Force the highlight button number register */
+void set_HL_BTN(vm_t *vm, int btnn)
+{
+ (vm->state).HL_BTNN_REG = btnn << 10;
+}
+
+/* Get functions */
+
+/* Searches the TT tables, to find the current TT.
+ * returns the current TT.
+ * returns 0 if not found.
+ */
+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.
+ */
+int get_ID(vm_t *vm, int id) {
+ int pgcN, i;
+ const pgcit_t *pgcit;
+
+ /* Relies on state to get the correct pgcit. */
+ pgcit = get_PGCIT(vm);
+ if(pgcit == NULL) {
+ Log1(vm, "PGCIT null!");
+ return 0;
+ }
+#ifdef TRACE
+ Log3(vm, "** Searching for menu (0x%x) entry PGC", 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
+ Log3(vm, "Found menu.");
+#endif
+ return pgcN;
+ }
+ }
+#ifdef TRACE
+ Log3(vm, "** No such id/menu (0x%02x) entry PGC", id & 0x7f);
+ for(i = 0; i < pgcit->nr_of_pgci_srp; i++) {
+ if ( (pgcit->pgci_srp[i].entry_id & 0x80) == 0x80) {
+ Log3(vm, "Available menus: 0x%x",
+ pgcit->pgci_srp[i].entry_id & 0x7f);
+ }
+ }
+#endif
+ return 0; /* error */
+}
+
+/* FIXME: we have a pgcN member in the vm's state now, so this should be obsolete */
+int get_PGCN(vm_t *vm) {
+ const pgcit_t *pgcit;
+ int pgcN = 1;
+
+ if ((vm->state).pgc == NULL) {
+ return 0; /* error */
+ }
+
+ pgcit = get_PGCIT(vm);
+
+ if (pgcit) {
+ while(pgcN <= pgcit->nr_of_pgci_srp) {
+ if(pgcit->pgci_srp[pgcN - 1].pgc == (vm->state).pgc) {
+ assert((vm->state).pgcN == pgcN);
+ return pgcN;
+ }
+ pgcN++;
+ }
+ }
+ Log0(vm, "get_PGCN failed. Was trying to find pgcN in domain %d",
+ (vm->state).domain);
+ return 0; /* error */
+}
+
+const pgcit_t* get_MENU_PGCIT(vm_t *vm, ifo_handle_t *h, uint16_t lang) {
+ int i;
+
+ if(h == NULL || h->pgci_ut == NULL) {
+ Log0(vm, "*** pgci_ut handle is NULL ***");
+ return NULL; /* error? */
+ }
+
+ i = 0;
+ while(i < h->pgci_ut->nr_of_lus
+ && h->pgci_ut->lu[i].lang_code != lang)
+ i++;
+ if(i == h->pgci_ut->nr_of_lus) {
+ Log1(vm, "Language '%c%c' not found, using '%c%c' instead",
+ (char)(lang >> 8), (char)(lang & 0xff),
+ (char)(h->pgci_ut->lu[0].lang_code >> 8),
+ (char)(h->pgci_ut->lu[0].lang_code & 0xff));
+ char *buffer = malloc(3 * h->pgci_ut->nr_of_lus + 1);
+ if(buffer)
+ {
+ buffer[3 * h->pgci_ut->nr_of_lus] = 0;
+ for(i = 0; i < h->pgci_ut->nr_of_lus; i++) {
+ sprintf(&buffer[3*i], "%c%c ",
+ (char)(h->pgci_ut->lu[i].lang_code >> 8),
+ (char)(h->pgci_ut->lu[i].lang_code & 0xff));
+ }
+ Log2(vm, "Menu Languages available: %s", buffer);
+ free(buffer);
+ }
+ i = 0; /* error? */
+ }
+
+ return h->pgci_ut->lu[i].pgcit;
+}
+
+/* Uses state to decide what to return */
+const pgcit_t* get_PGCIT(vm_t *vm) {
+ const pgcit_t *pgcit = NULL;
+
+ switch ((vm->state).domain) {
+ case DVD_DOMAIN_VTSTitle:
+ if(!vm->vtsi) return NULL;
+ pgcit = vm->vtsi->vts_pgcit;
+ break;
+ case DVD_DOMAIN_VTSMenu:
+ if(!vm->vtsi) return NULL;
+ pgcit = get_MENU_PGCIT(vm, vm->vtsi, (vm->state).registers.SPRM[0]);
+ break;
+ case DVD_DOMAIN_VMGM:
+ case DVD_DOMAIN_FirstPlay:
+ pgcit = get_MENU_PGCIT(vm, vm->vmgi, (vm->state).registers.SPRM[0]);
+ break;
+ default:
+ abort();
+ }
+
+ return pgcit;
+}
+
diff --git a/libdvdnav-embedded/src/vm/getset.h b/libdvdnav-embedded/src/vm/getset.h
new file mode 100644
index 0000000..5e80ffe
--- /dev/null
+++ b/libdvdnav-embedded/src/vm/getset.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2000, 2001 Håkan Hjort
+ * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+ * 2002-2004 the dvdnav project
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* get_XYZ returns a value.
+ * set_XYZ sets state using passed parameters.
+ * returns success/failure.
+ */
+
+/* Set */
+int set_TT(vm_t *vm, int tt);
+int set_PTT(vm_t *vm, int tt, int ptt);
+int set_VTS_TT(vm_t *vm, int vtsN, int vts_ttn);
+int set_VTS_PTT(vm_t *vm, int vtsN, int vts_ttn, int part);
+int set_PROG(vm_t *vm, int tt, int pgcn, int pgn);
+int set_VTS_PROG(vm_t *vm, int vtsN, int vts_ttn, int pgcn, int pgn);
+int set_FP_PGC(vm_t *vm);
+int set_MENU(vm_t *vm, int menu);
+int set_PGCN(vm_t *vm, int pgcN);
+int set_PGN(vm_t *vm); /* Set PGN based on (vm->state).CellN */
+void set_RSMinfo(vm_t *vm, int cellN, int blockN);
+void set_HL_BTN(vm_t *vm, int btnn);
+
+/* Get */
+int get_TT(vm_t *vm, int vtsN, int vts_ttn);
+int get_ID(vm_t *vm, int id);
+int get_PGCN(vm_t *vm);
+
+const pgcit_t* get_MENU_PGCIT(vm_t *vm, ifo_handle_t *h, uint16_t lang);
+const pgcit_t* get_PGCIT(vm_t *vm);
+
diff --git a/libdvdnav-embedded/src/vm/play.c b/libdvdnav-embedded/src/vm/play.c
new file mode 100644
index 0000000..8830883
--- /dev/null
+++ b/libdvdnav-embedded/src/vm/play.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2000, 2001 Håkan Hjort
+ * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+ * 2002-2004 the dvdnav project
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <dvdread/nav_types.h>
+#include <dvdread/ifo_read.h>
+#include "dvdnav/dvdnav.h"
+
+#include "decoder.h"
+#include "vm.h"
+#include "play.h"
+#include "vm/getset.h"
+
+#include "dvdnav_internal.h"
+#include "logger.h"
+
+/* Playback control */
+
+link_t play_PGC(vm_t *vm) {
+ link_t link_values;
+
+#ifdef TRACE
+ if((vm->state).domain != DVD_DOMAIN_FirstPlay) {
+ Log3(vm, "play_PGC: (vm->state).pgcN (%i)", get_PGCN(vm));
+ } else {
+ Log3(vm, "play_PGC: first_play_pgc");
+ }
+#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;
+
+ /* Handle random playback mode by choosing our initial program
+ * number randomly - some discs end up jumping to nowhere if you
+ * always choose the first cell.
+ */
+ if ((vm->state).pgc->pg_playback_mode!=0 &&
+ ((vm->state).pgc->pg_playback_mode & 0x80)==0) {
+ int pgCnt = ((vm->state).pgc->pg_playback_mode & 0x7f) + 1;
+ if (pgCnt > (vm->state).pgc->nr_of_programs) {
+ pgCnt = (vm->state).pgc->nr_of_programs;
+ }
+ if (pgCnt>1) {
+ (vm->state).pgN = 1 + ((int) ((float) pgCnt * rand()/(RAND_MAX+1.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
+ Log3(vm, "PGC pre commands didn't do a Jump, Link or Call");
+#endif
+ }
+ }
+ return play_PG(vm);
+}
+
+link_t play_PGC_PG(vm_t *vm, int pgN) {
+ link_t link_values;
+
+#ifdef TRACE
+ if((vm->state).domain != DVD_DOMAIN_FirstPlay) {
+ Log3(vm, "play_PGC_PG: (vm->state).pgcN (%i)", get_PGCN(vm));
+ } else {
+ Log3(vm, "play_PGC_PG: first_play_pgc");
+ }
+#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
+ Log3(vm, "PGC pre commands didn't do a Jump, Link or Call");
+#endif
+ }
+ }
+ return play_PG(vm);
+}
+
+link_t play_PGC_post(vm_t *vm) {
+ link_t link_values = { LinkNoLink, 0, 0, 0 };
+
+#ifdef TRACE
+ Log3(vm, "play_PGC_post:");
+#endif
+
+ /* 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 && (vm->state).pgc->command_tbl->nr_of_post &&
+ 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
+ Log3(vm, "** Fell of the end of the pgc, continuing in NextPGC");
+#endif
+ /* Should end up in the STOP_DOMAIN if next_pgc is 0. */
+ if(!set_PGCN(vm, (vm->state).pgc->next_pgc_nr)) {
+ link_values.command = Exit;
+ return link_values;
+ }
+ return play_PGC(vm);
+}
+
+link_t play_PG(vm_t *vm) {
+#ifdef TRACE
+ Log3(vm, "play_PG: (vm->state).pgN (%i)", (vm->state).pgN);
+#endif
+
+ assert((vm->state).pgN > 0);
+ if((vm->state).pgN > (vm->state).pgc->nr_of_programs) {
+#ifdef TRACE
+ Log3(vm, "play_PG: (vm->state).pgN (%i) > pgc->nr_of_programs (%i)",
+ (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);
+}
+
+link_t play_Cell(vm_t *vm) {
+ static const link_t play_this = {PlayThis, /* Block in Cell */ 0, 0, 0};
+
+#ifdef TRACE
+ Log3(vm, "play_Cell: (vm->state).cellN (%i)", (vm->state).cellN);
+#endif
+
+ assert((vm->state).cellN > 0);
+ if((vm->state).cellN > (vm->state).pgc->nr_of_cells) {
+#ifdef TRACE
+ Log3(vm, "(vm->state).cellN (%i) > pgc->nr_of_cells (%i)",
+ (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);
+ break;
+ 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 DVDNAV_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)) {
+ Log1(vm, "Invalid angle block");
+ (vm->state).cellN -= (vm->state).AGL_REG - 1;
+ }
+#endif
+ break;
+ case 2: /* reserved */
+ case 3: /* reserved */
+ default:
+ Log1(vm, "Invalid? Cell block_mode (%d), block_type (%d)",
+ (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:
+ Log1(vm, "Cell is in block but did not enter at first cell!");
+ }
+
+ /* 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
+ Log3(vm, "Cell should restart here");
+#endif
+ return play_this;
+}
+
+link_t play_Cell_post(vm_t *vm) {
+ const cell_playback_t *cell;
+
+#ifdef TRACE
+ Log3(vm, "play_Cell_post: (vm->state).cellN (%i)", (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;
+
+ if ((vm->state).pgc->command_tbl != NULL &&
+ (vm->state).pgc->command_tbl->nr_of_cell >= cell->cell_cmd_nr) {
+#ifdef TRACE
+ Log3(vm, "Cell command present, executing");
+#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
+ Log3(vm, "Cell command didn't do a Jump, Link or Call");
+#endif
+ }
+ } else {
+#ifdef TRACE
+ Log3(vm, "Invalid Cell command");
+#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);
+ break;
+ 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: /* reserved */
+ case 3: /* reserved */
+ default:
+ Log1(vm, "Invalid? Cell block_mode (%d), block_type (%d)",
+ (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
+ Log3(vm, "last cell in this PGC");
+#endif
+ return play_PGC_post(vm);
+ }
+ return play_Cell(vm);
+}
diff --git a/libdvdnav-embedded/src/vm/play.h b/libdvdnav-embedded/src/vm/play.h
new file mode 100644
index 0000000..7d76471
--- /dev/null
+++ b/libdvdnav-embedded/src/vm/play.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2000, 2001 Håkan Hjort
+ * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+ * 2002-2004 the dvdnav project
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* Play */
+link_t play_PGC(vm_t *vm);
+link_t play_PGC_PG(vm_t *vm, int pgN);
+link_t play_PGC_post(vm_t *vm);
+link_t play_PG(vm_t *vm);
+link_t play_Cell(vm_t *vm);
+link_t play_Cell_post(vm_t *vm);
+
diff --git a/libdvdnav-embedded/src/vm/vm.c b/libdvdnav-embedded/src/vm/vm.c
new file mode 100644
index 0000000..9276c91
--- /dev/null
+++ b/libdvdnav-embedded/src/vm/vm.c
@@ -0,0 +1,1177 @@
+/*
+ * Copyright (C) 2000, 2001 Håkan Hjort
+ * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+ * 2002-2004 the dvdnav project
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include <dvdread/nav_types.h>
+#include <dvdread/ifo_read.h>
+#include "dvdnav/dvdnav.h"
+
+#include "decoder.h"
+#include "vm.h"
+#include "play.h"
+#include "getset.h"
+#include "dvdnav_internal.h"
+#include "logger.h"
+
+#ifdef _MSC_VER
+#include <io.h> /* read() */
+#endif /* _MSC_VER */
+
+#ifdef __OS2__
+#define INCL_DOS
+#include <os2safe.h>
+#include <os2.h>
+#include <io.h> /* setmode() */
+#include <fcntl.h> /* O_BINARY */
+#endif
+
+#if DVDREAD_VERSION >= DVDREAD_VERSION_CODE(6,1,0)
+static void dvd_reader_logger_handler( void *priv, dvd_logger_level_t level,
+ const char *fmt, va_list list )
+{
+ vm_t *vm = priv;
+ if(vm->logcb.pf_log)
+ vm->logcb.pf_log(vm->priv, (dvdnav_logger_level_t) level, fmt, list );
+}
+#endif
+
+static int dvd_reader_seek_handler(void *priv, uint64_t pos)
+{
+ vm_t *vm = priv;
+ if(vm->streamcb.pf_seek)
+ return vm->streamcb.pf_seek(vm->priv, pos);
+ return 1;
+}
+
+static int dvd_reader_read_handler(void *priv, void *buffer, int count)
+{
+ vm_t *vm = priv;
+ if(vm->streamcb.pf_read)
+ return vm->streamcb.pf_read(vm->priv, buffer, count);
+ return 1;
+}
+
+static int dvd_reader_readv_handler(void *priv, void *iovec, int count)
+{
+ vm_t *vm = priv;
+ if(vm->streamcb.pf_readv)
+ return vm->streamcb.pf_readv(vm->priv, iovec, count);
+ return 1;
+}
+
+/*
+#define DVDNAV_STRICT
+*/
+
+/* Local prototypes */
+
+/* Process link - returns 1 if a hop has been performed */
+static int process_command(vm_t *vm,link_t link_values);
+
+/* Helper functions */
+static void vm_close(vm_t *vm);
+
+/* Debug functions */
+
+#ifdef TRACE
+void vm_position_print(vm_t *vm, vm_position_t *position) {
+ Log3(vm, "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",
+ 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);
+}
+
+static void vm_print_current_domain_state(vm_t *vm) {
+ const char *domain;
+
+ switch(vm->state.domain) {
+ case DVD_DOMAIN_VTSTitle: domain = "Video Title"; break;
+ case DVD_DOMAIN_VTSMenu: domain = "Video Title Menu"; break;
+ case DVD_DOMAIN_VMGM: domain = "Video Manager Menu"; break;
+ case DVD_DOMAIN_FirstPlay: domain = "First Play"; break;
+ default: domain = "Unknown"; break;
+ }
+ Log3(vm, "%s Domain: VTS:%d PGC:%d PG:%u CELL:%u BLOCK:%u VTS_TTN:%u TTN:%u TT_PGCN:%u",
+ domain,
+ vm->state.vtsN,
+ get_PGCN(vm),
+ vm->state.pgN,
+ vm->state.cellN,
+ vm->state.blockN,
+ vm->state.VTS_TTN_REG,
+ vm->state.TTN_REG,
+ vm->state.TT_PGCN_REG);
+}
+#endif
+
+#ifdef __OS2__
+#define open os2_open
+
+static int os2_open(const char *name, int oflag)
+{
+ HFILE hfile;
+ ULONG ulAction;
+ ULONG rc;
+
+ rc = DosOpenL(name, &hfile, &ulAction, 0, FILE_NORMAL,
+ OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
+ OPEN_ACCESS_READONLY | OPEN_SHARE_DENYNONE | OPEN_FLAGS_DASD,
+ NULL);
+
+ if(rc)
+ return -1;
+
+ setmode(hfile, O_BINARY);
+
+ return (int)hfile;
+}
+#endif
+
+static void escaped_strcpy(char *dst, const char *src, size_t len)
+{
+ for(size_t i=0; i < len; i++ )
+ {
+ if(src[i] == 0)
+ {
+ dst[i] = 0;
+ break;
+ }
+ else if(isprint(src[i]))
+ {
+ dst[i] = src[i];
+ } else {
+ dst[i] = ' ';
+ }
+ }
+}
+
+static int dvd_read_name(const vm_t *vm, char *name, char *serial, const char *device) {
+ /* Because we are compiling with _FILE_OFFSET_BITS=64
+ * all off_t are 64bit.
+ */
+ off_t off;
+ ssize_t read_size = 0;
+ int fd = -1;
+ uint8_t data[DVD_VIDEO_LB_LEN];
+
+ /* Read DVD name */
+ if (device == NULL) {
+ Log0(vm, "Device name string NULL");
+ goto fail;
+ }
+ if ((fd = open(device, O_RDONLY)) == -1) {
+ Log0(vm, "Unable to open device file %s.", device);
+ goto fail;
+ }
+
+ if ((off = lseek( fd, 32 * (off_t) DVD_VIDEO_LB_LEN, SEEK_SET )) == (off_t) - 1) {
+ Log0(vm, "Unable to seek to the title block %u.", 32);
+ goto fail;
+ }
+
+ if( off != ( 32 * (off_t) DVD_VIDEO_LB_LEN ) ) {
+ Log0(vm, "Can't seek to block %u", 32 );
+ goto fail;
+ }
+
+ if ((read_size = read( fd, data, DVD_VIDEO_LB_LEN )) == -1) {
+ Log0(vm, "Can't read name block. Probably not a DVD-ROM device.");
+ goto fail;
+ }
+
+ close(fd);
+ fd = -1;
+ if (read_size != DVD_VIDEO_LB_LEN) {
+ Log0(vm, "Can't read name block. Probably not a DVD-ROM device.");
+ goto fail;
+ }
+
+ char buffer[49] = {0};
+ strncpy(name, (const char*) &data[25], 48);
+ name[48] = 0;
+ escaped_strcpy(buffer, name, 48);
+ Log2(vm, "DVD Title: %s", buffer);
+
+ strncpy(serial, (const char*) &data[73], 14);
+ serial[14] = 0;
+ escaped_strcpy(buffer, serial, 14);
+ Log2(vm, "DVD Serial Number: %s", buffer);
+
+ escaped_strcpy(buffer, (const char *) &data[89], 128 - 89);
+ Log2(vm, "DVD Title (Alternative): %s", buffer);
+
+ return 1;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+
+ return 0;
+}
+
+int ifoOpenNewVTSI(vm_t *vm, dvd_reader_t *dvd, int vtsN) {
+ if(vm->state.vtsN == vtsN) {
+ return 1; /* We already have it */
+ }
+
+ if(vm->vtsi != NULL)
+ ifoClose(vm->vtsi);
+
+ vm->vtsi = ifoOpenVTSI(dvd, vtsN);
+ if(vm->vtsi == NULL) {
+ Log0(vm, "ifoOpenVTSI failed");
+ return 0;
+ }
+ if(!ifoRead_VTS_PTT_SRPT(vm->vtsi)) {
+ Log0(vm, "ifoRead_VTS_PTT_SRPT failed");
+ return 0;
+ }
+ if(!ifoRead_PGCIT(vm->vtsi)) {
+ Log0(vm, "ifoRead_PGCIT failed");
+ return 0;
+ }
+ if(!ifoRead_PGCI_UT(vm->vtsi)) {
+ Log0(vm, "ifoRead_PGCI_UT failed");
+ return 0;
+ }
+ if(!ifoRead_VOBU_ADMAP(vm->vtsi)) {
+ Log0(vm, "ifoRead_VOBU_ADMAP vtsi failed");
+ return 0;
+ }
+ if(!ifoRead_TITLE_VOBU_ADMAP(vm->vtsi)) {
+ Log0(vm, "ifoRead_TITLE_VOBU_ADMAP vtsi failed");
+ return 0;
+ }
+ vm->state.vtsN = vtsN;
+
+ return 1;
+}
+
+
+/* Initialisation & Destruction */
+
+vm_t* vm_new_vm(void *priv, const dvdnav_logger_cb *logcb) {
+ vm_t *vm = calloc(1, sizeof(vm_t));
+ if(vm)
+ {
+ vm->priv = priv;
+ if(logcb)
+ vm->logcb = *logcb;
+ }
+ return vm;
+}
+
+void vm_free_vm(vm_t *vm) {
+ vm_close(vm);
+ free(vm);
+}
+
+
+/* IFO Access */
+
+ifo_handle_t *vm_get_vmgi(vm_t *vm) {
+ return vm->vmgi;
+}
+
+ifo_handle_t *vm_get_vtsi(vm_t *vm) {
+ return vm->vtsi;
+}
+
+
+/* Reader Access */
+
+dvd_reader_t *vm_get_dvd_reader(vm_t *vm) {
+ return vm->dvd;
+}
+
+
+/* Basic Handling */
+
+int vm_start(vm_t *vm) {
+ if (vm->stopped) {
+ if (!vm_reset(vm, NULL, NULL, NULL))
+ return 0;
+
+ vm->stopped = 0;
+ }
+ /* Set pgc to FP (First Play) pgc */
+ set_FP_PGC(vm);
+ process_command(vm, play_PGC(vm));
+ return !vm->stopped;
+}
+
+void vm_stop(vm_t *vm) {
+ vm->stopped = 1;
+}
+
+static void vm_close(vm_t *vm) {
+ if(!vm)
+ return;
+ if(vm->vmgi) {
+ ifoClose(vm->vmgi);
+ vm->vmgi=NULL;
+ }
+ if(vm->vtsi) {
+ ifoClose(vm->vtsi);
+ vm->vtsi=NULL;
+ }
+ if(vm->dvd) {
+ DVDClose(vm->dvd);
+ vm->dvd=NULL;
+ }
+ vm->stopped = 1;
+}
+
+int vm_reset(vm_t *vm, const char *dvdroot,
+ void *priv, dvdnav_stream_cb *stream_cb) {
+ /* 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 Language 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.registers.SPRM[15] = 0x7CFC; /* Audio capabilities - All defined audio types */
+
+ vm->state.pgN = 0;
+ vm->state.cellN = 0;
+ vm->state.cell_restart = 0;
+
+ vm->state.domain = DVD_DOMAIN_FirstPlay;
+ vm->state.rsm_vtsN = 0;
+ vm->state.rsm_cellN = 0;
+ vm->state.rsm_blockN = 0;
+
+ vm->state.vtsN = -1;
+
+ vm->hop_channel = 0;
+
+ /* save target callbacks */
+ if(stream_cb)
+ vm->streamcb = *stream_cb;
+ else
+ vm->streamcb = (dvdnav_stream_cb) { NULL, NULL, NULL };
+
+ /* bind local callbacks */
+ vm->dvdstreamcb.pf_seek = vm->streamcb.pf_seek ? dvd_reader_seek_handler : NULL;
+ vm->dvdstreamcb.pf_read = vm->streamcb.pf_read ? dvd_reader_read_handler : NULL;
+ vm->dvdstreamcb.pf_readv = vm->streamcb.pf_readv ? dvd_reader_readv_handler : NULL;
+
+ if (vm->dvd && (dvdroot || (priv && stream_cb))) {
+ /* a new dvd device has been requested */
+ vm_close(vm);
+ }
+ if (!vm->dvd) {
+ /* dvdread stream callback handlers for redirection */
+#if DVDREAD_VERSION >= DVDREAD_VERSION_CODE(6,1,0)
+ dvd_logger_cb dvdread_logcb = { .pf_log = dvd_reader_logger_handler };
+ /* Only install log handler if we have one ourself */
+ dvd_logger_cb *p_dvdread_logcb = vm->logcb.pf_log ? &dvdread_logcb : NULL;
+ if(dvdroot)
+ vm->dvd = DVDOpen2(vm, p_dvdread_logcb, dvdroot);
+ else if(vm->priv && vm->dvdstreamcb.pf_read)
+ vm->dvd = DVDOpenStream2(vm, p_dvdread_logcb, &vm->dvdstreamcb);
+#else
+ if(dvdroot)
+ vm->dvd = DVDOpen(dvdroot);
+ else if(vm->priv && vm->dvdstreamcb.pf_read)
+ vm->dvd = DVDOpenStream(vm, &vm->dvdstreamcb);
+#endif
+ if(!vm->dvd) {
+ Log0(vm, "vm: failed to open/read the DVD");
+ return 0;
+ }
+ vm->vmgi = ifoOpenVMGI(vm->dvd);
+ if(!vm->vmgi) {
+ Log0(vm, "vm: vm: failed to read VIDEO_TS.IFO");
+ return 0;
+ }
+ if(!ifoRead_FP_PGC(vm->vmgi)) {
+ Log0(vm, "vm: vm: ifoRead_FP_PGC failed");
+ return 0;
+ }
+ if(!ifoRead_TT_SRPT(vm->vmgi)) {
+ Log0(vm, "vm: vm: ifoRead_TT_SRPT failed");
+ return 0;
+ }
+ if(!ifoRead_PGCI_UT(vm->vmgi)) {
+ Log0(vm, "vm: vm: ifoRead_PGCI_UT failed");
+ return 0;
+ }
+ if(!ifoRead_PTL_MAIT(vm->vmgi)) {
+ Log0(vm, "vm: ifoRead_PTL_MAIT failed");
+ /* return 0; Not really used for now.. */
+ }
+ if(!ifoRead_VTS_ATRT(vm->vmgi)) {
+ Log0(vm, "vm: ifoRead_VTS_ATRT failed");
+ /* return 0; Not really used for now.. */
+ }
+ if(!ifoRead_VOBU_ADMAP(vm->vmgi)) {
+ Log0(vm, "vm: ifoRead_VOBU_ADMAP vgmi failed");
+ /* return 0; Not really used for now.. */
+ }
+ /* ifoRead_TXTDT_MGI(vmgi); Not implemented yet */
+ if(dvd_read_name(vm, vm->dvd_name, vm->dvd_serial, dvdroot) != 1) {
+ Log1(vm, "vm: dvd_read_name failed");
+ }
+ }
+ if (vm->vmgi) {
+ int i, mask;
+ char buffer[8 * 3 + 1];
+ char *p = buffer;
+ for (i = 1, mask = 1; i <= 8; i++, mask <<= 1)
+ {
+ if (((vm->vmgi->vmgi_mat->vmg_category >> 16) & mask) == 0)
+ {
+ sprintf(p, " %02d", i);
+ p +=3;
+ }
+ }
+ *p = 0;
+ Log2(vm, "DVD disk reports itself with Region mask 0x%08x. Regions:%s",
+ vm->vmgi->vmgi_mat->vmg_category, buffer);
+ }
+ return 1;
+}
+
+
+/* copying and merging */
+
+vm_t *vm_new_copy(vm_t *source) {
+ vm_t *target = vm_new_vm(source->priv, &source->logcb);
+ int vtsN;
+ int pgcN = get_PGCN(source);
+ int pgN = (source->state).pgN;
+
+ if (target == NULL || pgcN == 0)
+ goto fail;
+
+ memcpy(target, source, sizeof(vm_t));
+
+ /* open a new vtsi handle, because the copy might switch to another VTS */
+ target->vtsi = NULL;
+ vtsN = (target->state).vtsN;
+ if (vtsN > 0) {
+ (target->state).vtsN = 0;
+ if (!ifoOpenNewVTSI(target, target->dvd, vtsN))
+ goto fail;
+
+ /* restore pgc pointer into the new vtsi */
+ if (!set_PGCN(target, pgcN))
+ goto fail;
+
+ (target->state).pgN = pgN;
+ }
+
+ return target;
+
+fail:
+ if (target != NULL)
+ vm_free_vm(target);
+
+ return NULL;
+}
+
+void vm_merge(vm_t *target, vm_t *source) {
+ if(target->vtsi)
+ ifoClose(target->vtsi);
+ memcpy(target, source, sizeof(vm_t));
+ memset(source, 0, sizeof(vm_t));
+}
+
+void vm_free_copy(vm_t *vm) {
+ if(vm->vtsi)
+ ifoClose(vm->vtsi);
+ free(vm);
+}
+
+
+/* regular playback */
+
+void vm_position_get(vm_t *vm, vm_position_t *position) {
+ position->button = vm->state.HL_BTNN_REG >> 10;
+ position->vts = vm->state.vtsN;
+ position->domain = vm->state.domain;
+ position->spu_channel = vm->state.SPST_REG;
+ position->audio_channel = vm->state.AST_REG;
+ position->angle_channel = vm->state.AGL_REG;
+ position->hop_channel = vm->hop_channel; /* Increases by one on each hop */
+ position->cell = vm->state.cellN;
+ position->cell_restart = vm->state.cell_restart;
+ position->cell_start = vm->state.pgc->cell_playback[vm->state.cellN - 1].first_sector;
+ position->still = vm->state.pgc->cell_playback[vm->state.cellN - 1].still_time;
+ position->block = vm->state.blockN;
+
+ /* handle PGC stills at PGC end */
+ if (vm->state.cellN == vm->state.pgc->nr_of_cells)
+ position->still += vm->state.pgc->still_time;
+ /* still already determined */
+ if (position->still)
+ return;
+ /* This is a rough fix for some strange still situations on some strange DVDs.
+ * There are discs (like the German "Back to the Future" RC2) where the only
+ * indication of a still is a cell playback time higher than the time the frames
+ * in this cell actually take to play (like 1 frame with 1 minute playback time).
+ * On the said BTTF disc, for these cells last_sector and last_vobu_start_sector
+ * are equal and the cells are very short, so we abuse these conditions to
+ * detect such discs. I consider these discs broken, so the fix is somewhat
+ * broken, too. */
+ if ((vm->state.pgc->cell_playback[vm->state.cellN - 1].last_sector ==
+ vm->state.pgc->cell_playback[vm->state.cellN - 1].last_vobu_start_sector) &&
+ (vm->state.pgc->cell_playback[vm->state.cellN - 1].last_sector -
+ vm->state.pgc->cell_playback[vm->state.cellN - 1].first_sector < 1024)) {
+ int time;
+ int size = vm->state.pgc->cell_playback[vm->state.cellN - 1].last_sector -
+ vm->state.pgc->cell_playback[vm->state.cellN - 1].first_sector;
+ time = (vm->state.pgc->cell_playback[vm->state.cellN - 1].playback_time.hour >> 4 ) * 36000;
+ time += (vm->state.pgc->cell_playback[vm->state.cellN - 1].playback_time.hour & 0x0f) * 3600;
+ time += (vm->state.pgc->cell_playback[vm->state.cellN - 1].playback_time.minute >> 4 ) * 600;
+ time += (vm->state.pgc->cell_playback[vm->state.cellN - 1].playback_time.minute & 0x0f) * 60;
+ time += (vm->state.pgc->cell_playback[vm->state.cellN - 1].playback_time.second >> 4 ) * 10;
+ time += (vm->state.pgc->cell_playback[vm->state.cellN - 1].playback_time.second & 0x0f) * 1;
+ if (!time || size / time > 30)
+ /* datarate is too high, it might be a very short, but regular cell */
+ return;
+ if (time > 0xff) time = 0xff;
+ position->still = time;
+ }
+}
+
+void vm_get_next_cell(vm_t *vm) {
+ 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_cell_block(vm_t *vm, int cell, int block) {
+ vm->state.cellN = cell;
+ process_command(vm, play_Cell(vm));
+ /* play_Cell can jump to a different cell in case of angles */
+ if (vm->state.cellN == cell)
+ vm->state.blockN = block;
+ return 1;
+}
+
+int vm_jump_title_program(vm_t *vm, int title, int pgcn, int pgn) {
+ link_t link;
+
+ if(!set_PROG(vm, title, pgcn, pgn))
+ return 0;
+ /* Some DVDs do not want us to jump directly into a title and have
+ * PGC pre commands taking us back to some menu. Since we do not like that,
+ * we do not execute PGC pre commands that would do a jump. */
+ /* process_command(vm, play_PGC_PG(vm, vm->state.pgN)); */
+ link = play_PGC_PG(vm, vm->state.pgN);
+ if (link.command != PlayThis)
+ /* jump occurred -> ignore it and play the PG anyway */
+ process_command(vm, play_PG(vm));
+ else
+ process_command(vm, link);
+ return 1;
+}
+
+int vm_jump_title_part(vm_t *vm, int title, int part) {
+ link_t link;
+
+ if(!set_PTT(vm, title, part))
+ return 0;
+ /* Some DVDs do not want us to jump directly into a title and have
+ * PGC pre commands taking us back to some menu. Since we do not like that,
+ * we do not execute PGC pre commands that would do a jump. */
+ /* process_command(vm, play_PGC_PG(vm, vm->state.pgN)); */
+ link = play_PGC_PG(vm, vm->state.pgN);
+ if (link.command != PlayThis)
+ /* jump occurred -> ignore it and play the PG anyway */
+ process_command(vm, play_PG(vm));
+ else
+ process_command(vm, link);
+ 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));
+ vm_jump_pg(vm, vm->state.pgc->nr_of_programs);
+ 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) {
+ DVDDomain_t old_domain = vm->state.domain;
+
+ switch (vm->state.domain) {
+ case DVD_DOMAIN_FirstPlay: /* FIXME XXX $$$ What should we do here? */
+ break;
+ case DVD_DOMAIN_VTSTitle:
+ set_RSMinfo(vm, 0, vm->state.blockN);
+ /* FALL THROUGH */
+ case DVD_DOMAIN_VTSMenu:
+ case DVD_DOMAIN_VMGM:
+ switch(menuid) {
+ case DVD_MENU_Title:
+ case DVD_MENU_Escape:
+ if(vm->vmgi == NULL || vm->vmgi->pgci_ut == NULL)
+ return 0;
+ vm->state.domain = DVD_DOMAIN_VMGM;
+ break;
+ case DVD_MENU_Root:
+ case DVD_MENU_Subpicture:
+ case DVD_MENU_Audio:
+ case DVD_MENU_Angle:
+ case DVD_MENU_Part:
+ if(vm->vtsi == NULL || vm->vtsi->pgci_ut == NULL)
+ return 0;
+ vm->state.domain = DVD_DOMAIN_VTSMenu;
+ 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;
+ }
+
+ return 0;
+}
+
+int vm_jump_resume(vm_t *vm) {
+ link_t link_values = { LinkRSM, 0, 0, 0 };
+
+ if (!vm->state.rsm_vtsN) /* Do we have resume info? */
+ return 0;
+ return !!process_command(vm, link_values);
+}
+
+int vm_exec_cmd(vm_t *vm, const 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 that's all... */
+}
+
+/* link processing */
+
+static int process_command(vm_t *vm, link_t link_values) {
+
+ while(link_values.command != PlayThis) {
+
+#ifdef TRACE
+ Log3(vm, "Before printout starts:");
+ vm_print_link(link_values);
+ Log3(vm, "Link values %i %i %i %i", link_values.command,
+ link_values.data1, link_values.data2, link_values.data3);
+ vm_print_current_domain_state(vm);
+ Log3(vm, "Before printout ends.");
+#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;
+ 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;
+ assert(vm->state.cellN > 1);
+ vm->state.cellN -= 1;
+ link_values = play_Cell(vm);
+ break;
+
+ case LinkTopPG:
+ /* Link to Top of current Program */
+ /* BUTTON number:data1 */
+ 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))
+ link_values = play_PGC(vm);
+ else
+ link_values.command = Exit;
+ 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))
+ link_values = play_PGC(vm);
+ else
+ link_values.command = Exit;
+ 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))
+ link_values = play_PGC(vm);
+ else
+ link_values.command = Exit;
+ 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:
+ /* Check and see if there is any rsm info!! */
+ if (!vm->state.rsm_vtsN) {
+ Log2(vm, "trying to resume without any resume info set");
+ link_values.command = Exit;
+ break;
+ }
+
+ vm->state.domain = DVD_DOMAIN_VTSTitle;
+ if (!ifoOpenNewVTSI(vm, vm->dvd, vm->state.rsm_vtsN))
+ assert(0);
+ 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; ?? */
+ int i;
+ 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 right value in set_PGN() below */
+ vm->state.cellN = vm->state.rsm_cellN;
+ link_values.command = PlayThis;
+ link_values.data1 = vm->state.rsm_blockN & 0xffff;
+ link_values.data2 = vm->state.rsm_blockN >> 16;
+ 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 */
+ /* PGC Pre-Commands are not executed */
+ assert(vm->state.domain == DVD_DOMAIN_VTSTitle);
+ 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))
+ link_values.command = Exit;
+ else
+ 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:
+ vm->stopped = 1;
+ return 0;
+
+ case JumpTT:
+ /* Jump to VTS Title Domain */
+ /* Only allowed from the First Play domain(PGC) */
+ /* or the Video Manager domain (VMG) */
+ /* Stop SPRM9 Timer */
+ /* Set SPRM1 and SPRM2 */
+ assert(vm->state.domain == DVD_DOMAIN_VMGM || vm->state.domain == DVD_DOMAIN_FirstPlay); /* ?? */
+ if(set_TT(vm, link_values.data1))
+ link_values = play_PGC(vm);
+ else
+ link_values.command = Exit;
+ 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) */
+ /* Stop SPRM9 Timer */
+ /* Set SPRM1 and SPRM2 */
+ assert(vm->state.domain == DVD_DOMAIN_VTSMenu || vm->state.domain == DVD_DOMAIN_VTSTitle); /* ?? */
+ if(!set_VTS_TT(vm, vm->state.vtsN, link_values.data1))
+ link_values.command = Exit;
+ else
+ 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) */
+ /* Stop SPRM9 Timer */
+ /* Set SPRM1 and SPRM2 */
+ assert(vm->state.domain == DVD_DOMAIN_VTSMenu || vm->state.domain == DVD_DOMAIN_VTSTitle); /* ?? */
+ if(!set_VTS_PTT(vm, vm->state.vtsN, link_values.data1, link_values.data2))
+ link_values.command = Exit;
+ else
+ 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) */
+ /* Stop SPRM9 Timer and any GPRM counters */
+ assert(vm->state.domain == DVD_DOMAIN_VMGM || vm->state.domain == DVD_DOMAIN_VTSMenu); /* ?? */
+ if (!set_FP_PGC(vm))
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+
+ case JumpSS_VMGM_MENU:
+ /* Jump to Video Manager domain - Title Menu:data1 or any PGC in VMG */
+ /* Allowed from anywhere except the VTS Title domain */
+ /* Stop SPRM9 Timer and any GPRM counters */
+ assert(vm->state.domain != DVD_DOMAIN_VTSTitle); /* ?? */
+ if(vm->vmgi == NULL || vm->vmgi->pgci_ut == NULL) {
+ link_values.command = Exit;
+ break;
+ }
+ vm->state.domain = DVD_DOMAIN_VMGM;
+ 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 */
+ /* Stop SPRM9 Timer and any GPRM counters */
+ /* ifoOpenNewVTSI:data1 */
+ /* VTS_TTN_REG:data2 */
+ /* get_MENU:data3 */
+ if(link_values.data1 != 0) {
+ assert(vm->state.domain == DVD_DOMAIN_VTSMenu ||
+ vm->state.domain == DVD_DOMAIN_VMGM || vm->state.domain == DVD_DOMAIN_FirstPlay); /* ?? */
+ if (link_values.data1 != vm->state.vtsN) {
+ /* the normal case */
+ assert(vm->state.domain != DVD_DOMAIN_VTSMenu);
+ if (!ifoOpenNewVTSI(vm, vm->dvd, link_values.data1)) /* Also sets vm->state.vtsN */
+ vm->vtsi = NULL;
+ } else {
+ /* This happens on some discs like "Captain Scarlet & the Mysterons" or
+ * the German RC2 of "Anatomie" in VTSM. */
+ }
+
+ if(vm->vtsi == NULL || vm->vtsi->pgci_ut == NULL) {
+ link_values.command = Exit;
+ break;
+ }
+ vm->state.domain = DVD_DOMAIN_VTSMenu;
+ } else {
+ /* This happens on 'The Fifth Element' region 2. */
+ assert(vm->state.domain == DVD_DOMAIN_VTSMenu);
+ }
+ /* 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;
+ /* TTN_REG (SPRM4), VTS_TTN_REG (SPRM5), TT_PGCN_REG (SPRM6) are linked, */
+ /* so if one changes, the others must change to match it. */
+ vm->state.TTN_REG = get_TT(vm, vm->state.vtsN, vm->state.VTS_TTN_REG);
+ if(!set_MENU(vm, link_values.data3))
+ assert(0);
+ link_values = play_PGC(vm);
+ break;
+
+ case JumpSS_VMGM_PGC:
+ /* set_PGCN:data1 */
+ /* Stop SPRM9 Timer and any GPRM counters */
+ assert(vm->state.domain != DVD_DOMAIN_VTSTitle); /* ?? */
+ if(vm->vmgi == NULL || vm->vmgi->pgci_ut == NULL) {
+ link_values.command = Exit;
+ break;
+ }
+ vm->state.domain = DVD_DOMAIN_VMGM;
+ 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 == DVD_DOMAIN_VTSTitle); /* ?? */
+ /* 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 == DVD_DOMAIN_VTSTitle); /* ?? */
+ /* Must be called before domain is changed */
+ if(vm->vmgi == NULL || vm->vmgi->pgci_ut == NULL) {
+ link_values.command = Exit;
+ break;
+ }
+ set_RSMinfo(vm, link_values.data2, /* We dont have block info */ 0);
+ vm->state.domain = DVD_DOMAIN_VMGM;
+ 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 == DVD_DOMAIN_VTSTitle); /* ?? */
+ /* Must be called before domain is changed */
+ if(vm->vtsi == NULL || vm->vtsi->pgci_ut == NULL) {
+ link_values.command = Exit;
+ break;
+ }
+ set_RSMinfo(vm, link_values.data2, /* We dont have block info */ 0);
+ vm->state.domain = DVD_DOMAIN_VTSMenu;
+ 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 == DVD_DOMAIN_VTSTitle); /* ?? */
+ /* Must be called before domain is changed */
+ if(vm->vmgi == NULL || vm->vmgi->pgci_ut == NULL) {
+ link_values.command = Exit;
+ break;
+ }
+ set_RSMinfo(vm, link_values.data2, /* We dont have block info */ 0);
+ vm->state.domain = DVD_DOMAIN_VMGM;
+ 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
+ Log3(vm, "After printout starts:");
+ vm_print_current_domain_state(vm);
+ Log3(vm, "After printout ends.");
+#endif
+
+ }
+
+ vm->state.blockN = link_values.data1 | (link_values.data2 << 16);
+ return 1;
+}
+
+//return the ifo_handle_t describing required title, used to
+//identify chapters
+ifo_handle_t *vm_get_title_ifo(vm_t *vm, uint32_t title)
+{
+ uint8_t titleset_nr;
+ if((title < 1) || (title > vm->vmgi->tt_srpt->nr_of_srpts))
+ return NULL;
+ titleset_nr = vm->vmgi->tt_srpt->title[title-1].title_set_nr;
+ return ifoOpen(vm->dvd, titleset_nr);
+}
+
+void vm_ifo_close(ifo_handle_t *ifo)
+{
+ ifoClose(ifo);
+}
diff --git a/libdvdnav-embedded/src/vm/vm.h b/libdvdnav-embedded/src/vm/vm.h
new file mode 100644
index 0000000..bada9f0
--- /dev/null
+++ b/libdvdnav-embedded/src/vm/vm.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2000, 2001 Håkan Hjort
+ * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef LIBDVDNAV_VM_H
+#define LIBDVDNAV_VM_H
+
+/**
+ * State: SPRM, GPRM, Domain, pgc, pgN, cellN, ?
+ */
+typedef struct {
+ registers_t registers;
+
+ DVDDomain_t domain;
+ int vtsN; /* 0 is vmgm? */
+ const pgc_t *pgc; /* either this or 'int pgcN' is enough? */
+ int pgcN; /* but provide pgcN for quick lookup */
+ int pgN; /* is this needed? can always 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;
+} dvd_state_t;
+
+typedef struct vm_position_s {
+ int16_t button; /* Button highlighted */
+ int32_t vts; /* vts number to use */
+ DVDDomain_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 */
+#if 0
+ /* currently unused */
+ int32_t title; /* title number */
+ int32_t chapter; /* chapter number */
+#endif
+ 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 {
+ void *priv;
+ dvdnav_logger_cb logcb;
+ dvdnav_stream_cb streamcb;
+ dvd_reader_t *dvd;
+ dvd_reader_stream_cb dvdstreamcb;
+ ifo_handle_t *vmgi;
+ ifo_handle_t *vtsi;
+ dvd_state_t state;
+ int32_t hop_channel;
+ char dvd_name[50];
+ char dvd_serial[15];
+ int stopped;
+} vm_t;
+
+/* magic number for seeking hops */
+#define HOP_SEEK 0x1000
+
+
+/* Audio stream number */
+#define AST_REG registers.SPRM[1]
+/* Subpicture stream number */
+#define SPST_REG registers.SPRM[2]
+/* Angle number */
+#define AGL_REG registers.SPRM[3]
+/* Title Track Number */
+#define TTN_REG registers.SPRM[4]
+/* VTS Title Track Number */
+#define VTS_TTN_REG registers.SPRM[5]
+/* PGC Number for this Title Track */
+#define TT_PGCN_REG registers.SPRM[6]
+/* Current Part of Title (PTT) number for (One_Sequential_PGC_Title) */
+#define PTTN_REG registers.SPRM[7]
+/* Highlighted Button Number (btn nr 1 == value 1024) */
+#define HL_BTNN_REG registers.SPRM[8]
+/* Parental Level */
+#define PTL_REG registers.SPRM[13]
+
+/* Initialisation & destruction */
+vm_t *vm_new_vm(void *, const dvdnav_logger_cb *);
+void vm_free_vm(vm_t *vm);
+
+/* IFO access */
+ifo_handle_t *vm_get_vmgi(vm_t *vm);
+ifo_handle_t *vm_get_vtsi(vm_t *vm);
+
+/* Reader Access */
+dvd_reader_t *vm_get_dvd_reader(vm_t *vm);
+
+/* Basic Handling */
+int vm_start(vm_t *vm);
+void vm_stop(vm_t *vm);
+int vm_reset(vm_t *vm, const char *dvdroot, void *priv,
+ dvdnav_stream_cb *stream_cb);
+
+/* copying and merging - useful for try-running an operation */
+vm_t *vm_new_copy(vm_t *vm);
+void vm_merge(vm_t *target, vm_t *source);
+void vm_free_copy(vm_t *vm);
+
+/* 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_cell_block(vm_t *vm, int cell, int block);
+int vm_jump_title_part(vm_t *vm, int title, int part);
+int vm_jump_title_program(vm_t *vm, int title, int pgcn, int pgn);
+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_jump_resume(vm_t *vm);
+int vm_exec_cmd(vm_t *vm, const vm_cmd_t *cmd);
+
+/* getting information */
+int vm_get_current_menu(vm_t *vm, int *menuid);
+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_active_stream(vm_t *vm, int mode);
+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);
+#endif
+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);
+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);
+ifo_handle_t *vm_get_title_ifo(vm_t *vm, uint32_t title);
+void vm_ifo_close(ifo_handle_t *ifo);
+
+/* Uncomment for VM command tracing */
+/* #define TRACE */
+#ifdef TRACE
+/* Debug */
+void vm_position_print(vm_t *vm, vm_position_t *position);
+#endif
+
+int ifoOpenNewVTSI(vm_t *vm, dvd_reader_t *dvd, int vtsN);
+
+
+#endif /* LIBDVDNAV_VM_H */
diff --git a/libdvdnav-embedded/src/vm/vmcmd.c b/libdvdnav-embedded/src/vm/vmcmd.c
new file mode 100644
index 0000000..557a8a1
--- /dev/null
+++ b/libdvdnav-embedded/src/vm/vmcmd.c
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2000, 2001 Martin Norbäck, Håkan Hjort
+ * 2002-2004 the dvdnav project
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <sys/time.h>
+
+#include "dvdnav/dvdnav.h"
+#include <dvdread/nav_types.h>
+#include "decoder.h"
+#include "vm.h"
+#include "vmcmd.h"
+#include "dvdnav_internal.h"
+
+/* freebsd compatibility */
+#ifndef PRIu8
+#define PRIu8 "d"
+#endif
+
+/* freebsd compatibility */
+#ifndef PRIu16
+#define PRIu16 "d"
+#endif
+
+static const char cmp_op_table[][4] = {
+ "", "&", "==", "!=", ">=", ">", "<=", "<"
+};
+static const char set_op_table[][4] = {
+ "", "=", "<->", "+=", "-=", "*=", "/=", "%=", "rnd", "&=", "|=", "^="
+};
+
+static const char link_table[][16] = {
+ "LinkNoLink", "LinkTopC", "LinkNextC", "LinkPrevC",
+ "", "LinkTopPG", "LinkNextPG", "LinkPrevPG",
+ "", "LinkTopPGC", "LinkNextPGC", "LinkPrevPGC",
+ "LinkGoUpPGC", "LinkTailPGC", "", "",
+ "RSM"
+};
+
+static const char *const system_reg_table[] = {
+ "Menu Description Language Code",
+ "Audio Stream Number",
+ "Sub-picture Stream Number",
+ "Angle Number",
+ "Title Track Number",
+ "VTS Title Track Number",
+ "VTS PGC Number",
+ "PTT Number for One_Sequential_PGC_Title",
+ "Highlighted Button Number",
+ "Navigation Timer",
+ "Title PGC Number for Navigation Timer",
+ "Audio Mixing Mode for Karaoke",
+ "Country Code for Parental Management",
+ "Parental Level",
+ "Player Configurations for Video",
+ "Player Configurations for Audio",
+ "Initial Language Code for Audio",
+ "Initial Language Code Extension for Audio",
+ "Initial Language Code for Sub-picture",
+ "Initial Language Code Extension for Sub-picture",
+ "Player Regional Code",
+ "Reserved 21",
+ "Reserved 22",
+ "Reserved 23"
+};
+
+static const char system_reg_abbr_table[][8] = {
+ "",
+ "ASTN",
+ "SPSTN",
+ "AGLN",
+ "TTN",
+ "VTS_TTN",
+ "TT_PGCN",
+ "PTTN",
+ "HL_BTNN",
+ "NVTMR",
+ "NV_PGCN",
+ "",
+ "CC_PLT",
+ "PLT",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+};
+
+static void print_system_reg(uint16_t reg) {
+ if(reg < sizeof(system_reg_abbr_table) / sizeof(system_reg_abbr_table[0]))
+ fprintf(MSG_OUT, "%s (SRPM:%d)", system_reg_table[reg], reg);
+ else
+ fprintf(MSG_OUT, " WARNING: Unknown system register ( reg=%d ) ", reg);
+}
+
+static void print_g_reg(uint8_t reg) {
+ if(reg < 16)
+ fprintf(MSG_OUT, "g[%" PRIu8 "]", reg);
+ else
+ fprintf(MSG_OUT, " WARNING: Unknown general register ");
+}
+
+static void print_reg(uint8_t reg) {
+ if(reg & 0x80)
+ print_system_reg(reg & 0x7f);
+ else
+ print_g_reg(reg & 0x7f);
+}
+
+static void print_cmp_op(uint8_t op) {
+ if(op < sizeof(cmp_op_table) / sizeof(cmp_op_table[0]))
+ fprintf(MSG_OUT, " %s ", cmp_op_table[op]);
+ else
+ fprintf(MSG_OUT, " WARNING: Unknown compare op ");
+}
+
+static void print_set_op(uint8_t op) {
+ if(op < sizeof(set_op_table) / sizeof(cmp_op_table[0]))
+ fprintf(MSG_OUT, " %s ", set_op_table[op]);
+ else
+ fprintf(MSG_OUT, " WARNING: Unknown set op ");
+}
+
+static void print_reg_or_data(command_t* command, int immediate, int start) {
+ if(immediate) {
+ uint32_t i = vm_getbits(command, start, 16);
+
+ fprintf(MSG_OUT, "0x%x", i);
+ if(isprint(i & 0xff) && isprint((i>>8) & 0xff))
+ fprintf(MSG_OUT, " (\"%c%c\")", (char)((i>>8) & 0xff), (char)(i & 0xff));
+ } else {
+ print_reg(vm_getbits(command, start - 8, 8));
+ }
+}
+
+static void print_reg_or_data_2(command_t* command, int immediate, int start) {
+ if(immediate)
+ fprintf(MSG_OUT, "0x%x", vm_getbits(command, start - 1, 7));
+ else
+ fprintf(MSG_OUT, "g[%" PRIu8 "]", vm_getbits(command, start - 4, 4));
+}
+
+static void print_reg_or_data_3(command_t* command, int immediate, int start) {
+ if(immediate) {
+ uint32_t i = vm_getbits(command, start, 16);
+
+ fprintf(MSG_OUT, "0x%x", i);
+ if(isprint(i & 0xff) && isprint((i>>8) & 0xff))
+ fprintf(MSG_OUT, " (\"%c%c\")", (char)((i>>8) & 0xff), (char)(i & 0xff));
+ } else {
+ print_reg(vm_getbits(command, start, 8));
+ }
+}
+
+
+static void print_if_version_1(command_t* command) {
+ uint8_t op = vm_getbits(command, 54, 3);
+
+ if(op) {
+ fprintf(MSG_OUT, "if (");
+ print_g_reg(vm_getbits(command,39,8));
+ print_cmp_op(op);
+ print_reg_or_data(command, vm_getbits(command, 55,1), 31);
+ fprintf(MSG_OUT, ") ");
+ }
+}
+
+static void print_if_version_2(command_t* command) {
+ uint8_t op = vm_getbits(command, 54, 3);
+
+ if(op) {
+ fprintf(MSG_OUT, "if (");
+ print_reg(vm_getbits(command, 15, 8));
+ print_cmp_op(op);
+ print_reg(vm_getbits(command, 7, 8));
+ fprintf(MSG_OUT, ") ");
+ }
+}
+
+static void print_if_version_3(command_t* command) {
+ uint8_t op = vm_getbits(command, 54, 3);
+
+ if(op) {
+ fprintf(MSG_OUT, "if (");
+ print_g_reg(vm_getbits(command, 43, 4));
+ print_cmp_op(op);
+ print_reg_or_data(command, vm_getbits(command, 55, 1), 15);
+ fprintf(MSG_OUT, ") ");
+ }
+}
+
+static void print_if_version_4(command_t* command) {
+ uint8_t op = vm_getbits(command, 54, 3);
+
+ if(op) {
+ fprintf(MSG_OUT, "if (");
+ print_g_reg(vm_getbits(command, 51, 4));
+ print_cmp_op(op);
+ print_reg_or_data(command, vm_getbits(command, 55, 1), 31);
+ fprintf(MSG_OUT, ") ");
+ }
+}
+
+static void print_if_version_5(command_t* command) {
+ uint8_t op = vm_getbits(command, 54, 3);
+ int set_immediate = vm_getbits(command, 60, 1);
+
+ if(op) {
+ if (set_immediate) {
+ fprintf(MSG_OUT, "if (");
+ print_g_reg(vm_getbits(command, 31, 8));
+ print_cmp_op(op);
+ print_reg(vm_getbits(command, 23, 8));
+ fprintf(MSG_OUT, ") ");
+ } else {
+ fprintf(MSG_OUT, "if (");
+ print_g_reg(vm_getbits(command, 39, 8));
+ print_cmp_op(op);
+ print_reg_or_data(command, vm_getbits(command, 55, 1), 31);
+ fprintf(MSG_OUT, ") ");
+ }
+ }
+}
+
+static void print_special_instruction(command_t* command) {
+ uint8_t op = vm_getbits(command, 51, 4);
+
+ switch(op) {
+ case 0: /* NOP */
+ fprintf(MSG_OUT, "Nop");
+ break;
+ case 1: /* Goto line */
+ fprintf(MSG_OUT, "Goto %" PRIu8, vm_getbits(command, 7, 8));
+ break;
+ case 2: /* Break */
+ fprintf(MSG_OUT, "Break");
+ break;
+ case 3: /* Parental level */
+ fprintf(MSG_OUT, "SetTmpPML %" PRIu8 ", Goto %" PRIu8,
+ vm_getbits(command, 11, 4), vm_getbits(command, 7, 8));
+ break;
+ default:
+ fprintf(MSG_OUT, "WARNING: Unknown special instruction (%i)",
+ vm_getbits(command, 51, 4));
+ }
+}
+
+static void print_linksub_instruction(command_t* command) {
+ uint32_t linkop = vm_getbits(command, 7, 8);
+ uint32_t button = vm_getbits(command, 15, 6);
+
+ if(linkop < sizeof(link_table)/sizeof(link_table[0]))
+ fprintf(MSG_OUT, "%s (button %" PRIu8 ")", link_table[linkop], button);
+ else
+ fprintf(MSG_OUT, "WARNING: Unknown linksub instruction (%i)", linkop);
+}
+
+static void print_link_instruction(command_t* command, int optional) {
+ uint8_t op = vm_getbits(command, 51, 4);
+
+ if(optional && op)
+ fprintf(MSG_OUT, ", ");
+
+ switch(op) {
+ case 0:
+ if(!optional)
+ fprintf(MSG_OUT, "WARNING: NOP (link)!");
+ break;
+ case 1:
+ print_linksub_instruction(command);
+ break;
+ case 4:
+ fprintf(MSG_OUT, "LinkPGCN %" PRIu16, vm_getbits(command, 14, 15));
+ break;
+ case 5:
+ fprintf(MSG_OUT, "LinkPTT %" PRIu16 " (button %" PRIu8 ")",
+ vm_getbits(command, 9, 10), vm_getbits(command, 15, 6));
+ break;
+ case 6:
+ fprintf(MSG_OUT, "LinkPGN %" PRIu8 " (button %" PRIu8 ")",
+ vm_getbits(command, 6, 7), vm_getbits(command, 15, 6));
+ break;
+ case 7:
+ fprintf(MSG_OUT, "LinkCN %" PRIu8 " (button %" PRIu8 ")",
+ vm_getbits(command, 7, 8), vm_getbits(command, 15, 6));
+ break;
+ default:
+ fprintf(MSG_OUT, "WARNING: Unknown link instruction");
+ }
+}
+
+static void print_jump_instruction(command_t* command) {
+ switch(vm_getbits(command, 51, 4)) {
+ case 1:
+ fprintf(MSG_OUT, "Exit");
+ break;
+ case 2:
+ fprintf(MSG_OUT, "JumpTT %" PRIu8, vm_getbits(command, 22, 7));
+ break;
+ case 3:
+ fprintf(MSG_OUT, "JumpVTS_TT %" PRIu8, vm_getbits(command, 22, 7));
+ break;
+ case 5:
+ fprintf(MSG_OUT, "JumpVTS_PTT %" PRIu8 ":%" PRIu16,
+ vm_getbits(command, 22, 7), vm_getbits(command, 41, 10));
+ break;
+ case 6:
+ switch(vm_getbits(command, 23, 2)) {
+ case 0:
+ fprintf(MSG_OUT, "JumpSS FP");
+ break;
+ case 1:
+ fprintf(MSG_OUT, "JumpSS VMGM (menu %" PRIu8 ")", vm_getbits(command, 19, 4));
+ break;
+ case 2:
+ fprintf(MSG_OUT, "JumpSS VTSM (vts %" PRIu8 ", title %" PRIu8
+ ", menu %" PRIu8 ")", vm_getbits(command, 30, 7), vm_getbits(command, 38, 7), vm_getbits(command, 19, 4));
+ break;
+ case 3:
+ fprintf(MSG_OUT, "JumpSS VMGM (pgc %" PRIu8 ")", vm_getbits(command, 46, 15));
+ break;
+ }
+ break;
+ case 8:
+ switch(vm_getbits(command, 23, 2)) {
+ case 0:
+ fprintf(MSG_OUT, "CallSS FP (rsm_cell %" PRIu8 ")",
+ vm_getbits(command, 31, 8));
+ break;
+ case 1:
+ fprintf(MSG_OUT, "CallSS VMGM (menu %" PRIu8
+ ", rsm_cell %" PRIu8 ")", vm_getbits(command, 19, 4), vm_getbits(command, 31, 8));
+ break;
+ case 2:
+ fprintf(MSG_OUT, "CallSS VTSM (menu %" PRIu8
+ ", rsm_cell %" PRIu8 ")", vm_getbits(command, 19, 4), vm_getbits(command, 31, 8));
+ break;
+ case 3:
+ fprintf(MSG_OUT, "CallSS VMGM (pgc %" PRIu8 ", rsm_cell %" PRIu8 ")",
+ vm_getbits(command, 46, 15), vm_getbits(command, 31, 8));
+ break;
+ }
+ break;
+ default:
+ fprintf(MSG_OUT, "WARNING: Unknown Jump/Call instruction");
+ }
+}
+
+static void print_system_set(command_t* command) {
+ int i;
+/* FIXME: What about SPRM11 ? Karaoke */
+/* Surely there must be some system set command for that ? */
+
+ switch(vm_getbits(command, 59, 4)) {
+ case 1: /* Set system reg 1 &| 2 &| 3 (Audio, Subp. Angle) */
+ for(i = 1; i <= 3; i++) {
+ if(vm_getbits(command, 47 - (i*8), 1)) {
+ print_system_reg(i);
+ fprintf(MSG_OUT, " = ");
+ print_reg_or_data_2(command, vm_getbits(command, 60, 1), 47 - (i*8) );
+ fprintf(MSG_OUT, " ");
+ }
+ }
+ break;
+ case 2: /* Set system reg 9 & 10 (Navigation timer, Title PGC number) */
+ print_system_reg(9);
+ fprintf(MSG_OUT, " = ");
+ print_reg_or_data(command, vm_getbits(command, 60, 1), 47);
+ fprintf(MSG_OUT, " ");
+ print_system_reg(10);
+ fprintf(MSG_OUT, " = %" PRIu16, vm_getbits(command, 30, 15)); /* ?? */
+ break;
+ case 3: /* Mode: Counter / Register + Set */
+ fprintf(MSG_OUT, "SetMode ");
+ if(vm_getbits(command, 23, 1))
+ fprintf(MSG_OUT, "Counter ");
+ else
+ fprintf(MSG_OUT, "Register ");
+ print_g_reg(vm_getbits(command, 19, 4));
+ print_set_op(0x1); /* '=' */
+ print_reg_or_data(command, vm_getbits(command, 60, 1), 47);
+ break;
+ case 6: /* Set system reg 8 (Highlighted button) */
+ print_system_reg(8);
+ if(vm_getbits(command, 60, 1)) /* immediate */
+ fprintf(MSG_OUT, " = 0x%x (button no %d)", vm_getbits(command, 31, 16), vm_getbits(command, 31, 6));
+ else
+ fprintf(MSG_OUT, " = g[%" PRIu8 "]", vm_getbits(command, 19, 4));
+ break;
+ default:
+ fprintf(MSG_OUT, "WARNING: Unknown system set instruction (%i)",
+ vm_getbits(command, 59, 4));
+ }
+}
+
+static void print_set_version_1(command_t* command) {
+ uint8_t set_op = vm_getbits(command, 59, 4);
+
+ if(set_op) {
+ print_g_reg(vm_getbits(command, 35, 4));
+ print_set_op(set_op);
+ print_reg_or_data(command, vm_getbits(command, 60, 1), 31);
+ } else {
+ fprintf(MSG_OUT, "NOP");
+ }
+}
+
+static void print_set_version_2(command_t* command) {
+ uint8_t set_op = vm_getbits(command, 59, 4);
+
+ if(set_op) {
+ print_g_reg(vm_getbits(command, 51, 4));
+ print_set_op(set_op);
+ print_reg_or_data(command, vm_getbits(command, 60, 1), 47);
+ } else {
+ fprintf(MSG_OUT, "NOP");
+ }
+}
+
+static void print_set_version_3(command_t* command) {
+ uint8_t set_op = vm_getbits(command, 59, 4);
+
+ if(set_op) {
+ print_g_reg(vm_getbits(command, 51, 4));
+ print_set_op(set_op);
+ print_reg_or_data_3(command, vm_getbits(command, 60, 1), 47);
+ } else {
+ fprintf(MSG_OUT, "NOP");
+ }
+}
+
+
+void vm_print_mnemonic(vm_cmd_t *vm_command) {
+ command_t command;
+ command.instruction =( (uint64_t) vm_command->bytes[0] << 56 ) |
+ ( (uint64_t) vm_command->bytes[1] << 48 ) |
+ ( (uint64_t) vm_command->bytes[2] << 40 ) |
+ ( (uint64_t) vm_command->bytes[3] << 32 ) |
+ ( (uint64_t) vm_command->bytes[4] << 24 ) |
+ ( (uint64_t) vm_command->bytes[5] << 16 ) |
+ ( (uint64_t) vm_command->bytes[6] << 8 ) |
+ (uint64_t) vm_command->bytes[7] ;
+ command.examined = 0;
+
+ switch(vm_getbits(&command,63,3)) { /* three first bits */
+ case 0: /* Special instructions */
+ print_if_version_1(&command);
+ print_special_instruction(&command);
+ break;
+ case 1: /* Jump/Call or Link instructions */
+ if(vm_getbits(&command,60,1)) {
+ print_if_version_2(&command);
+ print_jump_instruction(&command);
+ } else {
+ print_if_version_1(&command);
+ print_link_instruction(&command, 0); /* must be present */
+ }
+ break;
+ case 2: /* Set System Parameters instructions */
+ print_if_version_2(&command);
+ print_system_set(&command);
+ print_link_instruction(&command, 1); /* either 'if' or 'link' */
+ break;
+ case 3: /* Set General Parameters instructions */
+ print_if_version_3(&command);
+ print_set_version_1(&command);
+ print_link_instruction(&command, 1); /* either 'if' or 'link' */
+ break;
+ case 4: /* Set, Compare -> LinkSub instructions */
+ print_set_version_2(&command);
+ fprintf(MSG_OUT, ", ");
+ print_if_version_4(&command);
+ print_linksub_instruction(&command);
+ break;
+ case 5: /* Compare -> (Set and LinkSub) instructions */
+ print_if_version_5(&command);
+ fprintf(MSG_OUT, "{ ");
+ print_set_version_3(&command);
+ fprintf(MSG_OUT, ", ");
+ print_linksub_instruction(&command);
+ fprintf(MSG_OUT, " }");
+ break;
+ case 6: /* Compare -> Set, always LinkSub instructions */
+ print_if_version_5(&command);
+ fprintf(MSG_OUT, "{ ");
+ print_set_version_3(&command);
+ fprintf(MSG_OUT, " } ");
+ print_linksub_instruction(&command);
+ break;
+ default:
+ fprintf(MSG_OUT, "WARNING: Unknown instruction type (%i)", vm_getbits(&command, 63, 3));
+ }
+ /* Check if there still are bits set that were not examined */
+
+ if(command.instruction & ~ command.examined) {
+ fprintf(MSG_OUT, " libdvdnav: vmcmd.c: [WARNING, unknown bits:");
+ fprintf(MSG_OUT, " %08"PRIx64, (command.instruction & ~ command.examined) );
+ fprintf(MSG_OUT, "]");
+ }
+}
+
+void vm_print_cmd(int row, vm_cmd_t *vm_command) {
+ int i;
+
+ fprintf(MSG_OUT, "(%03d) ", row + 1);
+ for(i = 0; i < 8; i++)
+ fprintf(MSG_OUT, "%02x ", vm_command->bytes[i]);
+ fprintf(MSG_OUT, "| ");
+
+ vm_print_mnemonic(vm_command);
+ fprintf(MSG_OUT, "\n");
+}
+
diff --git a/libdvdnav-embedded/src/vm/vmcmd.h b/libdvdnav-embedded/src/vm/vmcmd.h
new file mode 100644
index 0000000..1e28c11
--- /dev/null
+++ b/libdvdnav-embedded/src/vm/vmcmd.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2000, 2001 Martin Norbäck, Håkan Hjort
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef LIBDVDNAV_VMCMD_H
+#define LIBDVDNAV_VMCMD_H
+
+void vm_print_mnemonic(vm_cmd_t *command);
+void vm_print_cmd(int row, vm_cmd_t *command);
+
+#endif /* LIBDVDNAV_VMCMD_H */
diff --git a/libdvdnav-embedded/src/vm/vmget.c b/libdvdnav-embedded/src/vm/vmget.c
new file mode 100644
index 0000000..5816f2c
--- /dev/null
+++ b/libdvdnav-embedded/src/vm/vmget.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2000, 2001 Håkan Hjort
+ * Copyright (C) 2001 Rich Wareham <richwareham@users.sourceforge.net>
+ * 2002-2004 the dvdnav project
+ *
+ * This file is part of libdvdnav, a DVD navigation library. It is modified
+ * from a file originally part of the Ogle DVD player.
+ *
+ * libdvdnav is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libdvdnav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with libdvdnav; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <dvdread/nav_types.h>
+#include <dvdread/ifo_read.h>
+#include "dvdnav/dvdnav.h"
+
+#include "decoder.h"
+#include "vm.h"
+#include "getset.h"
+#include "dvdnav_internal.h"
+#include "logger.h"
+
+/* getting information */
+
+int vm_get_current_menu(vm_t *vm, int *menuid) {
+ const pgcit_t* pgcit;
+ int pgcn;
+ pgcn = (vm->state).pgcN;
+ pgcit = get_PGCIT(vm);
+ if(pgcit==NULL) return 0;
+ *menuid = pgcit->pgci_srp[pgcn - 1].entry_id & 0xf ;
+ return 1;
+}
+
+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) {
+ if (vts_ptt_srpt->title[vts_ttn].ptt[part].pgn == pgN) {
+ found = 1;
+ break;
+ }
+ if (part > 0 && vts_ptt_srpt->title[vts_ttn].ptt[part].pgn > pgN &&
+ vts_ptt_srpt->title[vts_ttn].ptt[part - 1].pgn < pgN) {
+ part--;
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (found) break;
+ }
+ vts_ttn++;
+ part++;
+
+ if (!found) {
+ Log1(vm, "chapter NOT FOUND!");
+ return 0;
+ }
+
+ title = get_TT(vm, vm->state.vtsN, vts_ttn);
+
+#ifdef TRACE
+ if (title) {
+ Log3(vm, "************ this chapter FOUND!");
+ Log3(vm, "VTS_PTT_SRPT - Title %3i part %3i: PGC: %3i PG: %3i",
+ title, part,
+ vts_ptt_srpt->title[vts_ttn-1].ptt[part-1].pgcn ,
+ vts_ptt_srpt->title[vts_ttn-1].ptt[part-1].pgn );
+ }
+#endif
+ *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;
+
+ if((vm->state).domain != DVD_DOMAIN_VTSTitle)
+ audioN = 0;
+
+ if(audioN < 8) {
+ /* Is there any control 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 != DVD_DOMAIN_VTSTitle && 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 != DVD_DOMAIN_VTSTitle)
+ subpN = 0;
+
+ if(subpN < 32) { /* a valid logical stream */
+ /* Is this logical stream present */
+ if((vm->state).pgc->subp_control[subpN] & (1u<<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 != DVD_DOMAIN_VTSTitle && 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 == DVD_DOMAIN_VTSTitle && !((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 == DVD_DOMAIN_VTSTitle) {
+ const title_info_t *title;
+ /* TTN_REG does not always 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 DVD_DOMAIN_VTSTitle:
+ *num_avail = vm->vtsi->vtsi_mat->nr_of_vts_audio_streams;
+ *current = (vm->state).AST_REG;
+ break;
+ case DVD_DOMAIN_VTSMenu:
+ *num_avail = vm->vtsi->vtsi_mat->nr_of_vtsm_audio_streams; /* 1 */
+ *current = 1;
+ break;
+ case DVD_DOMAIN_VMGM:
+ case DVD_DOMAIN_FirstPlay:
+ *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 DVD_DOMAIN_VTSTitle:
+ *num_avail = vm->vtsi->vtsi_mat->nr_of_vts_subp_streams;
+ *current = (vm->state).SPST_REG;
+ break;
+ case DVD_DOMAIN_VTSMenu:
+ *num_avail = vm->vtsi->vtsi_mat->nr_of_vtsm_subp_streams; /* 1 */
+ *current = 0x41;
+ break;
+ case DVD_DOMAIN_VMGM:
+ case DVD_DOMAIN_FirstPlay:
+ *num_avail = vm->vmgi->vmgi_mat->nr_of_vmgm_subp_streams; /* 1 */
+ *current = 0x41;
+ break;
+ }
+}
+#endif
+
+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;
+ }
+}
+
+int vm_get_video_aspect(vm_t *vm) {
+ int aspect = vm_get_video_attr(vm).display_aspect_ratio;
+
+ if(aspect != 0 && aspect != 3) {
+ Log1(vm, "display aspect ratio is unexpected: %d!", aspect);
+ return -1;
+ }
+
+ (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 DVD_DOMAIN_VTSTitle:
+ return vm->vtsi->vtsi_mat->vts_video_attr;
+ case DVD_DOMAIN_VTSMenu:
+ return vm->vtsi->vtsi_mat->vtsm_video_attr;
+ case DVD_DOMAIN_VMGM:
+ case DVD_DOMAIN_FirstPlay:
+ return vm->vmgi->vmgi_mat->vmgm_video_attr;
+ default:
+ assert(0);
+ }
+}
+
+audio_attr_t vm_get_audio_attr(vm_t *vm, int streamN) {
+ switch ((vm->state).domain) {
+ case DVD_DOMAIN_VTSTitle:
+ return vm->vtsi->vtsi_mat->vts_audio_attr[streamN];
+ case DVD_DOMAIN_VTSMenu:
+ return vm->vtsi->vtsi_mat->vtsm_audio_attr;
+ case DVD_DOMAIN_VMGM:
+ case DVD_DOMAIN_FirstPlay:
+ return vm->vmgi->vmgi_mat->vmgm_audio_attr;
+ default:
+ assert(0);
+ }
+}
+
+subp_attr_t vm_get_subp_attr(vm_t *vm, int streamN) {
+ switch ((vm->state).domain) {
+ case DVD_DOMAIN_VTSTitle:
+ return vm->vtsi->vtsi_mat->vts_subp_attr[streamN];
+ case DVD_DOMAIN_VTSMenu:
+ return vm->vtsi->vtsi_mat->vtsm_subp_attr;
+ case DVD_DOMAIN_VMGM:
+ case DVD_DOMAIN_FirstPlay:
+ return vm->vmgi->vmgi_mat->vmgm_subp_attr;
+ default:
+ assert(0);
+ }
+}