summaryrefslogtreecommitdiff
path: root/contrib/libvcd/info.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/libvcd/info.c')
-rw-r--r--contrib/libvcd/info.c2103
1 files changed, 2103 insertions, 0 deletions
diff --git a/contrib/libvcd/info.c b/contrib/libvcd/info.c
new file mode 100644
index 000000000..b01bd6eee
--- /dev/null
+++ b/contrib/libvcd/info.c
@@ -0,0 +1,2103 @@
+/*
+ $Id: info.c,v 1.8 2007/03/23 21:47:31 dsalt Exp $
+
+ Copyright (C) 2002, 2003, 2004 Rocky Bernstein <rocky@panix.com>
+
+ This program 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.
+
+ 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Foundation
+ Software, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+/*
+ Things here refer to higher-level structures usually accessed via
+ vcdinfo_t. For lower-level access which generally use
+ structures other than vcdinfo_t, see inf.c
+*/
+
+
+/* Private headers */
+#include "info_private.h"
+#include "vcd_assert.h"
+#include "pbc.h"
+#include "util.h"
+#include "vcd_read.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <cdio/cdio.h>
+#include <cdio/bytesex.h>
+#include <cdio/cd_types.h>
+#include <cdio/util.h>
+
+/* Eventually move above libvcd includes but having vcdinfo including. */
+#include <libvcd/info.h>
+
+#include <stdio.h>
+#include <stddef.h>
+#include <errno.h>
+
+static const char _rcsid[] = "$Id: info.c,v 1.8 2007/03/23 21:47:31 dsalt Exp $";
+
+#define BUF_COUNT 16
+#define BUF_SIZE 80
+
+/* Return a pointer to a internal free buffer */
+static char *
+_getbuf (void)
+{
+ static char _buf[BUF_COUNT][BUF_SIZE];
+ static int _num = -1;
+
+ _num++;
+ _num %= BUF_COUNT;
+
+ memset (_buf[_num], 0, BUF_SIZE);
+
+ return _buf[_num];
+}
+
+/*
+ Initialize/allocate segment portion of vcdinfo_obj_t.
+
+ Getting exact segments sizes is done in a rather complicated way.
+ A simple approach would be to use the fixed size allocated on disk,
+ but players have trouble with the internal fragmentation padding.
+ More accurate results are obtained by consulting with ISO 9660
+ information for the corresponding file entry.
+
+ Another approach to get segment sizes is to read/scan the
+ MPEGs. That would be rather slow.
+*/
+static void
+_init_segments (vcdinfo_obj_t *obj)
+{
+ InfoVcd_t *info = vcdinfo_get_infoVcd(obj);
+ segnum_t num_segments = vcdinfo_get_num_segments(obj);
+ CdioListNode *entnode;
+ CdioList *entlist;
+ int i;
+ lsn_t last_lsn=0;
+
+ obj->first_segment_lsn = cdio_msf_to_lsn(&info->first_seg_addr);
+ obj->seg_sizes = _vcd_malloc( num_segments * sizeof(uint32_t *));
+
+ if (NULL == obj->seg_sizes || 0 == num_segments) return;
+
+ entlist = iso9660_fs_readdir(obj->img, "SEGMENT", true);
+
+ i=0;
+ _CDIO_LIST_FOREACH (entnode, entlist) {
+ iso9660_stat_t *statbuf = _cdio_list_node_data (entnode);
+
+ if (statbuf->type == _STAT_DIR) continue;
+
+ while(info->spi_contents[i].item_cont) {
+ obj->seg_sizes[i] = VCDINFO_SEGMENT_SECTOR_SIZE;
+ i++;
+ }
+
+ /* Should have an entry in the ISO 9660 filesystem. Get and save
+ in statbuf.secsize this size.
+ */
+ obj->seg_sizes[i] = statbuf->secsize;
+
+ if (last_lsn >= statbuf->lsn)
+ vcd_warn ("Segments if ISO 9660 directory out of order lsn %ul >= %ul",
+ (unsigned int) last_lsn, (unsigned int) statbuf->lsn);
+ last_lsn = statbuf->lsn;
+
+ i++;
+ }
+
+ while(i < num_segments && info->spi_contents[i].item_cont) {
+ obj->seg_sizes[i] = VCDINFO_SEGMENT_SECTOR_SIZE;
+ i++;
+ }
+
+ if (i != num_segments)
+ vcd_warn ("Number of segments found %d is not number of segments %d",
+ i, num_segments);
+
+ _cdio_list_free (entlist, true);
+
+
+#if 0
+ /* Figure all of the segment sector sizes */
+ for (i=0; i < num_segments; i++) {
+
+ obj->seg_sizes[i] = VCDINFO_SEGMENT_SECTOR_SIZE;
+
+ if ( !info->spi_contents[i].item_cont ) {
+ /* Should have an entry in the ISO 9660 filesystem. Get and save
+ in statbuf.secsize this size.
+ */
+ lsn_t lsn = vcdinfo_get_seg_lsn(obj, i);
+ iso9660_stat_t *statbuf =iso9660_find_fs_lsn(obj->img, lsn);
+ if (NULL != statbuf) {
+ obj->seg_sizes[i] = statbuf->secsize;
+ free(statbuf);
+ } else {
+ vcd_warn ("Trouble finding ISO 9660 size for segment %d.", i);
+ }
+ }
+ }
+#endif
+
+}
+
+/*!
+ Return the number of audio channels implied by "audio_type".
+ 0 is returned on error.
+*/
+unsigned int
+vcdinfo_audio_type_num_channels(const vcdinfo_obj_t *obj,
+ unsigned int audio_type)
+{
+ const int audio_types[2][5] =
+ {
+ { /* VCD 2.0 */
+ 0, /* no audio*/
+ 1, /* single channel */
+ 1, /* stereo */
+ 2, /* dual channel */
+ 0}, /* error */
+
+ { /* SVCD, HQVCD */
+ 0, /* no stream */
+ 1, /* 1 stream */
+ 2, /* 2 streams */
+ 1, /* 1 multi-channel stream (surround sound) */
+ 0} /* error */
+ };
+
+ /* We should also check that the second index is in range too. */
+ if (audio_type > 4) {
+ return 0;
+ }
+
+ /* Get first index entry into above audio_type array from vcd_type */
+ switch (obj->vcd_type) {
+
+ case VCD_TYPE_VCD:
+ case VCD_TYPE_VCD11:
+ return 1;
+
+ case VCD_TYPE_VCD2:
+ return 3;
+ break;
+
+ case VCD_TYPE_HQVCD:
+ case VCD_TYPE_SVCD:
+ return audio_types[1][audio_type];
+ break;
+
+ case VCD_TYPE_INVALID:
+ default:
+ /* We have an invalid entry. Set to handle below. */
+ return 0;
+ }
+}
+
+/*!
+ Return a string describing an audio type.
+*/
+const char *
+vcdinfo_audio_type2str(const vcdinfo_obj_t *obj, unsigned int audio_type)
+{
+ const char *audio_types[3][5] =
+ {
+ /* INVALID, VCD 1.0, or VCD 1.1 */
+ { "unknown", "invalid", "", "", "" },
+
+ /*VCD 2.0 */
+ { "no audio", "single channel", "stereo", "dual channel", "error" },
+
+ /* SVCD, HQVCD */
+ { "no stream", "1 stream", "2 streams",
+ "1 multi-channel stream (surround sound)", "error"},
+ };
+
+ unsigned int first_index = 0;
+
+ /* Get first index entry into above audio_type array from vcd_type */
+ switch (obj->vcd_type) {
+
+ case VCD_TYPE_VCD:
+ case VCD_TYPE_VCD11:
+ case VCD_TYPE_VCD2:
+ first_index=1;
+ break;
+
+ case VCD_TYPE_HQVCD:
+ case VCD_TYPE_SVCD:
+ first_index=2;
+ break;
+
+ case VCD_TYPE_INVALID:
+ default:
+ /* We have an invalid entry. Set to handle below. */
+ audio_type=4;
+ }
+
+ /* We should also check that the second index is in range too. */
+ if (audio_type > 3) {
+ first_index=0;
+ audio_type=1;
+ }
+
+ return audio_types[first_index][audio_type];
+}
+
+/*!
+ Note first seg_num is 0!
+*/
+const char *
+vcdinfo_ogt2str(const vcdinfo_obj_t *obj, segnum_t seg_num)
+{
+ const InfoVcd_t *info = &obj->info;
+ const char *ogt_str[] =
+ {
+ "None",
+ "1 available",
+ "0 & 1 available",
+ "all 4 available"
+ };
+
+ return ogt_str[info->spi_contents[seg_num].ogt];
+}
+
+
+const char *
+vcdinfo_video_type2str(const vcdinfo_obj_t *obj, segnum_t seg_num)
+{
+ const char *video_types[] =
+ {
+ "no stream",
+ "NTSC still",
+ "NTSC still (lo+hires)",
+ "NTSC motion",
+ "reserved (0x4)",
+ "PAL still",
+ "PAL still (lo+hires)",
+ "PAL motion",
+ "INVALID ENTRY"
+ };
+
+ return video_types[vcdinfo_get_video_type(obj, seg_num)];
+}
+
+/*!
+ \brief Classify itemid_num into the kind of item it is: track #, entry #,
+ segment #.
+ \param itemid is set to contain this classification an the converted
+ entry number.
+*/
+void
+vcdinfo_classify_itemid (uint16_t itemid_num,
+ /*out*/ vcdinfo_itemid_t *itemid)
+{
+
+ itemid->num = itemid_num;
+ if (itemid_num < 2)
+ itemid->type = VCDINFO_ITEM_TYPE_NOTFOUND;
+ else if (itemid_num < MIN_ENCODED_TRACK_NUM) {
+ itemid->type = VCDINFO_ITEM_TYPE_TRACK;
+ itemid->num--;
+ } else if (itemid_num < 600) {
+ itemid->type = VCDINFO_ITEM_TYPE_ENTRY;
+ itemid->num -= MIN_ENCODED_TRACK_NUM;
+ } else if (itemid_num < MIN_ENCODED_SEGMENT_NUM)
+ itemid->type = VCDINFO_ITEM_TYPE_LID;
+ else if (itemid_num <= MAX_ENCODED_SEGMENT_NUM) {
+ itemid->type = VCDINFO_ITEM_TYPE_SEGMENT;
+ itemid->num -= (MIN_ENCODED_SEGMENT_NUM);
+ } else
+ itemid->type = VCDINFO_ITEM_TYPE_SPAREID2;
+}
+
+const char *
+vcdinfo_pin2str (uint16_t itemid_num)
+{
+ char *buf = _getbuf ();
+ vcdinfo_itemid_t itemid;
+
+ vcdinfo_classify_itemid(itemid_num, &itemid);
+ strcpy (buf, "??");
+
+ switch(itemid.type) {
+ case VCDINFO_ITEM_TYPE_NOTFOUND:
+ snprintf (buf, BUF_SIZE, "play nothing (0x%4.4x)", itemid.num);
+ break;
+ case VCDINFO_ITEM_TYPE_TRACK:
+ snprintf (buf, BUF_SIZE, "SEQUENCE[%d] (0x%4.4x)", itemid.num-1,
+ itemid_num);
+ break;
+ case VCDINFO_ITEM_TYPE_ENTRY:
+ snprintf (buf, BUF_SIZE, "ENTRY[%d] (0x%4.4x)", itemid.num, itemid_num);
+ break;
+ case VCDINFO_ITEM_TYPE_SEGMENT:
+ snprintf (buf, BUF_SIZE, "SEGMENT[%d] (0x%4.4x)", itemid.num, itemid_num);
+ break;
+ case VCDINFO_ITEM_TYPE_LID:
+ snprintf (buf, BUF_SIZE, "spare id (0x%4.4x)", itemid.num);
+ break;
+ case VCDINFO_ITEM_TYPE_SPAREID2:
+ snprintf (buf, BUF_SIZE, "spare id2 (0x%4.4x)", itemid.num);
+ break;
+ }
+
+ return buf;
+}
+
+/*!
+ Return a string containing the VCD album id, or NULL if there is
+ some problem in getting this.
+*/
+const char *
+vcdinfo_get_album_id(const vcdinfo_obj_t *obj)
+{
+ if ( NULL == obj ) return (NULL);
+ return vcdinf_get_album_id(&obj->info);
+}
+
+/*!
+ Return the VCD ID.
+ NULL is returned if there is some problem in getting this.
+*/
+char *
+vcdinfo_get_application_id(vcdinfo_obj_t *p_obj)
+{
+ if ( NULL == p_obj ) return (NULL);
+ return iso9660_get_application_id(&p_obj->pvd);
+}
+
+/*!
+ Return a pointer to the cdio structure for the CD image opened or
+ NULL if error.
+*/
+CdIo *
+vcdinfo_get_cd_image (const vcdinfo_obj_t *vcd_obj)
+{
+ if (NULL == vcd_obj) return NULL;
+ return vcd_obj->img;
+}
+
+
+/*!
+ \fn vcdinfo_selection_get_lid(const vcdinfo_obj_t *obj, lid_t lid,
+ unsigned int selection);
+
+ \brief Get offset of a selection for a given lid.
+
+ Return the LID offset associated with a the selection number of the
+ passed-in LID parameter.
+
+ \return VCDINFO_INVALID_LID is returned if obj on error or obj
+ is NULL. Otherwise the LID offset is returned.
+*/
+lid_t vcdinfo_selection_get_lid(const vcdinfo_obj_t *obj, lid_t lid,
+ unsigned int selection)
+{
+ unsigned int offset;
+
+ if (NULL == obj) return VCDINFO_INVALID_LID;
+
+ offset = vcdinfo_selection_get_offset(obj, lid, selection);
+ switch (offset) {
+ case VCDINFO_INVALID_OFFSET:
+ case PSD_OFS_MULTI_DEF:
+ case PSD_OFS_MULTI_DEF_NO_NUM:
+ return VCDINFO_INVALID_LID;
+ default:
+ {
+ vcdinfo_offset_t *ofs = vcdinfo_get_offset_t(obj, offset);
+ return ofs->lid;
+ }
+ }
+}
+
+/*!
+ \fn vcdinfo_selection_get_offset(const vcdinfo_obj_t *obj, lid_t lid,
+ unsigned int selection);
+
+ \brief Get offset of a selection for a given lid.
+
+ Return the LID offset associated with a the selection number of the
+ passed-in LID parameter.
+
+ \return VCDINFO_INVALID_OFFSET is returned if error, obj is NULL or
+ the lid is not some type of selection list. Otherwise the LID offset
+ is returned.
+*/
+uint16_t vcdinfo_selection_get_offset(const vcdinfo_obj_t *obj, lid_t lid,
+ unsigned int selection)
+{
+ unsigned int bsn;
+
+ PsdListDescriptor_t pxd;
+ vcdinfo_lid_get_pxd(obj, &pxd, lid);
+ if (pxd.descriptor_type != PSD_TYPE_SELECTION_LIST &&
+ pxd.descriptor_type != PSD_TYPE_EXT_SELECTION_LIST) {
+ vcd_warn( "Requesting selection of LID %i which not a selection list -"
+ " type is 0x%x",
+ lid, pxd.descriptor_type );
+ return VCDINFO_INVALID_OFFSET;
+ }
+
+ bsn=vcdinf_get_bsn(pxd.psd);
+
+ if ( (selection - bsn + 1) > 0) {
+ return vcdinfo_lid_get_offset(obj, lid, selection-bsn+1);
+ } else {
+ vcd_warn( "Selection number %u too small. bsn %u", selection, bsn );
+ return VCDINFO_INVALID_OFFSET;
+ }
+}
+
+/**
+ \fn vcdinfo_get_default_offset(const vcdinfo_obj_t *obj, unsinged int lid);
+ \brief Get return offset for a given PLD selector descriptor.
+ \return VCDINFO_INVALID_OFFSET is returned on error or if pld has no
+ "return" entry or pld is NULL. Otherwise the LID offset is returned.
+ */
+uint16_t
+vcdinfo_get_default_offset(const vcdinfo_obj_t *obj, lid_t lid)
+{
+ if (NULL != obj) {
+
+ PsdListDescriptor_t pxd;
+
+ vcdinfo_lid_get_pxd(obj, &pxd, lid);
+
+ switch (pxd.descriptor_type) {
+ case PSD_TYPE_EXT_SELECTION_LIST:
+ case PSD_TYPE_SELECTION_LIST:
+ return vcdinf_psd_get_default_offset(pxd.psd);
+ break;
+ case PSD_TYPE_PLAY_LIST:
+ case PSD_TYPE_END_LIST:
+ case PSD_TYPE_COMMAND_LIST:
+ break;
+ }
+ }
+ return VCDINFO_INVALID_OFFSET;
+}
+
+/*!
+ \brief Get default or multi-default LID.
+
+ Return the LID offset associated with a the "default" entry of the
+ passed-in LID parameter. Note "default" entries are associated
+ with PSDs that are (extended) selection lists. If the "default"
+ offset is a multi-default, we use entry_num to find the proper
+ "default" LID. Otherwise this routine is exactly like
+ vcdinfo_get_default_lid with the exception of requiring an
+ additional "entry_num" parameter.
+
+ \return VCDINFO_INVALID_LID is returned on error, or if the LID
+ is not a selection list or no "default" entry. Otherwise the LID
+ offset is returned.
+*/
+lid_t
+vcdinfo_get_multi_default_lid(const vcdinfo_obj_t *obj, lid_t lid,
+ lsn_t lsn)
+{
+ unsigned int offset;
+ unsigned int entry_num;
+
+ entry_num = vcdinfo_lsn_get_entry(obj, lsn);
+ offset = vcdinfo_get_multi_default_offset(obj, lid, entry_num);
+
+ switch (offset) {
+ case VCDINFO_INVALID_OFFSET:
+ case PSD_OFS_MULTI_DEF:
+ case PSD_OFS_MULTI_DEF_NO_NUM:
+ return VCDINFO_INVALID_LID;
+ default:
+ {
+ vcdinfo_offset_t *ofs = vcdinfo_get_offset_t(obj, offset);
+ return ofs->lid;
+ }
+ }
+}
+
+/*!
+ \brief Get default or multi-default LID offset.
+
+ Return the LID offset associated with a the "default" entry of the
+ passed-in LID parameter. Note "default" entries are associated
+ with PSDs that are (extended) selection lists. If the "default"
+ offset is a multi-default, we use entry_num to find the proper
+ "default" offset. Otherwise this routine is exactly like
+ vcdinfo_get_default_offset with the exception of requiring an
+ additional "entry_num" parameter.
+
+ \return VCDINFO_INVALID_OFFSET is returned on error, or if the LID
+ is not a selection list or no "default" entry. Otherwise the LID
+ offset is returned.
+*/
+uint16_t
+vcdinfo_get_multi_default_offset(const vcdinfo_obj_t *obj, lid_t lid,
+ unsigned int entry_num)
+{
+ uint16_t offset=vcdinfo_get_default_offset(obj, lid);
+
+ switch (offset) {
+ case PSD_OFS_MULTI_DEF:
+ case PSD_OFS_MULTI_DEF_NO_NUM:
+ {
+ /* Have some work todo... Figure the selection number. */
+ PsdListDescriptor_t pxd;
+
+ vcdinfo_lid_get_pxd(obj, &pxd, lid);
+
+ switch (pxd.descriptor_type) {
+
+ case PSD_TYPE_SELECTION_LIST:
+ case PSD_TYPE_EXT_SELECTION_LIST: {
+ vcdinfo_itemid_t selection_itemid;
+ uint16_t selection_itemid_num;
+ unsigned int start_entry_num;
+
+ if (pxd.psd == NULL) return VCDINFO_INVALID_OFFSET;
+ selection_itemid_num = vcdinf_psd_get_itemid(pxd.psd);
+ vcdinfo_classify_itemid(selection_itemid_num, &selection_itemid);
+ if (selection_itemid.type != VCDINFO_ITEM_TYPE_TRACK) {
+ return VCDINFO_INVALID_OFFSET;
+ }
+
+ start_entry_num = vcdinfo_track_get_entry(obj, selection_itemid.num);
+ return vcdinfo_selection_get_offset(obj, lid,
+ entry_num-start_entry_num);
+ }
+ default: ;
+ }
+ }
+ default:
+ return VCDINFO_INVALID_OFFSET;
+ }
+}
+
+/*!
+ Return a string containing the default VCD device if none is specified.
+ Return NULL we can't get this information.
+*/
+char *
+vcdinfo_get_default_device (const vcdinfo_obj_t *vcd_obj)
+{
+
+ /* If device not already open, then we'll open it temporarily and
+ let CdIo select a driver, get the default for that and then
+ close/destroy the temporary we created.
+ */
+ CdIo *cdio=NULL;
+ if (vcd_obj != NULL && vcd_obj->img != NULL)
+ cdio = vcd_obj->img;
+
+ return cdio_get_default_device(cdio);
+}
+
+/*!
+ Return number of sector units in of an entry. 0 is returned if entry_num
+ is out of range.
+ The first entry number is 0.
+*/
+uint32_t
+vcdinfo_get_entry_sect_count (const vcdinfo_obj_t *obj, unsigned int entry_num)
+{
+ const EntriesVcd_t *entries = &obj->entries;
+ const unsigned int entry_count = vcdinf_get_num_entries(entries);
+ if (entry_num > entry_count)
+ return 0;
+ else {
+ const lsn_t this_lsn = vcdinfo_get_entry_lsn(obj, entry_num);
+ lsn_t next_lsn;
+ if (entry_num < entry_count-1) {
+ track_t track=vcdinfo_get_track(obj, entry_num);
+ track_t next_track=vcdinfo_get_track(obj, entry_num+1);
+ next_lsn = vcdinfo_get_entry_lsn(obj, entry_num+1);
+ /* If we've changed tracks, don't include pregap sector between
+ tracks.
+ */
+ if (track != next_track) next_lsn -= CDIO_PREGAP_SECTORS;
+ } else {
+ /* entry_num == entry_count -1. Or the last entry.
+ This is really really ugly. There's probably a better
+ way to do it.
+ Below we get the track of the current entry and then the LBA of the
+ beginning of the following (leadout?) track.
+
+ Wait! It's uglier than that! Since VCD's can be created
+ *without* a pregap to the leadout track, we try not to use
+ that if we can get the entry from the ISO 9660 filesystem.
+ */
+ track_t track = vcdinfo_get_track(obj, entry_num);
+ if (track != VCDINFO_INVALID_TRACK) {
+ iso9660_stat_t *statbuf;
+ const lsn_t lsn = vcdinfo_get_track_lsn(obj, track);
+
+ /* Try to get the sector count from the ISO 9660 filesystem */
+ statbuf = iso9660_find_fs_lsn(obj->img, lsn);
+
+ if (NULL != statbuf) {
+ next_lsn = lsn + statbuf->secsize;
+ free(statbuf);
+ } else {
+ /* Failed on ISO 9660 filesystem. Use next track or
+ LEADOUT track. */
+ next_lsn = vcdinfo_get_track_lsn(obj, track+1);
+ }
+ if (next_lsn == VCDINFO_NULL_LSN)
+ return 0;
+ } else {
+ /* Something went wrong. Set up size to zero. */
+ return 0;
+ }
+ }
+ return (next_lsn - this_lsn);
+ }
+}
+
+/*! Return the starting MSF (minutes/secs/frames) for sequence
+ entry_num in obj. NULL is returned if there is no entry.
+ The first entry number is 0.
+*/
+const msf_t *
+vcdinfo_get_entry_msf(const vcdinfo_obj_t *obj, unsigned int entry_num)
+{
+ const EntriesVcd_t *entries = &obj->entries;
+ return vcdinf_get_entry_msf(entries, entry_num);
+}
+
+/*! Return the starting LBA (logical block address) for sequence
+ entry_num in obj. VCDINFO_NULL_LBA is returned if there is no entry.
+*/
+lba_t
+vcdinfo_get_entry_lba(const vcdinfo_obj_t *obj, unsigned int entry_num)
+{
+ if ( NULL == obj ) return VCDINFO_NULL_LBA;
+ else {
+ const msf_t *msf = vcdinfo_get_entry_msf(obj, entry_num);
+ msf = vcdinfo_get_entry_msf(obj, entry_num);
+ return (msf != NULL) ? cdio_msf_to_lba(msf) : VCDINFO_NULL_LBA;
+ }
+}
+
+/*! Return the starting LBA (logical block address) for sequence
+ entry_num in obj. VCDINFO_NULL_LBA is returned if there is no entry.
+*/
+lsn_t
+vcdinfo_get_entry_lsn(const vcdinfo_obj_t *obj, unsigned int entry_num)
+{
+ if ( NULL == obj ) return VCDINFO_NULL_LBA;
+ else {
+ const msf_t *msf = vcdinfo_get_entry_msf(obj, entry_num);
+ return (msf != NULL) ? cdio_msf_to_lsn(msf) : VCDINFO_NULL_LSN;
+ }
+}
+
+EntriesVcd_t *
+vcdinfo_get_entriesVcd (vcdinfo_obj_t *obj)
+{
+ if (NULL == obj) return NULL;
+ return &obj->entries;
+}
+
+/*!
+ Get the VCD format (VCD 1.0 VCD 1.1, SVCD, ... for this object.
+ The type is also set inside obj.
+*/
+vcd_type_t
+vcdinfo_get_format_version (vcdinfo_obj_t *obj)
+{
+ return obj->vcd_type;
+}
+
+/*!
+ Return a string giving VCD format (VCD 1.0 VCD 1.1, SVCD, ...
+ for this object.
+*/
+const char *
+vcdinfo_get_format_version_str (const vcdinfo_obj_t *obj)
+{
+ if (NULL == obj) return "*Uninitialized*";
+ return vcdinf_get_format_version_str(obj->vcd_type);
+}
+
+InfoVcd_t *
+vcdinfo_get_infoVcd (vcdinfo_obj_t *obj)
+{
+ if (NULL == obj) return NULL;
+ return &obj->info;
+}
+
+/*! Return the entry number closest and before the given LSN.
+ */
+unsigned int
+vcdinfo_lsn_get_entry(const vcdinfo_obj_t *obj, lsn_t lsn)
+{
+
+ /* Do a binary search to find the entry. */
+ unsigned int i = 0;
+ unsigned int j = vcdinfo_get_num_entries(obj);
+ unsigned int mid;
+ unsigned int mid_lsn;
+ do {
+ mid = (i+j)/2;
+ mid_lsn = vcdinfo_get_entry_lsn(obj, mid);
+ if ( lsn <= mid_lsn ) j = mid-1;
+ if ( lsn >= mid_lsn ) i = mid+1;
+ } while (i <= j);
+
+ /* We want the entry closest but before. */
+ return (lsn == mid_lsn) ? mid : mid-1;
+}
+
+
+void *
+vcdinfo_get_pvd (vcdinfo_obj_t *obj)
+{
+ if (NULL == obj) return NULL;
+ return &obj->pvd;
+}
+
+void *
+vcdinfo_get_scandata (vcdinfo_obj_t *obj)
+{
+ if (NULL == obj) return NULL;
+ return obj->scandata_buf;
+}
+
+void *
+vcdinfo_get_searchDat (vcdinfo_obj_t *obj)
+{
+ if (NULL == obj) return NULL;
+ return obj->search_buf;
+}
+
+void *
+vcdinfo_get_tracksSVD (vcdinfo_obj_t *obj)
+{
+ if (NULL == obj) return NULL;
+ return obj->tracks_buf;
+}
+
+/*!
+ Get the itemid for a given list ID.
+ VCDINFO_REJECTED_MASK is returned on error or if obj is NULL.
+*/
+uint16_t
+vcdinfo_lid_get_itemid(const vcdinfo_obj_t *obj, lid_t lid)
+{
+ PsdListDescriptor_t pxd;
+
+ if (obj == NULL) return VCDINFO_REJECTED_MASK;
+ vcdinfo_lid_get_pxd(obj, &pxd, lid);
+ switch (pxd.descriptor_type) {
+ case PSD_TYPE_SELECTION_LIST:
+ case PSD_TYPE_EXT_SELECTION_LIST:
+ if (pxd.psd == NULL) return VCDINFO_REJECTED_MASK;
+ return vcdinf_psd_get_itemid(pxd.psd);
+ break;
+ case PSD_TYPE_PLAY_LIST:
+ /* FIXME: There is an array of items */
+ case PSD_TYPE_END_LIST:
+ case PSD_TYPE_COMMAND_LIST:
+ return VCDINFO_REJECTED_MASK;
+ }
+
+ return VCDINFO_REJECTED_MASK;
+
+}
+
+/*!
+ Get the LOT pointer.
+*/
+LotVcd_t *
+vcdinfo_get_lot(const vcdinfo_obj_t *obj)
+{
+ if (NULL == obj) return NULL;
+ return obj->lot;
+}
+
+/*!
+ Get the extended LOT pointer.
+*/
+LotVcd_t *
+vcdinfo_get_lot_x(const vcdinfo_obj_t *obj)
+{
+ if (NULL == obj) return NULL;
+ return obj->lot_x;
+}
+
+/*!
+ Return number of LIDs.
+*/
+lid_t
+vcdinfo_get_num_LIDs (const vcdinfo_obj_t *obj)
+{
+ /* Should probably use _vcd_pbc_max_lid instead? */
+ if (NULL==obj) return 0;
+ return vcdinf_get_num_LIDs(&obj->info);
+}
+
+/*!
+ Return the number of entries in the VCD.
+*/
+unsigned int
+vcdinfo_get_num_entries(const vcdinfo_obj_t *obj)
+{
+ const EntriesVcd_t *entries = &obj->entries;
+ return vcdinf_get_num_entries(entries);
+}
+
+/*!
+ Return the number of segments in the VCD. Return 0 if there is some
+ problem.
+*/
+segnum_t
+vcdinfo_get_num_segments(const vcdinfo_obj_t *obj)
+{
+ if (NULL==obj) return 0;
+ return vcdinf_get_num_segments(&obj->info);
+}
+
+/*!
+ \fn vcdinfo_get_offset_lid(const vcdinfo_obj_t *obj, unsigned int entry_num);
+ \brief Get offset entry_num for a given LID.
+ \return VCDINFO_INVALID_OFFSET is returned if obj on error or obj
+ is NULL. Otherwise the LID offset is returned.
+*/
+uint16_t
+vcdinfo_lid_get_offset(const vcdinfo_obj_t *obj, lid_t lid,
+ unsigned int entry_num)
+{
+ PsdListDescriptor_t pxd;
+
+ if (obj == NULL) return VCDINFO_INVALID_OFFSET;
+ vcdinfo_lid_get_pxd(obj, &pxd, lid);
+
+ switch (pxd.descriptor_type) {
+ case PSD_TYPE_SELECTION_LIST:
+ case PSD_TYPE_EXT_SELECTION_LIST:
+ if (pxd.psd == NULL) return VCDINFO_INVALID_OFFSET;
+ return vcdinf_psd_get_offset(pxd.psd, entry_num-1);
+ break;
+ case PSD_TYPE_PLAY_LIST:
+ /* FIXME: There is an array of items */
+ case PSD_TYPE_END_LIST:
+ case PSD_TYPE_COMMAND_LIST:
+ return VCDINFO_INVALID_OFFSET;
+ }
+ return VCDINFO_INVALID_OFFSET;
+
+}
+
+/*!
+ NULL is returned on error.
+*/
+static vcdinfo_offset_t *
+_vcdinfo_get_offset_t (const vcdinfo_obj_t *obj, unsigned int offset, bool ext)
+{
+ CdioListNode *node;
+ CdioList *offset_list = ext ? obj->offset_x_list : obj->offset_list;
+
+ switch (offset) {
+ case PSD_OFS_DISABLED:
+ case PSD_OFS_MULTI_DEF:
+ case PSD_OFS_MULTI_DEF_NO_NUM:
+ return NULL;
+ default: ;
+ }
+
+ _CDIO_LIST_FOREACH (node, offset_list)
+ {
+ vcdinfo_offset_t *ofs = _cdio_list_node_data (node);
+ if (offset == ofs->offset)
+ return ofs;
+ }
+ return NULL;
+}
+
+/*!
+ Get the VCD info list.
+*/
+CdioList *
+vcdinfo_get_offset_list(const vcdinfo_obj_t *obj)
+{
+ if (NULL == obj) return NULL;
+ return obj->offset_list;
+}
+
+
+/*!
+ Get the VCD info extended offset list.
+*/
+CdioList *
+vcdinfo_get_offset_x_list(const vcdinfo_obj_t *obj)
+{
+ if (NULL == obj) return NULL;
+ return obj->offset_x_list;
+}
+
+/*!
+ Get the VCD info offset multiplier.
+*/
+unsigned int vcdinfo_get_offset_mult(const vcdinfo_obj_t *obj)
+{
+ if (NULL == obj) return 0xFFFF;
+ return obj->info.offset_mult;
+}
+
+/*!
+ Get entry in offset list for the item that has offset. This entry
+ has for example the LID. NULL is returned on error.
+*/
+vcdinfo_offset_t *
+vcdinfo_get_offset_t (const vcdinfo_obj_t *obj, unsigned int offset)
+{
+ vcdinfo_offset_t *off_p= _vcdinfo_get_offset_t (obj, offset, true);
+ if (NULL != off_p)
+ return off_p;
+ return _vcdinfo_get_offset_t (obj, offset, false);
+}
+
+/*!
+ Return a string containing the VCD publisher id with trailing
+ blanks removed, or NULL if there is some problem in getting this.
+*/
+const char *
+vcdinfo_get_preparer_id(const vcdinfo_obj_t *obj)
+{
+ if ( NULL == obj ) return (NULL);
+ return iso9660_get_preparer_id(&obj->pvd);
+}
+
+/*!
+ Get the PSD.
+*/
+uint8_t *
+vcdinfo_get_psd(const vcdinfo_obj_t *obj)
+{
+ if ( NULL == obj ) return (NULL);
+ return obj->psd;
+}
+
+/*!
+ Get the extended PSD.
+*/
+uint8_t *
+vcdinfo_get_psd_x(const vcdinfo_obj_t *obj)
+{
+ if ( NULL == obj ) return (NULL);
+ return obj->psd_x;
+}
+
+/*!
+ Return number of bytes in PSD. Return 0 if there's an error.
+*/
+uint32_t
+vcdinfo_get_psd_size (const vcdinfo_obj_t *obj)
+{
+ if ( NULL == obj ) return 0;
+ return vcdinf_get_psd_size(&obj->info);
+}
+
+/*!
+ Return number of bytes in the extended PSD. Return 0 if there's an error.
+*/
+uint32_t
+vcdinfo_get_psd_x_size (const vcdinfo_obj_t *obj)
+{
+ if ( NULL == obj ) return 0;
+ return obj->psd_x_size;
+}
+
+/*!
+ Return a string containing the VCD publisher id with trailing
+ blanks removed, or NULL if there is some problem in getting this.
+*/
+char *
+vcdinfo_get_publisher_id(const vcdinfo_obj_t *obj)
+{
+ if ( NULL == obj ) return (NULL);
+ return iso9660_get_publisher_id(&obj->pvd);
+}
+
+/*!
+ Get the PSD Selection List Descriptor for a given lid.
+ NULL is returned if error or not found.
+*/
+static bool
+_vcdinfo_lid_get_pxd(const vcdinfo_obj_t *obj, PsdListDescriptor_t *pxd,
+ uint16_t lid, bool ext)
+{
+ CdioListNode *node;
+ unsigned mult = obj->info.offset_mult;
+ const uint8_t *psd = ext ? obj->psd_x : obj->psd;
+ CdioList *offset_list = ext ? obj->offset_x_list : obj->offset_list;
+
+ if (offset_list == NULL) return false;
+
+ _CDIO_LIST_FOREACH (node, offset_list)
+ {
+ vcdinfo_offset_t *ofs = _cdio_list_node_data (node);
+ unsigned _rofs = ofs->offset * mult;
+
+ pxd->descriptor_type = psd[_rofs];
+
+ switch (pxd->descriptor_type)
+ {
+ case PSD_TYPE_PLAY_LIST:
+ {
+ pxd->pld = (PsdPlayListDescriptor_t *) (psd + _rofs);
+ if (vcdinf_pld_get_lid(pxd->pld) == lid) {
+ return true;
+ }
+ break;
+ }
+
+ case PSD_TYPE_EXT_SELECTION_LIST:
+ case PSD_TYPE_SELECTION_LIST:
+ {
+ pxd->psd = (PsdSelectionListDescriptor_t *) (psd + _rofs);
+ if (vcdinf_psd_get_lid(pxd->psd) == lid) {
+ return true;
+ }
+ break;
+ }
+ default: ;
+ }
+ }
+ return false;
+}
+
+/*!
+ Get the PSD Selection List Descriptor for a given lid.
+ False is returned if not found.
+*/
+bool
+vcdinfo_lid_get_pxd(const vcdinfo_obj_t *obj, PsdListDescriptor_t *pxd,
+ uint16_t lid)
+{
+ if (_vcdinfo_lid_get_pxd(obj, pxd, lid, true))
+ return true;
+ return _vcdinfo_lid_get_pxd(obj, pxd, lid, false);
+}
+
+/**
+ \fn vcdinfo_get_return_offset(const vcdinfo_obj_t *obj);
+ \brief Get return offset for a given LID.
+ \return VCDINFO_INVALID_OFFSET is returned on error or if LID has no
+ "return" entry. Otherwise the LID offset is returned.
+ */
+uint16_t
+vcdinfo_get_return_offset(const vcdinfo_obj_t *obj, lid_t lid)
+{
+ if (NULL != obj) {
+
+ PsdListDescriptor_t pxd;
+
+ vcdinfo_lid_get_pxd(obj, &pxd, lid);
+
+ switch (pxd.descriptor_type) {
+ case PSD_TYPE_PLAY_LIST:
+ return vcdinf_pld_get_return_offset(pxd.pld);
+ case PSD_TYPE_SELECTION_LIST:
+ case PSD_TYPE_EXT_SELECTION_LIST:
+ return vcdinf_psd_get_return_offset(pxd.psd);
+ break;
+ case PSD_TYPE_END_LIST:
+ case PSD_TYPE_COMMAND_LIST:
+ break;
+ }
+ }
+
+ return VCDINFO_INVALID_OFFSET;
+}
+
+/*!
+ Return the audio type for a given segment.
+ VCDINFO_INVALID_AUDIO_TYPE is returned on error.
+*/
+unsigned int
+vcdinfo_get_seg_audio_type(const vcdinfo_obj_t *obj, segnum_t seg_num)
+{
+ if ( NULL == obj || NULL == &obj->info
+ || seg_num >= vcdinfo_get_num_segments(obj) )
+ return VCDINFO_INVALID_AUDIO_TYPE;
+ return(obj->info.spi_contents[seg_num].audio_type);
+}
+
+/*!
+ Return true if this segment is supposed to continue to the next one,
+ (is part of an "item" or listing in the ISO 9660 filesystem).
+*/
+bool
+vcdinfo_get_seg_continue(const vcdinfo_obj_t *obj, segnum_t seg_num)
+{
+ if ( NULL == obj || NULL == &obj->info
+ || seg_num >= vcdinfo_get_num_segments(obj) )
+ return false;
+ return(obj->info.spi_contents[seg_num].item_cont);
+}
+
+/*! Return the starting LBA (logical block address) for segment
+ entry_num in obj. VCDINFO_LBA_NULL is returned if there is no entry.
+
+ Note first seg_num is 0.
+*/
+lba_t
+vcdinfo_get_seg_lba(const vcdinfo_obj_t *obj, segnum_t seg_num)
+{
+ if (obj == NULL) return VCDINFO_NULL_LBA;
+ return cdio_lsn_to_lba(vcdinfo_get_seg_lba(obj, seg_num));
+}
+
+/*! Return the starting LBA (logical block address) for segment
+ entry_num in obj. VCDINFO_LSN_NULL is returned if there is no entry.
+
+ Note first seg_num is 0.
+*/
+lsn_t
+vcdinfo_get_seg_lsn(const vcdinfo_obj_t *obj, segnum_t seg_num)
+{
+ if (obj == NULL || seg_num >= vcdinfo_get_num_segments(obj))
+ return VCDINFO_NULL_LSN;
+ return obj->first_segment_lsn + (VCDINFO_SEGMENT_SECTOR_SIZE * seg_num);
+}
+
+/*! Return the starting MSF (minutes/secs/frames) for segment
+ entry_num in obj. NULL is returned if there is no entry.
+
+ Note first seg_num is 0!
+*/
+const msf_t *
+vcdinfo_get_seg_msf(const vcdinfo_obj_t *obj, segnum_t seg_num)
+{
+ if (obj == NULL || seg_num >= vcdinfo_get_num_segments(obj))
+ return NULL;
+ else {
+ lsn_t lsn = vcdinfo_get_seg_lsn(obj, seg_num);
+ static msf_t msf;
+ cdio_lsn_to_msf(lsn, &msf);
+ return &msf;
+ }
+}
+
+/*! Return the x-y resolution for a given segment.
+ Note first i_seg is 0.
+*/
+void
+vcdinfo_get_seg_resolution(const vcdinfo_obj_t *p_vcdinfo, segnum_t i_seg,
+ /*out*/ uint16_t *max_x, /*out*/ uint16_t *max_y)
+{
+ vcdinfo_video_segment_type_t segtype
+ = vcdinfo_get_video_type(p_vcdinfo, i_seg);
+ segnum_t i_segs = vcdinfo_get_num_segments(p_vcdinfo);
+
+ if (i_seg >= i_segs) return;
+
+ switch (segtype) {
+ case VCDINFO_FILES_VIDEO_NTSC_STILL:
+ *max_x = 704;
+ *max_y = 480;
+ break;
+ case VCDINFO_FILES_VIDEO_NTSC_STILL2:
+ *max_x = 352;
+ *max_y = 240;
+ break;
+ case VCDINFO_FILES_VIDEO_PAL_STILL:
+ *max_x = 704;
+ *max_y = 576;
+ break;
+ case VCDINFO_FILES_VIDEO_PAL_STILL2:
+ *max_x = 352;
+ *max_y = 288;
+ break;
+ default:
+ /* */
+ switch (vcdinfo_get_format_version(p_vcdinfo)) {
+ case VCD_TYPE_VCD:
+ *max_x = 352;
+ *max_y = 240;
+ break;
+ case VCD_TYPE_VCD11:
+ case VCD_TYPE_VCD2:
+ *max_x = 352;
+ switch(segtype) {
+ case VCDINFO_FILES_VIDEO_NTSC_MOTION:
+ *max_y = 240;
+ break;
+ case VCDINFO_FILES_VIDEO_PAL_MOTION:
+ *max_y = 288;
+ default:
+ *max_y = 289;
+ }
+ break;
+ default: ;
+ }
+ }
+}
+
+
+
+/*!
+ Return the number of sectors for segment
+ entry_num in obj. 0 is returned if there is no entry.
+
+ Use this routine to figure out the actual number of bytes a physical
+ region of a disk or CD takes up for a segment.
+
+ If an item has been broken up into a number of "continued" segments,
+ we will report the item size for the first segment and 0 for the
+ remaining ones. We may revisit this decision later.
+*/
+uint32_t
+vcdinfo_get_seg_sector_count(const vcdinfo_obj_t *obj, segnum_t seg_num)
+{
+ if (obj == NULL || seg_num >= vcdinfo_get_num_segments(obj))
+ return 0;
+ return obj->seg_sizes[seg_num];
+}
+
+/*!
+ Return a string containing the VCD system id with trailing
+ blanks removed, or NULL if there is some problem in getting this.
+*/
+const char *
+vcdinfo_get_system_id(const vcdinfo_obj_t *obj)
+{
+ if ( NULL == obj || NULL == &obj->pvd ) return (NULL);
+ return(iso9660_get_system_id(&obj->pvd));
+}
+
+/*!
+ Return the track number for entry n in obj.
+
+ In contrast to libcdio we start numbering at 0 which is the
+ ISO9660 and metadata information for the Video CD. Thus track
+ 1 is the first track the first complete MPEG track generally.
+*/
+track_t
+vcdinfo_get_track(const vcdinfo_obj_t *obj, const unsigned int entry_num)
+{
+ const EntriesVcd_t *entries = &obj->entries;
+ const unsigned int entry_count = vcdinf_get_num_entries(entries);
+ /* Note entry_num is 0 origin. */
+ return entry_num < entry_count ?
+ vcdinf_get_track(entries, entry_num)-1: VCDINFO_INVALID_TRACK;
+}
+
+/*!
+ Return the audio type for a given track.
+ VCDINFO_INVALID_AUDIO_TYPE is returned on error.
+
+ Note: track 1 is usually the first track.
+*/
+unsigned int
+vcdinfo_get_track_audio_type(const vcdinfo_obj_t *obj, track_t track_num)
+{
+ TracksSVD *tracks;
+ TracksSVD2 *tracks2;
+ if ( NULL == obj || NULL == &obj->info ) return VCDINFO_INVALID_AUDIO_TYPE;
+ tracks = obj->tracks_buf;
+
+ if ( NULL == tracks ) return 0;
+ tracks2 = (TracksSVD2 *) &(tracks->playing_time[tracks->tracks]);
+ return(tracks2->contents[track_num-1].audio);
+}
+
+/*!
+ Return the highest track number in the current medium.
+
+ Because we track start numbering at 0 (which is the ISO 9660 track
+ containing Video CD naviagion and disk information), this is one
+ less than the number of tracks.
+
+ If there are no tracks, we return -1.
+*/
+unsigned int
+vcdinfo_get_num_tracks(const vcdinfo_obj_t *obj)
+{
+ if (obj == NULL || obj->img == NULL) return 0;
+
+ return cdio_get_num_tracks(obj->img)-1;
+}
+
+
+/*!
+ Return the starting LBA (logical block address) for track number
+ track_num in obj.
+
+ The IS0-9660 filesystem track has number 0. Tracks associated
+ with playable entries numbers start at 1.
+
+ The "leadout" track is specified either by
+ using track_num LEADOUT_TRACK or the total tracks+1.
+ VCDINFO_NULL_LBA is returned on failure.
+*/
+lba_t
+vcdinfo_get_track_lba(const vcdinfo_obj_t *obj, track_t track_num)
+{
+ if (NULL == obj || NULL == obj->img)
+ return VCDINFO_NULL_LBA;
+
+
+ /* CdIo tracks start at 1 rather than 0. */
+ return cdio_get_track_lba(obj->img, track_num+1);
+}
+
+/*!
+ Return the starting LSN (logical sector number) for track number
+ track_num in obj.
+
+ The IS0-9660 filesystem track has number 0. Tracks associated
+ with playable entries numbers start at 1.
+
+ The "leadout" track is specified either by
+ using track_num LEADOUT_TRACK or the total tracks+1.
+ VCDINFO_NULL_LBA is returned on failure.
+*/
+lsn_t
+vcdinfo_get_track_lsn(const vcdinfo_obj_t *obj, track_t track_num)
+{
+ if (NULL == obj || NULL == obj->img)
+ return VCDINFO_NULL_LSN;
+
+ /* CdIo tracks start at 1 rather than 0. */
+ return cdio_get_track_lsn(obj->img, track_num+1);
+}
+
+/*!
+ Return the starting MSF (minutes/secs/frames) for track number
+ track_num in obj.
+
+ The IS0-9660 filesystem track has number 0. Tracks associated
+ with playable entries numbers start at 1.
+
+ The "leadout" track is specified either by
+ using track_num LEADOUT_TRACK or the total tracks+1.
+ VCDINFO_NULL_LBA is returned on failure.
+*/
+int
+vcdinfo_get_track_msf(const vcdinfo_obj_t *obj, track_t track_num,
+ uint8_t *min, uint8_t *sec, uint8_t *frame)
+{
+ msf_t msf;
+
+ if (NULL == obj || NULL == obj->img)
+ return 1;
+
+ /* CdIo tracks start at 1 rather than 0. */
+ if (cdio_get_track_msf(obj->img, track_num+1, &msf)) {
+ *min = cdio_from_bcd8(msf.m);
+ *sec = cdio_from_bcd8(msf.s);
+ *frame = cdio_from_bcd8(msf.f);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*!
+ Return the size in sectors for track n.
+
+ The IS0-9660 filesystem track has number 0. Tracks associated
+ with playable entries numbers start at 1.
+
+ FIXME: Whether we count the track pregap sectors is a bit haphazard.
+ We should add a parameter to indicate whether this is wanted or not.
+
+*/
+unsigned int
+vcdinfo_get_track_sect_count(const vcdinfo_obj_t *obj, const track_t track_num)
+{
+ if (NULL == obj || VCDINFO_INVALID_TRACK == track_num)
+ return 0;
+
+ {
+ iso9660_stat_t *statbuf;
+ const lsn_t lsn = vcdinfo_get_track_lsn(obj, track_num);
+
+ /* Try to get the sector count from the ISO 9660 filesystem */
+ if (obj->has_xa && (statbuf = iso9660_find_fs_lsn(obj->img, lsn))) {
+ unsigned int secsize = statbuf->secsize;
+ free(statbuf);
+ return secsize;
+ } else {
+ const lsn_t next_lsn=vcdinfo_get_track_lsn(obj, track_num+1);
+ /* Failed on ISO 9660 filesystem. Use track information. */
+ return next_lsn > lsn ? next_lsn - lsn : 0;
+ }
+ }
+ return 0;
+}
+
+/*!
+ Return size in bytes for track number for entry n in obj.
+
+ The IS0-9660 filesystem track has number 1. Tracks associated
+ with playable entries numbers start at 2.
+
+ FIXME: Whether we count the track pregap sectors is a bit haphazard.
+ We should add a parameter to indicate whether this is wanted or not.
+*/
+unsigned int
+vcdinfo_get_track_size(const vcdinfo_obj_t *obj, track_t track_num)
+{
+ if (NULL == obj || VCDINFO_INVALID_TRACK == track_num)
+ return 0;
+
+ {
+ iso9660_stat_t statbuf;
+ const lsn_t lsn = cdio_lba_to_lsn(vcdinfo_get_track_lba(obj, track_num));
+
+ /* Try to get the sector count from the ISO 9660 filesystem */
+ if (obj->has_xa && iso9660_find_fs_lsn(obj->img, lsn)) {
+ return statbuf.size;
+ }
+#if 0
+ else {
+ /* Failed on ISO 9660 filesystem. Use track information. */
+ if (obj->img != NULL)
+ return cdio_get_track_size(obj->img);
+ }
+#endif
+ }
+ return 0;
+}
+
+/*!
+ \brief Get the kind of video stream segment of segment seg_num in obj.
+ \return VCDINFO_FILES_VIDEO_INVALID is returned if on error or obj is
+ null. Otherwise the enumeration type.
+
+ Note first seg_num is 0!
+*/
+vcdinfo_video_segment_type_t
+vcdinfo_get_video_type(const vcdinfo_obj_t *obj, segnum_t seg_num)
+{
+ const InfoVcd_t *info;
+ if (obj == NULL) return VCDINFO_FILES_VIDEO_INVALID;
+ info = &obj->info;
+ if (info == NULL) return VCDINFO_FILES_VIDEO_INVALID;
+ return info->spi_contents[seg_num].video_type;
+}
+
+/*!
+ \brief Get the kind of VCD that obj refers to.
+*/
+vcd_type_t
+vcdinfo_get_VCD_type(const vcdinfo_obj_t *obj)
+{
+ if (NULL == obj) return VCD_TYPE_INVALID;
+ return obj->vcd_type;
+}
+
+
+/*!
+ Return the VCD volume count - the number of CD's in the collection.
+ O is returned if there is some problem in getting this.
+*/
+unsigned int
+vcdinfo_get_volume_count(const vcdinfo_obj_t *obj)
+{
+ if ( NULL == obj ) return 0;
+ return vcdinf_get_volume_count(&obj->info);
+}
+
+/*!
+ Return the VCD ID.
+ NULL is returned if there is some problem in getting this.
+*/
+const char *
+vcdinfo_get_volume_id(const vcdinfo_obj_t *obj)
+{
+ if ( NULL == obj || NULL == &obj->pvd ) return (NULL);
+ return(iso9660_get_volume_id(&obj->pvd));
+}
+
+/*!
+ Return the VCD volumeset ID.
+ NULL is returned if there is some problem in getting this.
+*/
+const char *
+vcdinfo_get_volumeset_id(const vcdinfo_obj_t *obj)
+{
+ if ( NULL == obj || NULL == &obj->pvd ) return (NULL);
+ return(vcdinfo_strip_trail(obj->pvd.volume_set_id, ISO_MAX_VOLUMESET_ID));
+}
+
+/*!
+ Return the VCD volume num - the number of the CD in the collection.
+ This is a number between 1 and the volume count.
+ O is returned if there is some problem in getting this.
+*/
+unsigned int
+vcdinfo_get_volume_num(const vcdinfo_obj_t *obj)
+{
+ if ( NULL == obj ) return 0;
+ return(uint16_from_be( obj->info.vol_id));
+}
+
+int
+vcdinfo_get_wait_time (uint16_t wtime)
+{
+ /* Note: this doesn't agree exactly with _wtime */
+ if (wtime < 61)
+ return wtime;
+ else if (wtime < 255)
+ return (wtime - 60) * 10 + 60;
+ else
+ return -1;
+}
+
+/*!
+ Return true is there is playback control.
+*/
+bool
+vcdinfo_has_pbc (const vcdinfo_obj_t *obj)
+{
+ return (obj && obj->info.psd_size!=0);
+}
+
+/*!
+ Return true if VCD has "extended attributes" (XA). Extended attributes
+ add meta-data attributes to a entries of file describing the file.
+ See also cdio_get_xa_attr_str() which returns a string similar to
+ a string you might get on a Unix filesystem listing ("ls").
+*/
+bool
+vcdinfo_has_xa(const vcdinfo_obj_t *obj)
+{
+ return obj->has_xa;
+}
+
+/*!
+ Add one to the MSF.
+*/
+void
+vcdinfo_inc_msf (uint8_t *min, uint8_t *sec, int8_t *frame)
+{
+ (*frame)++;
+ if (*frame>=CDIO_CD_FRAMES_PER_SEC) {
+ *frame = 0;
+ (*sec)++;
+ if (*sec>=CDIO_CD_SECS_PER_MIN) {
+ *sec = 0;
+ (*min)++;
+ }
+ }
+}
+
+/*!
+ Convert minutes, seconds and frame (MSF components) into a
+ logical block address (or LBA).
+ See also cdio_msf_to_lba which uses msf_t as its single parameter.
+*/
+lba_t
+vcdinfo_msf2lba (uint8_t min, uint8_t sec, int8_t frame)
+{
+ return CDIO_CD_FRAMES_PER_SEC*(CDIO_CD_SECS_PER_MIN*min + sec) + frame;
+}
+
+/*!
+ Convert minutes, seconds and frame (MSF components) into a
+ logical block address (or LBA).
+ See also cdio_msf_to_lba which uses msf_t as its single parameter.
+*/
+void
+vcdinfo_lba2msf (lba_t lba, uint8_t *min, uint8_t *sec, uint8_t *frame)
+{
+ *min = lba / (60*75);
+ lba %= (60*75);
+ *sec = lba / 75;
+ *frame = lba % 75;
+}
+
+/*!
+ Convert minutes, seconds and frame (MSF components) into a
+ logical sector number (or LSN).
+*/
+lsn_t
+vcdinfo_msf2lsn (uint8_t min, uint8_t sec, int8_t frame)
+{
+ lba_t lba=75*(60*min + sec) + frame;
+ if (lba < CDIO_PREGAP_SECTORS) {
+ vcd_error ("lba (%u) less than pregap sector (%u)",
+ (unsigned int) lba, CDIO_PREGAP_SECTORS);
+ return lba;
+ }
+ return lba - CDIO_PREGAP_SECTORS;
+}
+
+const char *
+vcdinfo_ofs2str (const vcdinfo_obj_t *obj, unsigned int offset, bool ext)
+{
+ vcdinfo_offset_t *ofs;
+ char *buf;
+
+ switch (offset) {
+ case PSD_OFS_DISABLED:
+ return "disabled";
+ case PSD_OFS_MULTI_DEF:
+ return "multi-default";
+ case PSD_OFS_MULTI_DEF_NO_NUM:
+ return "multi_def_no_num";
+ default: ;
+ }
+
+ buf = _getbuf ();
+ ofs = _vcdinfo_get_offset_t(obj, offset, ext);
+ if (ofs != NULL) {
+ if (ofs->lid)
+ snprintf (buf, BUF_SIZE, "LID[%d] @0x%4.4x",
+ ofs->lid, ofs->offset);
+ else
+ snprintf (buf, BUF_SIZE, "PSD[?] @0x%4.4x",
+ ofs->offset);
+ } else {
+ snprintf (buf, BUF_SIZE, "? @0x%4.4x", offset);
+ }
+ return buf;
+}
+
+bool
+vcdinfo_read_psd (vcdinfo_obj_t *obj)
+{
+ unsigned psd_size = vcdinfo_get_psd_size (obj);
+
+ if (psd_size)
+ {
+ if (psd_size > 256*1024)
+ {
+ vcd_error ("weird psd size (%u) -- aborting", psd_size);
+ return false;
+ }
+
+ obj->lot = _vcd_malloc (ISO_BLOCKSIZE * LOT_VCD_SIZE);
+ obj->psd = _vcd_malloc (ISO_BLOCKSIZE * _vcd_len2blocks (psd_size,
+ ISO_BLOCKSIZE));
+
+ if (cdio_read_mode2_sectors (obj->img, (void *) obj->lot, LOT_VCD_SECTOR,
+ false, LOT_VCD_SIZE))
+ return false;
+
+ if (cdio_read_mode2_sectors (obj->img, (void *) obj->psd, PSD_VCD_SECTOR,
+ false, _vcd_len2blocks (psd_size,
+ ISO_BLOCKSIZE)))
+ return false;
+
+ } else {
+ return false;
+ }
+ return true;
+}
+
+/*! Return the entry number for the given track. */
+unsigned int
+vcdinfo_track_get_entry(const vcdinfo_obj_t *obj, track_t i_track)
+{
+ /* FIXME: Add structure to directly map track to first entry number.
+ Until then...
+ */
+ lsn_t lsn= vcdinfo_get_track_lsn(obj, i_track);
+ return vcdinfo_lsn_get_entry(obj, lsn);
+}
+
+/*!
+ Calls recursive routine to populate obj->offset_list or obj->offset_x_list
+ by going through LOT.
+
+ Returns false if there was some error.
+*/
+bool
+vcdinfo_visit_lot (vcdinfo_obj_t *obj, bool extended)
+{
+ struct _vcdinf_pbc_ctx pbc_ctx;
+ bool ret;
+
+ pbc_ctx.psd_size = vcdinfo_get_psd_size (obj);
+ pbc_ctx.psd_x_size = obj->psd_x_size;
+ pbc_ctx.offset_mult = 8;
+ pbc_ctx.maximum_lid = vcdinfo_get_num_LIDs(obj);
+ pbc_ctx.offset_x_list = NULL;
+ pbc_ctx.offset_list = NULL;
+ pbc_ctx.psd = obj->psd;
+ pbc_ctx.psd_x = obj->psd_x;
+ pbc_ctx.lot = obj->lot;
+ pbc_ctx.lot_x = obj->lot_x;
+ pbc_ctx.extended = extended;
+
+ ret = vcdinf_visit_lot(&pbc_ctx);
+ if (NULL != obj->offset_x_list)
+ _cdio_list_free(obj->offset_x_list, true);
+ obj->offset_x_list = pbc_ctx.offset_x_list;
+ if (NULL != obj->offset_list)
+ _cdio_list_free(obj->offset_list, true);
+ obj->offset_list = pbc_ctx.offset_list;
+ return ret;
+}
+
+/*!
+ Change trailing blanks in str to nulls. Str has a maximum size of
+ n characters.
+*/
+const char *
+vcdinfo_strip_trail (const char str[], size_t n)
+{
+ static char buf[1025];
+ int j;
+
+ vcd_assert (n < 1024);
+
+ strncpy (buf, str, n);
+ buf[n] = '\0';
+
+ for (j = strlen (buf) - 1; j >= 0; j--)
+ {
+ if (buf[j] != ' ')
+ break;
+
+ buf[j] = '\0';
+ }
+
+ return buf;
+}
+
+/*!
+ Return true if offset is "rejected". That is shouldn't be displayed
+ in a list of entries.
+*/
+bool
+vcdinfo_is_rejected(uint16_t offset)
+{
+ return (offset & VCDINFO_REJECTED_MASK) != 0;
+}
+
+/*!
+ Nulls/zeros vcdinfo_obj_t structures; The caller should have
+ ensured that obj != NULL.
+ routines using obj are called.
+*/
+static void
+_vcdinfo_zero(vcdinfo_obj_t *obj)
+{
+ memset(obj, 0, sizeof(vcdinfo_obj_t));
+ obj->vcd_type = VCD_TYPE_INVALID;
+ obj->img = NULL;
+ obj->lot = NULL;
+ obj->source_name = NULL;
+ obj->seg_sizes = NULL;
+}
+
+/*!
+ Initialize the vcdinfo structure "obj". Should be done before other
+ routines using obj are called.
+*/
+bool
+vcdinfo_init(vcdinfo_obj_t *obj)
+{
+ if (NULL == obj) return false;
+ _vcdinfo_zero(obj);
+ return cdio_init();
+}
+
+/*!
+ Set up vcdinfo structure "obj" for reading from a particular
+ medium. This should be done before after initialization but before
+ any routines that need to retrieve data.
+
+ source_name is the device or file to use for inspection, and
+ source_type indicates what driver to use or class of drivers in the
+ case of DRIVER_DEVICE.
+ access_mode gives the CD access method for reading should the driver
+ allow for more than one kind of access method (e.g. MMC versus ioctl
+ on GNU/Linux)
+
+ If source_name is NULL we'll fill in the appropriate default device
+ name for the given source_type. However if in addtion source_type is
+ DRIVER_UNKNOWN, then we'll scan for a drive containing a VCD.
+
+ VCDINFO_OPEN_VCD is returned if everything went okay;
+ VCDINFO_OPEN_ERROR if there was an error and VCDINFO_OPEN_OTHER if the
+ medium is something other than a VCD.
+ */
+vcdinfo_open_return_t
+vcdinfo_open(vcdinfo_obj_t **obj_p, char *source_name[],
+ driver_id_t source_type, const char access_mode[])
+{
+ CdIo *img;
+ vcdinfo_obj_t *obj = _vcd_malloc(sizeof(vcdinfo_obj_t));
+ iso9660_stat_t *statbuf;
+
+
+ /* If we don't specify a driver_id or a source_name, scan the
+ system for a CD that contains a VCD.
+ */
+ if (NULL == *source_name && source_type == DRIVER_UNKNOWN) {
+ char **cd_drives=NULL;
+ cd_drives = cdio_get_devices_with_cap_ret(NULL,
+ (CDIO_FS_ANAL_SVCD|CDIO_FS_ANAL_CVD|CDIO_FS_ANAL_VIDEOCD
+ |CDIO_FS_UNKNOWN),
+ true, &source_type);
+ if ( NULL == cd_drives || NULL == cd_drives[0] ) {
+ return VCDINFO_OPEN_ERROR;
+ }
+ *source_name = strdup(cd_drives[0]);
+ cdio_free_device_list(cd_drives);
+ }
+
+ img = cdio_open(*source_name, source_type);
+ if (NULL == img) {
+ return VCDINFO_OPEN_ERROR;
+ }
+
+ *obj_p = obj;
+
+ if (access_mode != NULL)
+ cdio_set_arg (img, "access-mode", access_mode);
+
+ if (NULL == *source_name) {
+ *source_name = cdio_get_default_device(img);
+ if (NULL == *source_name) return VCDINFO_OPEN_ERROR;
+ }
+
+ memset (obj, 0, sizeof (vcdinfo_obj_t));
+ obj->img = img; /* Note we do this after the above wipeout! */
+
+ if (!iso9660_fs_read_pvd(obj->img, &(obj->pvd))) {
+ return VCDINFO_OPEN_ERROR;
+ }
+
+ /* Determine if VCD has XA attributes. */
+ {
+
+ iso9660_pvd_t const *pvd = &obj->pvd;
+
+ obj->has_xa = !strncmp ((char *) pvd + ISO_XA_MARKER_OFFSET,
+ ISO_XA_MARKER_STRING,
+ strlen (ISO_XA_MARKER_STRING));
+ }
+
+ if (!read_info(obj->img, &(obj->info), &(obj->vcd_type)) ||
+ vcdinfo_get_format_version (obj) == VCD_TYPE_INVALID ||
+ !read_entries(obj->img, &(obj->entries))) {
+ free (obj); /* match 0.7.23's behaviour */
+ return VCDINFO_OPEN_OTHER;
+ }
+
+ {
+ size_t len = strlen(*source_name)+1;
+ obj->source_name = (char *) malloc(len * sizeof(char));
+ strncpy(obj->source_name, *source_name, len);
+ }
+
+ if (obj->vcd_type == VCD_TYPE_SVCD || obj->vcd_type == VCD_TYPE_HQVCD) {
+ statbuf = iso9660_fs_stat (obj->img, "MPEGAV");
+
+ if (NULL != statbuf) {
+ vcd_warn ("non compliant /MPEGAV folder detected!");
+ free(statbuf);
+ }
+
+
+ statbuf = iso9660_fs_stat (obj->img, "SVCD/TRACKS.SVD;1");
+ if (NULL != statbuf) {
+ lsn_t lsn = statbuf->lsn;
+ if (statbuf->size != ISO_BLOCKSIZE)
+ vcd_warn ("TRACKS.SVD filesize != %d!", ISO_BLOCKSIZE);
+
+ obj->tracks_buf = _vcd_malloc (ISO_BLOCKSIZE);
+
+ free(statbuf);
+ if (cdio_read_mode2_sector (obj->img, obj->tracks_buf, lsn, false))
+ return VCDINFO_OPEN_ERROR;
+ }
+ }
+
+ _init_segments (obj);
+
+ switch (obj->vcd_type) {
+ case VCD_TYPE_VCD2: {
+ /* FIXME: Can reduce CD reads by using
+ iso9660_fs_readdir(img, "EXT", true) and then scanning for
+ the files listed below.
+ */
+ statbuf = iso9660_fs_stat (img, "EXT/PSD_X.VCD;1");
+ if (NULL != statbuf) {
+ lsn_t lsn = statbuf->lsn;
+ uint32_t secsize = statbuf->secsize;
+
+ obj->psd_x = _vcd_malloc (ISO_BLOCKSIZE * secsize);
+ obj->psd_x_size = statbuf->size;
+
+ vcd_debug ("found /EXT/PSD_X.VCD at sector %lu",
+ (long unsigned int) lsn);
+
+ free(statbuf);
+ if (cdio_read_mode2_sectors (img, obj->psd_x, lsn, false, secsize))
+ return VCDINFO_OPEN_ERROR;
+ }
+
+ statbuf = iso9660_fs_stat (img, "EXT/LOT_X.VCD;1");
+ if (NULL != statbuf) {
+ lsn_t lsn = statbuf->lsn;
+ uint32_t secsize = statbuf->secsize;
+ obj->lot_x = _vcd_malloc (ISO_BLOCKSIZE * secsize);
+
+ vcd_debug ("found /EXT/LOT_X.VCD at sector %lu",
+ (unsigned long int) lsn);
+
+ if (statbuf->size != LOT_VCD_SIZE * ISO_BLOCKSIZE)
+ vcd_warn ("LOT_X.VCD size != 65535");
+
+ free(statbuf);
+ if (cdio_read_mode2_sectors (img, obj->lot_x, lsn, false, secsize))
+ return VCDINFO_OPEN_ERROR;
+
+ }
+ break;
+ }
+ case VCD_TYPE_SVCD:
+ case VCD_TYPE_HQVCD: {
+ /* FIXME: Can reduce CD reads by using
+ iso9660_fs_readdir(img, "SVCD", true) and then scanning for
+ the files listed below.
+ */
+ statbuf = iso9660_fs_stat (img, "MPEGAV");
+ if (NULL != statbuf) {
+ vcd_warn ("non compliant /MPEGAV folder detected!");
+ free(statbuf);
+ }
+
+ statbuf = iso9660_fs_stat (img, "SVCD/TRACKS.SVD;1");
+ if (NULL == statbuf)
+ vcd_warn ("mandatory /SVCD/TRACKS.SVD not found!");
+ else {
+ vcd_debug ("found TRACKS.SVD signature at sector %lu",
+ (unsigned long int) statbuf->lsn);
+ free(statbuf);
+ }
+
+ statbuf = iso9660_fs_stat (img, "SVCD/SEARCH.DAT;1");
+ if (NULL == statbuf)
+ vcd_warn ("mandatory /SVCD/SEARCH.DAT not found!");
+ else {
+ lsn_t lsn = statbuf->lsn;
+ uint32_t secsize = statbuf->secsize;
+ uint32_t stat_size = statbuf->size;
+ uint32_t size;
+
+ vcd_debug ("found SEARCH.DAT at sector %lu", (unsigned long int) lsn);
+
+ obj->search_buf = _vcd_malloc (ISO_BLOCKSIZE * secsize);
+
+ if (cdio_read_mode2_sectors (img, obj->search_buf, lsn, false, secsize))
+ return VCDINFO_OPEN_ERROR;
+
+ size = (3 * uint16_from_be (((SearchDat *)obj->search_buf)->scan_points))
+ + sizeof (SearchDat);
+
+ free(statbuf);
+ if (size > stat_size) {
+ vcd_warn ("number of scanpoints leads to bigger size than "
+ "file size of SEARCH.DAT! -- rereading");
+
+ free (obj->search_buf);
+ obj->search_buf = _vcd_malloc (ISO_BLOCKSIZE
+ * _vcd_len2blocks(size, ISO_BLOCKSIZE));
+
+ if (cdio_read_mode2_sectors (img, obj->search_buf, lsn, false,
+ secsize))
+ return VCDINFO_OPEN_ERROR;
+ }
+ }
+ break;
+ }
+ default:
+ ;
+ }
+
+ statbuf = iso9660_fs_stat (img, "EXT/SCANDATA.DAT;1");
+ if (statbuf != NULL) {
+ lsn_t lsn = statbuf->lsn;
+ uint32_t secsize = statbuf->secsize;
+
+ vcd_debug ("found /EXT/SCANDATA.DAT at sector %u", (unsigned int) lsn);
+
+ obj->scandata_buf = _vcd_malloc (ISO_BLOCKSIZE * secsize);
+
+ free(statbuf);
+ if (cdio_read_mode2_sectors (img, obj->scandata_buf, lsn, false, secsize))
+ return VCDINFO_OPEN_ERROR;
+ }
+
+ return VCDINFO_OPEN_VCD;
+
+}
+
+/*!
+ Dispose of any resources associated with vcdinfo structure "obj".
+ Call this when "obj" it isn't needed anymore.
+
+ True is returned is everything went okay, and false if not.
+*/
+bool
+vcdinfo_close(vcdinfo_obj_t *obj)
+{
+ if (obj != NULL) {
+ if (obj->offset_list != NULL)
+ _cdio_list_free(obj->offset_list, true);
+ if (obj->offset_x_list != NULL)
+ _cdio_list_free(obj->offset_x_list, true);
+ free(obj->seg_sizes);
+ free(obj->lot);
+ free(obj->lot_x);
+ if (obj->psd_x) free(obj->psd_x);
+ if (obj->psd) free(obj->psd);
+ if (obj->scandata_buf) free(obj->scandata_buf);
+ free(obj->tracks_buf);
+ free(obj->search_buf);
+ free(obj->source_name);
+
+ if (obj->img != NULL) cdio_destroy (obj->img);
+ _vcdinfo_zero(obj);
+ }
+
+ free(obj);
+ return(true);
+}
+
+
+/*
+ * Local variables:
+ * c-file-style: "gnu"
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ */