summaryrefslogtreecommitdiff
path: root/contrib/libvcd/pbc.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/libvcd/pbc.c')
-rw-r--r--contrib/libvcd/pbc.c880
1 files changed, 880 insertions, 0 deletions
diff --git a/contrib/libvcd/pbc.c b/contrib/libvcd/pbc.c
new file mode 100644
index 000000000..1b4f1a6c1
--- /dev/null
+++ b/contrib/libvcd/pbc.c
@@ -0,0 +1,880 @@
+/*
+ $Id: pbc.c,v 1.3 2005/01/01 02:43:59 rockyb Exp $
+
+ Copyright (C) 2000, 2004 Herbert Valerio Riedel <hvr@gnu.org>
+
+ 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 Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <stddef.h>
+#include <math.h>
+
+#include <cdio/cdio.h>
+#include <cdio/bytesex.h>
+
+/* Public headers */
+#include <libvcd/logging.h>
+#include <libvcd/files.h>
+#include <libvcd/types.h>
+#include <libvcd/info.h>
+
+/* FIXME! Make this really private. */
+#include <libvcd/files_private.h>
+
+/* Private headers */
+#include "vcd_assert.h"
+#include "obj.h"
+#include "pbc.h"
+#include "util.h"
+
+static const char _rcsid[] = "$Id: pbc.c,v 1.3 2005/01/01 02:43:59 rockyb Exp $";
+
+static uint8_t
+_wtime (int seconds)
+{
+ if (seconds < 0)
+ return 255;
+
+ if (seconds <= 60)
+ return seconds;
+
+ if (seconds <= 2000)
+ {
+ double _tmp;
+
+ _tmp = seconds;
+ _tmp -= 60;
+ _tmp /= 10;
+ _tmp += 60;
+
+ return rint (_tmp);
+ }
+
+ vcd_warn ("wait time of %ds clipped to 2000s", seconds);
+
+ return 254;
+}
+
+static pbc_t *
+_vcd_pbc_byid(const VcdObj *obj, const char item_id[])
+{
+ CdioListNode *node;
+
+ _CDIO_LIST_FOREACH (node, obj->pbc_list)
+ {
+ pbc_t *_pbc = _cdio_list_node_data (node);
+
+ if (_pbc->id && !strcmp (item_id, _pbc->id))
+ return _pbc;
+ }
+
+ /* not found */
+ return NULL;
+}
+
+unsigned
+_vcd_pbc_lid_lookup (const VcdObj *obj, const char item_id[])
+{
+ CdioListNode *node;
+ unsigned n = 1;
+
+ _CDIO_LIST_FOREACH (node, obj->pbc_list)
+ {
+ pbc_t *_pbc = _cdio_list_node_data (node);
+
+ vcd_assert (n < 0x8000);
+
+ if (_pbc->id && !strcmp (item_id, _pbc->id))
+ return n;
+
+ n++;
+ }
+
+ /* not found */
+ return 0;
+}
+
+static void
+_set_area_helper (pbc_area_t *dest, const pbc_area_t *src, const char sel_id[])
+{
+ memset (dest, 0, sizeof (pbc_area_t));
+
+ if (src)
+ {
+ if (src->x1 || src->x2 || src->y1 || src->y2) /* not disabled */
+ {
+ if (src->x1 >= src->x2)
+ vcd_error ("selection '%s': area x1 >= x2 (%d >= %d)",
+ sel_id, src->x1, src->x2);
+
+ if (src->y1 >= src->y2)
+ vcd_error ("selection '%s': area y1 >= y2 (%d >= %d)",
+ sel_id, src->y1, src->y2);
+ }
+
+ *dest = *src;
+ }
+}
+
+enum item_type_t
+_vcd_pbc_lookup (const VcdObj *obj, const char item_id[])
+{
+ unsigned id;
+
+ vcd_assert (item_id != NULL);
+
+ if ((id = _vcd_pbc_pin_lookup (obj, item_id)))
+ {
+ if (id < 2)
+ return ITEM_TYPE_NOTFOUND;
+ else if (id < MIN_ENCODED_TRACK_NUM)
+ return ITEM_TYPE_TRACK;
+ else if (id < 600)
+ return ITEM_TYPE_ENTRY;
+ else if (id <= MAX_ENCODED_SEGMENT_NUM)
+ return ITEM_TYPE_SEGMENT;
+ else
+ vcd_assert_not_reached ();
+ }
+ else if (_vcd_pbc_lid_lookup (obj, item_id))
+ return ITEM_TYPE_PBC;
+
+ return ITEM_TYPE_NOTFOUND;
+}
+
+uint16_t
+_vcd_pbc_pin_lookup (const VcdObj *obj, const char item_id[])
+{
+ int n;
+ CdioListNode *node;
+
+ if (!item_id)
+ return 0;
+
+ /* check sequence items */
+
+ n = 0;
+ _CDIO_LIST_FOREACH (node, obj->mpeg_sequence_list)
+ {
+ mpeg_sequence_t *_sequence = _cdio_list_node_data (node);
+
+ vcd_assert (n < 98);
+
+ if (_sequence->id && !strcmp (item_id, _sequence->id))
+ return n + 2;
+
+ n++;
+ }
+
+ /* check entry points */
+
+ n = 0;
+ _CDIO_LIST_FOREACH (node, obj->mpeg_sequence_list)
+ {
+ mpeg_sequence_t *_sequence = _cdio_list_node_data (node);
+ CdioListNode *node2;
+
+ /* default entry point */
+
+ if (_sequence->default_entry_id
+ && !strcmp (item_id, _sequence->default_entry_id))
+ return n + 100;
+ n++;
+
+ /* additional entry points */
+
+ _CDIO_LIST_FOREACH (node2, _sequence->entry_list)
+ {
+ entry_t *_entry = _cdio_list_node_data (node2);
+
+ vcd_assert (n < 500);
+
+ if (_entry->id && !strcmp (item_id, _entry->id))
+ return n + 100;
+
+ n++;
+ }
+ }
+
+ /* check sequence items */
+
+ n = 0;
+ _CDIO_LIST_FOREACH (node, obj->mpeg_segment_list)
+ {
+ mpeg_segment_t *_segment = _cdio_list_node_data (node);
+
+ vcd_assert (n < 1980);
+
+ if (_segment->id && !strcmp (item_id, _segment->id))
+ return n + MIN_ENCODED_SEGMENT_NUM;
+
+ n += _segment->segment_count;
+ }
+
+ return 0;
+}
+
+bool
+_vcd_pbc_available (const VcdObj *obj)
+{
+ vcd_assert (obj != NULL);
+ vcd_assert (obj->pbc_list != NULL);
+
+ if (!_cdio_list_length (obj->pbc_list))
+ return false;
+
+ if (!_vcd_obj_has_cap_p (obj, _CAP_PBC))
+ {
+ vcd_warn ("PBC list not empty but VCD type not capable of PBC!");
+ return false;
+ }
+
+ return true;
+}
+
+uint16_t
+_vcd_pbc_max_lid (const VcdObj *obj)
+{
+ uint16_t retval = 0;
+
+ if (_vcd_pbc_available (obj))
+ retval = _cdio_list_length (obj->pbc_list);
+
+ return retval;
+}
+
+static size_t
+_vcd_pbc_node_length (const VcdObj *obj, const pbc_t *_pbc, bool extended)
+{
+ size_t retval = 0;
+
+ if (extended)
+ vcd_assert (_vcd_obj_has_cap_p (obj, _CAP_PBC_X));
+
+ switch (_pbc->type)
+ {
+ int n;
+
+ case PBC_PLAYLIST:
+ n = _cdio_list_length (_pbc->item_id_list);
+ retval = __cd_offsetof (_PsdPlayListDescriptor, itemid[n]);
+ break;
+
+ case PBC_SELECTION:
+ n = _cdio_list_length (_pbc->select_id_list);
+
+ retval = __cd_offsetof (PsdSelectionListDescriptor_t, ofs[n]);
+
+ if (extended || _vcd_obj_has_cap_p (obj, _CAP_4C_SVCD))
+ retval += __cd_offsetof (PsdSelectionListDescriptorExtended, area[n]);
+ break;
+
+ case PBC_END:
+ retval = sizeof (PsdEndListDescriptor);
+ break;
+
+ default:
+ vcd_assert_not_reached ();
+ break;
+ }
+
+ return retval;
+}
+
+static uint16_t
+_lookup_psd_offset (const VcdObj *obj, const char item_id[], bool extended)
+{
+ CdioListNode *node;
+
+ if (extended)
+ vcd_assert (_vcd_obj_has_cap_p (obj, _CAP_PBC_X));
+
+ /* disable it */
+ if (!item_id)
+ return PSD_OFS_DISABLED;
+
+ _CDIO_LIST_FOREACH (node, obj->pbc_list)
+ {
+ pbc_t *_pbc = _cdio_list_node_data (node);
+
+ if (!_pbc->id || strcmp (item_id, _pbc->id))
+ continue;
+
+ return (extended ? _pbc->offset_ext : _pbc->offset) / INFO_OFFSET_MULT;
+ }
+
+ vcd_error ("PSD: referenced PSD '%s' not found", item_id);
+
+ /* not found */
+ return PSD_OFS_DISABLED;
+}
+
+static void
+_vcd_pin_mark_id (const VcdObj *obj, const char _id[])
+{
+ mpeg_sequence_t *_seq;
+ mpeg_segment_t *_seg;
+
+ vcd_assert (obj != NULL);
+
+ if (!_id)
+ return;
+
+ if ((_seq = _vcd_obj_get_sequence_by_id ((VcdObj *) obj, _id)))
+ _seq->referenced = true;
+
+ if ((_seg = _vcd_obj_get_segment_by_id ((VcdObj *) obj, _id)))
+ _seg->referenced = true;
+}
+
+static void
+_vcd_pbc_mark_id (const VcdObj *obj, const char _id[])
+{
+ pbc_t *_pbc;
+
+ vcd_assert (obj != NULL);
+
+ if (!_id)
+ return;
+
+ _pbc = _vcd_pbc_byid (obj, _id);
+
+ if (!_pbc) /* not found */
+ return;
+
+ if (_pbc->referenced) /* already marked */
+ return;
+
+ _pbc->referenced = true;
+
+ switch (_pbc->type)
+ {
+ case PBC_PLAYLIST:
+ {
+ CdioListNode *node;
+
+ _vcd_pbc_mark_id (obj, _pbc->prev_id);
+ _vcd_pbc_mark_id (obj, _pbc->next_id);
+ _vcd_pbc_mark_id (obj, _pbc->retn_id);
+
+ _CDIO_LIST_FOREACH (node, _pbc->item_id_list)
+ {
+ const char *_id = _cdio_list_node_data (node);
+
+ _vcd_pin_mark_id (obj, _id);
+ }
+ }
+ break;
+
+ case PBC_SELECTION:
+ {
+ CdioListNode *node;
+
+ _vcd_pbc_mark_id (obj, _pbc->prev_id);
+ _vcd_pbc_mark_id (obj, _pbc->next_id);
+ _vcd_pbc_mark_id (obj, _pbc->retn_id);
+
+ if (_pbc->selection_type == _SEL_NORMAL)
+ _vcd_pbc_mark_id (obj, _pbc->default_id);
+
+ _vcd_pbc_mark_id (obj, _pbc->timeout_id);
+
+ _vcd_pin_mark_id (obj, _pbc->item_id);
+
+ _CDIO_LIST_FOREACH (node, _pbc->select_id_list)
+ {
+ const char *_id = _cdio_list_node_data (node);
+
+ _vcd_pbc_mark_id (obj, _id);
+ }
+ }
+ break;
+
+ case PBC_END:
+ _vcd_pin_mark_id (obj, _pbc->image_id);
+ break;
+
+ default:
+ vcd_assert_not_reached ();
+ break;
+ }
+}
+
+void
+_vcd_pbc_check_unreferenced (const VcdObj *obj)
+{
+ CdioListNode *node;
+
+ /* clear all flags */
+
+ _CDIO_LIST_FOREACH (node, obj->pbc_list)
+ {
+ pbc_t *_pbc = _cdio_list_node_data (node);
+
+ _pbc->referenced = false;
+ }
+
+ _CDIO_LIST_FOREACH (node, obj->mpeg_sequence_list)
+ {
+ mpeg_sequence_t *_sequence = _cdio_list_node_data (node);
+
+ _sequence->referenced = false;
+ }
+
+ _CDIO_LIST_FOREACH (node, obj->mpeg_segment_list)
+ {
+ mpeg_segment_t *_segment = _cdio_list_node_data (node);
+
+ _segment->referenced = false;
+ }
+
+ /* start from non-rejected lists */
+
+ _CDIO_LIST_FOREACH (node, obj->pbc_list)
+ {
+ pbc_t *_pbc = _cdio_list_node_data (node);
+
+ vcd_assert (_pbc->id != NULL);
+
+ if (_pbc->rejected)
+ continue;
+
+ _vcd_pbc_mark_id (obj, _pbc->id);
+ }
+
+ /* collect flags */
+
+ _CDIO_LIST_FOREACH (node, obj->pbc_list)
+ {
+ pbc_t *_pbc = _cdio_list_node_data (node);
+
+ if (!_pbc->referenced)
+ vcd_warn ("PSD item '%s' is unreachable", _pbc->id);
+ }
+
+ _CDIO_LIST_FOREACH (node, obj->mpeg_sequence_list)
+ {
+ mpeg_sequence_t *_sequence = _cdio_list_node_data (node);
+
+ if (!_sequence->referenced)
+ vcd_warn ("sequence '%s' is not reachable by PBC", _sequence->id);
+ }
+
+ _CDIO_LIST_FOREACH (node, obj->mpeg_segment_list)
+ {
+ mpeg_segment_t *_segment = _cdio_list_node_data (node);
+
+ if (!_segment->referenced)
+ vcd_warn ("segment item '%s' is unreachable", _segment->id);
+ }
+
+}
+
+void
+_vcd_pbc_node_write (const VcdObj *obj, const pbc_t *_pbc, void *buf,
+ bool extended)
+{
+ vcd_assert (obj != NULL);
+ vcd_assert (_pbc != NULL);
+ vcd_assert (buf != NULL);
+
+ if (extended)
+ vcd_assert (_vcd_obj_has_cap_p (obj, _CAP_PBC_X));
+
+ switch (_pbc->type)
+ {
+ case PBC_PLAYLIST:
+ {
+ _PsdPlayListDescriptor *_md = buf;
+ CdioListNode *node;
+ int n;
+
+ _md->type = PSD_TYPE_PLAY_LIST;
+ _md->noi = _cdio_list_length (_pbc->item_id_list);
+
+ vcd_assert (_pbc->lid < 0x8000);
+ _md->lid = uint16_to_be (_pbc->lid | (_pbc->rejected ? 0x8000 : 0));
+
+ _md->prev_ofs =
+ uint16_to_be (_lookup_psd_offset (obj, _pbc->prev_id, extended));
+ _md->next_ofs =
+ uint16_to_be (_lookup_psd_offset (obj, _pbc->next_id, extended));
+ _md->return_ofs =
+ uint16_to_be (_lookup_psd_offset (obj, _pbc->retn_id, extended));
+ _md->ptime = uint16_to_be (rint (_pbc->playing_time * 15.0));
+ _md->wtime = _wtime (_pbc->wait_time);
+ _md->atime = _wtime (_pbc->auto_pause_time);
+
+ n = 0;
+ _CDIO_LIST_FOREACH (node, _pbc->item_id_list)
+ {
+ const char *_id = _cdio_list_node_data (node);
+ uint16_t _pin;
+
+ if (_id)
+ {
+ _pin = _vcd_pbc_pin_lookup (obj, _id);
+
+ if (!_pin)
+ vcd_error ("PSD: referenced play item '%s' not found", _id);
+
+ _md->itemid[n] = uint16_to_be (_pin);
+ }
+ else
+ _md->itemid[n] = 0; /* play nothing */
+
+ n++;
+ }
+ }
+ break;
+
+ case PBC_SELECTION:
+ {
+ PsdSelectionListDescriptor_t *_md = buf;
+
+ const unsigned int _nos = _cdio_list_length (_pbc->select_id_list);
+
+ if (extended)
+ _md->type = PSD_TYPE_EXT_SELECTION_LIST;
+ else
+ _md->type = PSD_TYPE_SELECTION_LIST;
+
+ if (!IN (_pbc->bsn, 1, MAX_PBC_SELECTIONS))
+ vcd_error ("selection '%s': BSN (%d) not in range [1..%d]",
+ _pbc->id, _pbc->bsn, MAX_PBC_SELECTIONS);
+
+ if (!IN (_nos, 0, MAX_PBC_SELECTIONS))
+ vcd_error ("selection '%s': too many selections (%d > %d)",
+ _pbc->id, _nos, MAX_PBC_SELECTIONS);
+
+ if (_nos + _pbc->bsn > 100)
+ vcd_error ("selection '%s': BSN + NOS (%d + %d) > 100",
+ _pbc->id, _pbc->bsn, _nos);
+
+ _md->bsn = _pbc->bsn;
+ _md->nos = _nos;
+
+ vcd_assert (sizeof (PsdSelectionListFlags) == 1);
+
+ /* selection flags */
+ if (_vcd_obj_has_cap_p (obj, _CAP_4C_SVCD))
+ _md->flags.SelectionAreaFlag = true;
+ else
+ _md->flags.SelectionAreaFlag = false;
+
+ _md->flags.CommandListFlag = false;
+
+ vcd_assert (_pbc->lid < 0x8000);
+ _md->lid = uint16_to_be (_pbc->lid | (_pbc->rejected ? 0x8000 : 0));
+
+ _md->prev_ofs =
+ uint16_to_be (_lookup_psd_offset (obj, _pbc->prev_id, extended));
+ _md->next_ofs =
+ uint16_to_be (_lookup_psd_offset (obj, _pbc->next_id, extended));
+ _md->return_ofs =
+ uint16_to_be (_lookup_psd_offset (obj, _pbc->retn_id, extended));
+
+ switch (_pbc->selection_type)
+ {
+ case _SEL_NORMAL:
+ _md->default_ofs =
+ uint16_to_be (_lookup_psd_offset (obj, _pbc->default_id, extended));
+ break;
+
+ case _SEL_MULTI_DEF:
+ _md->default_ofs = uint16_to_be (PSD_OFS_MULTI_DEF);
+ if (_pbc->default_id)
+ vcd_warn ("ignoring default target '%s' for multi default selection '%s'",
+ _pbc->default_id, _pbc->id);
+ break;
+
+ case _SEL_MULTI_DEF_NO_NUM:
+ _md->default_ofs = uint16_to_be (PSD_OFS_MULTI_DEF_NO_NUM);
+ if (_pbc->default_id)
+ vcd_warn ("ignoring default target '%s' for multi default (w/o num) selection '%s'",
+ _pbc->default_id, _pbc->id);
+ break;
+
+ default:
+ vcd_assert_not_reached ();
+ break;
+ }
+
+ _md->timeout_ofs =
+ uint16_to_be (_lookup_psd_offset (obj, _pbc->timeout_id, extended));
+ _md->totime = _wtime (_pbc->timeout_time);
+
+ if (_pbc->loop_count > 0x7f)
+ vcd_warn ("loop count %d > 127", _pbc->loop_count);
+
+ _md->loop = (_pbc->loop_count > 0x7f) ? 0x7f : _pbc->loop_count;
+
+ if (_pbc->jump_delayed)
+ _md->loop |= 0x80;
+
+ /* timeout related sanity checks */
+ if (_pbc->loop_count > 0
+ && _pbc->timeout_time >= 0
+ && !_pbc->timeout_id
+ && !_nos)
+ vcd_warn ("PSD: selection '%s': neither timeout nor select target available, but neither loop count is infinite nor timeout wait time", _pbc->id);
+
+ if (_pbc->timeout_id && (_pbc->timeout_time < 0 || _pbc->loop_count <= 0))
+ vcd_warn ("PSD: selection '%s': timeout target '%s' is never used due to loop count or timeout wait time given", _pbc->id, _pbc->timeout_id);
+
+ if (_pbc->item_id)
+ {
+ const uint16_t _pin = _vcd_pbc_pin_lookup (obj, _pbc->item_id);
+
+ if (!_pin)
+ vcd_error ("PSD: referenced play item '%s' not found", _pbc->item_id);
+
+ _md->itemid = uint16_to_be (_pin);
+ }
+ else
+ _md->itemid = 0; /* play nothing */
+
+ /* sanity checks */
+ switch (_pbc->selection_type)
+ {
+ case _SEL_NORMAL:
+ break;
+
+ case _SEL_MULTI_DEF:
+ case _SEL_MULTI_DEF_NO_NUM:
+ if (_pbc->jump_delayed)
+ vcd_warn ("selection '%s': jump timing shall be immediate", _pbc->id);
+
+ if (_pbc->bsn != 1)
+ vcd_error ("selection '%s': BSN != 1 for multi default selection",
+ _pbc->id);
+
+ /* checking NOS == NOE */
+ if (!_pbc->item_id)
+ vcd_error ("selection '%s': play nothing play item not allowed for multidefault list",
+ _pbc->id);
+
+ {
+ mpeg_sequence_t *_seq;
+
+ if ((_seq = _vcd_obj_get_sequence_by_id ((VcdObj *) obj, _pbc->item_id))
+ || (_seq = _vcd_obj_get_sequence_by_entry_id ((VcdObj *) obj, _pbc->item_id)))
+ {
+ const unsigned _entries =
+ _cdio_list_length (_seq->entry_list) + 1;
+
+ if (_nos != _entries)
+ vcd_error ("selection '%s': number of entrypoints"
+ " (%d for sequence '%s') != number of selections (%d)",
+ _pbc->id, _entries, _pbc->item_id, _nos);
+ }
+ else
+ vcd_error ("selection '%s': play item '%s'"
+ " is requried to be sequence or entry point"
+ " item for multi default selecton",
+ _pbc->id, _pbc->item_id);
+ }
+
+ break;
+
+ default:
+ vcd_assert_not_reached ();
+ break;
+ }
+
+ /* fill selection array */
+ {
+ CdioListNode *node = NULL;
+ int idx = 0;
+
+ idx = 0;
+ _CDIO_LIST_FOREACH (node, _pbc->select_id_list)
+ {
+ const char *_id = _cdio_list_node_data (node);
+
+ _md->ofs[idx] =
+ uint16_to_be (_lookup_psd_offset (obj, _id, extended));
+
+ idx++;
+ }
+ }
+
+ if (extended || _vcd_obj_has_cap_p (obj, _CAP_4C_SVCD))
+ {
+ PsdSelectionListDescriptorExtended *_md2;
+ CdioListNode *node;
+ int n;
+
+ /* append extended selection areas */
+
+ _md2 = (void *) &_md->ofs[_nos];
+
+ _set_area_helper (&_md2->next_area, _pbc->next_area, _pbc->id);
+ _set_area_helper (&_md2->prev_area, _pbc->prev_area, _pbc->id);
+ _set_area_helper (&_md2->return_area, _pbc->return_area, _pbc->id);
+
+ _set_area_helper (&_md2->default_area, _pbc->default_area, _pbc->id);
+
+ n = 0;
+ if (_pbc->select_area_list)
+ _CDIO_LIST_FOREACH (node, _pbc->select_area_list)
+ {
+ const pbc_area_t *_area = _cdio_list_node_data (node);
+
+ _set_area_helper (&_md2->area[n], _area, _pbc->id);
+
+ n++;
+ }
+
+ vcd_assert (n == _nos);
+ }
+ }
+ break;
+
+ case PBC_END:
+ {
+ PsdEndListDescriptor *_md = buf;
+
+ _md->type = PSD_TYPE_END_LIST;
+
+ if (_vcd_obj_has_cap_p (obj, _CAP_4C_SVCD))
+ {
+ _md->next_disc = _pbc->next_disc;
+
+ if (_pbc->image_id)
+ {
+ uint16_t _pin = _vcd_pbc_pin_lookup (obj, _pbc->image_id);
+ mpeg_segment_t *_segment;
+
+ if (!_pbc->next_disc)
+ vcd_warn ("PSD: endlist '%s': change disc picture given,"
+ " but next volume is 0", _pbc->id);
+
+ if (!_pin)
+ vcd_error ("PSD: referenced play item '%s' not found",
+ _pbc->item_id);
+
+ _md->change_pic = uint16_to_be (_pin);
+
+ /* sanity checks */
+
+ _segment = _vcd_obj_get_segment_by_id ((VcdObj *) obj,
+ _pbc->image_id);
+
+ if (!_segment)
+ vcd_warn ("PSD: endlist '%s': referenced play item '%s'"
+ " is not a segment play item",
+ _pbc->id, _pbc->image_id);
+ else if (_segment->info->shdr[0].seen
+ || !(_segment->info->shdr[1].seen || _segment->info->shdr[2].seen))
+ vcd_warn ("PSD: endlist '%s': referenced play item '%s'"
+ " should be a still picture",
+ _pbc->id, _pbc->image_id);
+ }
+ }
+ else if (_pbc->next_disc || _pbc->image_id)
+ vcd_warn ("extended end list attributes ignored for non-SVCD");
+ }
+ break;
+
+ default:
+ vcd_assert_not_reached ();
+ break;
+ }
+}
+
+pbc_t *
+vcd_pbc_new (enum pbc_type_t type)
+{
+ pbc_t *_pbc;
+
+ _pbc = _vcd_malloc (sizeof (pbc_t));
+ _pbc->type = type;
+
+ switch (type)
+ {
+ case PBC_PLAYLIST:
+ _pbc->item_id_list = _cdio_list_new ();
+ break;
+
+ case PBC_SELECTION:
+ _pbc->select_id_list = _cdio_list_new ();
+ _pbc->select_area_list = _cdio_list_new ();
+ break;
+
+ case PBC_END:
+ break;
+
+ default:
+ vcd_assert_not_reached ();
+ break;
+ }
+
+ return _pbc;
+}
+
+/*
+ */
+
+bool
+_vcd_pbc_finalize (VcdObj *obj)
+{
+ CdioListNode *node;
+ unsigned offset = 0, offset_ext = 0;
+ unsigned lid;
+
+ lid = 1;
+ _CDIO_LIST_FOREACH (node, obj->pbc_list)
+ {
+ pbc_t *_pbc = _cdio_list_node_data (node);
+ unsigned length, length_ext = 0;
+
+ length = _vcd_pbc_node_length (obj, _pbc, false);
+ if (_vcd_obj_has_cap_p (obj, _CAP_PBC_X))
+ length_ext = _vcd_pbc_node_length (obj, _pbc, true);
+
+ /* round them up to... */
+ length = _vcd_ceil2block (length, INFO_OFFSET_MULT);
+ if (_vcd_obj_has_cap_p (obj, _CAP_PBC_X))
+ length_ext = _vcd_ceil2block (length_ext, INFO_OFFSET_MULT);
+
+ /* node may not cross sector boundary! */
+ offset = _vcd_ofs_add (offset, length, ISO_BLOCKSIZE);
+ if (_vcd_obj_has_cap_p (obj, _CAP_PBC_X))
+ offset_ext = _vcd_ofs_add (offset_ext, length_ext, ISO_BLOCKSIZE);
+
+ /* save start offsets */
+ _pbc->offset = offset - length;
+ if (_vcd_obj_has_cap_p (obj, _CAP_PBC_X))
+ _pbc->offset_ext = offset_ext - length_ext;
+
+ _pbc->lid = lid;
+
+ lid++;
+ }
+
+ obj->psd_size = offset;
+ if (_vcd_obj_has_cap_p (obj, _CAP_PBC_X))
+ obj->psdx_size = offset_ext;
+
+ vcd_debug ("pbc: psd size %d (extended psd %d)", offset, offset_ext);
+
+ return true;
+}