diff options
Diffstat (limited to 'contrib/libvcd/pbc.c')
-rw-r--r-- | contrib/libvcd/pbc.c | 880 |
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; +} |