vdr/xine-lib-vdr/src/input/vcd/libvcd Makefile.am Makefile.in bitvec.h bytesex.h bytesex_asm.h data_structures.c data_structures.h dict.h directory.c directory.h files.c image.c image_bincue.c image_cdrdao.c image_nrg.c image_sink.h inf.c info.c info_private.c info_private.h logging.c mpeg.c mpeg.h mpeg_stream.c mpeg_stream.h obj.h pbc.c pbc.h salloc.c salloc.h sector.c sector_private.h stream.c stream.h stream_stdio.c stream_stdio.h util.c util.h vcd.c vcd.h vcd_assert.h vcd_read.c vcd_read.h

Darren Salt pkg-vdr-dvb-changes@lists.alioth.debian.org
Mon, 04 Apr 2005 22:29:46 +0000


Update of /cvsroot/pkg-vdr-dvb/vdr/xine-lib-vdr/src/input/vcd/libvcd
In directory haydn:/tmp/cvs-serv2129/src/input/vcd/libvcd

Added Files:
	Makefile.am Makefile.in bitvec.h bytesex.h bytesex_asm.h 
	data_structures.c data_structures.h dict.h directory.c 
	directory.h files.c image.c image_bincue.c image_cdrdao.c 
	image_nrg.c image_sink.h inf.c info.c info_private.c 
	info_private.h logging.c mpeg.c mpeg.h mpeg_stream.c 
	mpeg_stream.h obj.h pbc.c pbc.h salloc.c salloc.h sector.c 
	sector_private.h stream.c stream.h stream_stdio.c 
	stream_stdio.h util.c util.h vcd.c vcd.h vcd_assert.h 
	vcd_read.c vcd_read.h 
Log Message:
Import of VDR-patched xine-lib.

--- NEW FILE: mpeg.h ---
/*
    $Id: mpeg.h,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2000 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
*/

#ifndef __VCD_MPEG_H__
#define __VCD_MPEG_H__

#include <string.h>

/* Public headers */
#include <libvcd/logging.h>
#include <libvcd/types.h>

/* Private headers */
#include "data_structures.h"

typedef enum {
  MPEG_VERS_INVALID = 0,
  MPEG_VERS_MPEG1 = 1,
  MPEG_VERS_MPEG2 = 2
} mpeg_vers_t;

PRAGMA_BEGIN_PACKED
struct vcd_mpeg_scan_data_t {
  uint8_t tag;
  uint8_t len;
  msf_t prev_ofs;
  msf_t next_ofs;
  msf_t back_ofs;
  msf_t forw_ofs;
} GNUC_PACKED;
PRAGMA_END_PACKED

#define struct_vcd_mpeg_scan_data_t_SIZEOF 14

#define VCD_MPEG_SCAN_DATA_WARNS 8

typedef struct {
  struct vcd_mpeg_packet_info {
    bool video[3];
    bool audio[3];
    bool ogt[4];

    bool padding;
    bool pem;
    bool zero;
    bool system_header;

    struct vcd_mpeg_scan_data_t *scan_data_ptr; /* points into actual packet memory! */

    enum aps_t {
      APS_NONE = 0,
      APS_I,    /* iframe */
      APS_GI,   /* gop + iframe */
      APS_SGI,  /* sequence + gop + iframe */
      APS_ASGI  /* aligned sequence + gop + iframe */
    } aps;
    double aps_pts;
    int aps_idx;

    bool has_pts;
    double pts;

    uint64_t scr;
    unsigned muxrate;

    bool gop;
    struct {
      uint8_t h, m, s, f;
    } gop_timecode;
  } packet;

  struct vcd_mpeg_stream_info {
    unsigned packets;

    mpeg_vers_t version;

    bool ogt[4];

    struct vcd_mpeg_stream_vid_info {
      bool seen;
      unsigned hsize;
      unsigned vsize;
      double aratio;
      double frate;
      unsigned bitrate;
      unsigned vbvsize;
      bool constrained_flag;

      VcdList *aps_list; /* filled up by vcd_mpeg_source */
      double last_aps_pts; /* temp, see ->packet */
      
    } shdr[3];

    struct vcd_mpeg_stream_aud_info {
      bool seen;
      unsigned layer;
      unsigned bitrate;
      unsigned sampfreq;
      enum {
        MPEG_STEREO = 1,
        MPEG_JOINT_STEREO,
        MPEG_DUAL_CHANNEL,
        MPEG_SINGLE_CHANNEL
      } mode;
    } ahdr[3];

    unsigned muxrate;

    bool seen_pts;
    double min_pts;
    double max_pts;

    double playing_time;

    unsigned scan_data;
    unsigned scan_data_warnings;
  } stream;
} VcdMpegStreamCtx;

int
vcd_mpeg_parse_packet (const void *buf, unsigned buflen, bool parse_pes,
                       VcdMpegStreamCtx *ctx);

typedef enum {
  MPEG_NORM_OTHER,
  MPEG_NORM_PAL,
  MPEG_NORM_NTSC,
  MPEG_NORM_FILM,
  MPEG_NORM_PAL_S,
  MPEG_NORM_NTSC_S
} mpeg_norm_t;

mpeg_norm_t 
vcd_mpeg_get_norm (const struct vcd_mpeg_stream_vid_info *_info);

enum vcd_mpeg_packet_type {
  PKT_TYPE_INVALID = 0,
  PKT_TYPE_VIDEO,
  PKT_TYPE_AUDIO,
  PKT_TYPE_OGT,
  PKT_TYPE_ZERO,
  PKT_TYPE_EMPTY
};

enum vcd_mpeg_packet_type
vcd_mpeg_packet_get_type (const struct vcd_mpeg_packet_info *_info);

struct vcd_mpeg_stream_vid_type {
  enum {
    VID_TYPE_NONE = 0,
    VID_TYPE_MOTION,
    VID_TYPE_STILL
  } type;
  enum {
    VID_NORM_OTHER = 0,
    VID_NORM_PAL,
    VID_NORM_NTSC
  } norm;
  enum {
    VID_RES_OTHER = 0,
    VID_RES_SIF,
    VID_RES_HALF_D1,
    VID_RES_2_3_D1,
    VID_RES_FULL_D2
  } resolution;
};

#endif /* __VCD_MPEG_H__ */


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: image_bincue.c ---
/*
    $Id: image_bincue.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2001 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 <stdlib.h>
#include <string.h>

#include <cdio/cdio.h>
#include <cdio/iso9660.h>

/* Public headers */
#include <libvcd/sector.h>
#include <libvcd/logging.h>

/* Private headers */
#include "vcd_assert.h"
#include "bytesex.h"
#include "image_sink.h"
#include "stream_stdio.h"
#include "util.h"

static const char _rcsid[] = "$Id: image_bincue.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $";

/* reader */

#define DEFAULT_VCD_DEVICE "videocd.bin"

/****************************************************************************
 * writer
 */

typedef struct {
  bool sector_2336_flag;
  VcdDataSink *bin_snk;
  VcdDataSink *cue_snk;
  char *bin_fname;
  char *cue_fname;

  bool init;
} _img_bincue_snk_t;

static void
_sink_init (_img_bincue_snk_t *_obj)
{
  if (_obj->init)
    return;

  if (!(_obj->bin_snk = vcd_data_sink_new_stdio (_obj->bin_fname)))
    vcd_error ("init failed");

  if (!(_obj->cue_snk = vcd_data_sink_new_stdio (_obj->cue_fname)))
    vcd_error ("init failed");

  _obj->init = true;  
}

static void
_sink_free (void *user_data)
{
  _img_bincue_snk_t *_obj = user_data;

  vcd_data_sink_destroy (_obj->bin_snk);
  vcd_data_sink_destroy (_obj->cue_snk);
  free (_obj->bin_fname);
  free (_obj->cue_fname);
  free (_obj);
}

static int
_set_cuesheet (void *user_data, const VcdList *vcd_cue_list)
{
  _img_bincue_snk_t *_obj = user_data;
  VcdListNode *node;
  int track_no, index_no;
  const vcd_cue_t *_last_cue = 0;
  
  _sink_init (_obj);

  vcd_data_sink_printf (_obj->cue_snk, "FILE \"%s\" BINARY\r\n",
			_obj->bin_fname);

  track_no = 0;
  index_no = 0;
  _VCD_LIST_FOREACH (node, (VcdList *) vcd_cue_list)
    {
      const vcd_cue_t *_cue = _vcd_list_node_data (node);
      
      msf_t _msf = { 0, 0, 0 };
      
      switch (_cue->type)
	{
	case VCD_CUE_TRACK_START:
	  track_no++;
	  index_no = 0;

	  vcd_data_sink_printf (_obj->cue_snk, 
				"  TRACK %2.2d MODE2/%d\r\n"
				"    FLAGS DCP\r\n",
				track_no, (_obj->sector_2336_flag ? 2336 : 2352));

	  if (_last_cue && _last_cue->type == VCD_CUE_PREGAP_START)
	    {
	      cdio_lba_to_msf (_last_cue->lsn, &_msf);

	      vcd_data_sink_printf (_obj->cue_snk, 
				    "    INDEX %2.2d %2.2x:%2.2x:%2.2x\r\n",
				    index_no, _msf.m, _msf.s, _msf.f);
	    }

	  index_no++;

	  cdio_lba_to_msf (_cue->lsn, &_msf);

	  vcd_data_sink_printf (_obj->cue_snk, 
				"    INDEX %2.2d %2.2x:%2.2x:%2.2x\r\n",
				index_no, _msf.m, _msf.s, _msf.f);
	  break;

	case VCD_CUE_PREGAP_START:
	  /* handled in next iteration */
	  break;

	case VCD_CUE_SUBINDEX:
	  vcd_assert (_last_cue != 0);

	  index_no++;
	  vcd_assert (index_no < 100);

	  cdio_lba_to_msf (_cue->lsn, &_msf);

	  vcd_data_sink_printf (_obj->cue_snk, 
				"    INDEX %2.2d %2.2x:%2.2x:%2.2x\r\n",
				index_no, _msf.m, _msf.s, _msf.f);
	  break;
	  
	case VCD_CUE_END:
	  vcd_data_sink_close (_obj->cue_snk);
	  return 0;
	  break;

	case VCD_CUE_LEADIN:
	  break;
	}

      _last_cue = _cue;
    }

  vcd_assert_not_reached ();

  return -1;
}
 
static int
_vcd_image_bincue_write (void *user_data, const void *data, uint32_t lsn)
{
  const char *buf = data;
  _img_bincue_snk_t *_obj = user_data;
  long offset = lsn;

  _sink_init (_obj);

  offset *= _obj->sector_2336_flag ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE_RAW;

  vcd_data_sink_seek(_obj->bin_snk, offset);
  
  if (_obj->sector_2336_flag)
    vcd_data_sink_write(_obj->bin_snk, buf + 12 + 4, M2RAW_SECTOR_SIZE, 1);
  else
    vcd_data_sink_write(_obj->bin_snk, buf, CDIO_CD_FRAMESIZE_RAW, 1);

  return 0;
}

static int
_sink_set_arg (void *user_data, const char key[], const char value[])
{
  _img_bincue_snk_t *_obj = user_data;

  if (!strcmp (key, "bin"))
    {
      free (_obj->bin_fname);

      if (!value)
	return -2;

      _obj->bin_fname = strdup (value);
    }
  else if (!strcmp (key, "cue"))
    {
      free (_obj->cue_fname);

      if (!value)
	return -2;

      _obj->cue_fname = strdup (value);
    }
  else if (!strcmp (key, "sector"))
    {
      if (!strcmp (value, "2336"))
	_obj->sector_2336_flag = true;
      else if (!strcmp (value, "2352"))
	_obj->sector_2336_flag = false;
      else
	return -2;
    }
  else
    return -1;

  return 0;
}

VcdImageSink *
vcd_image_sink_new_bincue (void)
{
  _img_bincue_snk_t *_data;

  vcd_image_sink_funcs _funcs = {
    .set_cuesheet = _set_cuesheet,
    .write        = _vcd_image_bincue_write,
    .free         = _sink_free,
    .set_arg      = _sink_set_arg
  };

  _data = _vcd_malloc (sizeof (_img_bincue_snk_t));

  _data->bin_fname = strdup ("videocd.bin");
  _data->cue_fname = strdup ("videocd.cue");

  return vcd_image_sink_new (_data, &_funcs);
}


--- NEW FILE: vcd.h ---
/*
    $Id: vcd.h,v 1.1 2005/04/04 22:29:43 dsalt-guest Exp $

    Copyright (C) 2000 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
*/

/* libvcd main header */

#ifndef __VCD_H__
#define __VCD_H__

/* Private headers */
#include "image_sink.h"
#include "mpeg_stream.h"
#include "stream.h"

#include <libvcd/types.h>

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

/* allocates and initializes a new VideoCD object */
VcdObj *
vcd_obj_new (vcd_type_t vcd_type);

/* VideoCD parameters */
typedef enum {
  VCD_PARM_INVALID = 0,
  VCD_PARM_VOLUME_ID,           /* char *          max length 32  */
  VCD_PARM_PUBLISHER_ID,        /* char *          max length 128 */
  VCD_PARM_PREPARER_ID,         /* char *          max length 128 */
  VCD_PARM_ALBUM_ID,            /* char *          max length 16  */
  VCD_PARM_VOLUME_COUNT,        /* unsigned        [1..65535]     */
  VCD_PARM_VOLUME_NUMBER,       /* unsigned        [0..65535]     */
  VCD_PARM_RESTRICTION,         /* unsigned        [0..3]         */
  VCD_PARM_NEXT_VOL_LID2,       /* bool */
  VCD_PARM_NEXT_VOL_SEQ2,       /* bool */
  VCD_PARM_APPLICATION_ID,      /* char *          max length 128 */
  VCD_PARM_SEC_TYPE,            /* unsigned        [2336, 2352]   */
  VCD_PARM_SVCD_VCD3_MPEGAV,    /* bool */
  VCD_PARM_SVCD_VCD3_ENTRYSVD,  /* bool */
  VCD_PARM_SVCD_VCD3_TRACKSVD,  /* bool */
  VCD_PARM_UPDATE_SCAN_OFFSETS, /* bool */
  VCD_PARM_RELAXED_APS,         /* bool */
  VCD_PARM_LEADOUT_PAUSE,       /* bool */
  VCD_PARM_LEADOUT_PREGAP,      /* unsigned        [0..300] */
  VCD_PARM_TRACK_PREGAP,        /* unsigned        [1..300] */
  VCD_PARM_TRACK_FRONT_MARGIN,  /* unsigned        [0..150] */
  VCD_PARM_TRACK_REAR_MARGIN    /* unsigned        [0..150] */
} vcd_parm_t;

/* sets VideoCD parameter */
int 
vcd_obj_set_param_uint (VcdObj *obj, vcd_parm_t param, unsigned arg);

int 
vcd_obj_set_param_str (VcdObj *obj, vcd_parm_t param, const char *arg);

int 
vcd_obj_set_param_bool (VcdObj *obj, vcd_parm_t param, bool arg);

/* add custom files; if raw_flag set, the data source has to include a
   mode2 subheader, and thus needs to be a multiple of 2336 byte blocksize */
int
vcd_obj_add_file (VcdObj *obj, const char iso_pathname[],
                  VcdDataSource *file, bool raw_flag);

int
vcd_obj_add_dir (VcdObj *obj, const char iso_pathname[]);

/* this is for actually adding mpeg items to VCD, returns 
   a negative value for error..  */

int 
vcd_obj_append_sequence_play_item (VcdObj *obj, VcdMpegSource *mpeg_source, 
                                   const char item_id[], 
                                   const char default_entry_id[]);

int 
vcd_obj_add_sequence_entry (VcdObj *obj, const char sequence_id[], 
                            double entry_time, const char entry_id[]);

int 
vcd_obj_add_sequence_pause (VcdObj *obj, const char sequence_id[], 
                            double pause_timestamp, const char pause_id[]);

int 
vcd_obj_add_segment_pause (VcdObj *obj, const char segment_id[], 
                           double pause_timestamp, const char pause_id[]);

int 
vcd_obj_append_segment_play_item (VcdObj *obj, VcdMpegSource *mpeg_source, 
                                  const char item_id[]);

/* warning -- api will change for pbc */
typedef struct _pbc_t pbc_t;

int
vcd_obj_append_pbc_node (VcdObj *obj, struct _pbc_t *_pbc);

/* removes item (sequence, entry, segment, ...) by id, returns
   negative value on error */
int 
vcd_obj_remove_item (VcdObj *obj, const char id[]);

/* returns image size in sectors */
long 
vcd_obj_get_image_size (VcdObj *obj);

/* this one is to be called when every parameter has been set and the
   image is about to be written. returns sectors to be written... */
long 
vcd_obj_begin_output (VcdObj *obj);

/* callback hook called every few (>|<) iterations, if it returns a value != 0
   the writing process gets aborted */
typedef struct
{
  long sectors_written;
  long total_sectors;
  int in_track;
  int total_tracks;
}
progress_info_t;

typedef int (*progress_callback_t) (const progress_info_t *progress_info,
                                    void *user_data);

/* writes the actual bin image file; a return value != 0 means the
   action was aborted by user or some other error has occured... */
int
vcd_obj_write_image (VcdObj *obj, VcdImageSink *image_sink,
                     progress_callback_t callback, void *user_data,
                     const time_t *create_time);

/* this should be called writing the bin and/or cue file is done---even if 
   an error occurred */
void 
vcd_obj_end_output (VcdObj *obj);

/* destructor for VideoCD objects; call this to destory a VideoCD
   object created by vcd_obj_new () */
void 
vcd_obj_destroy (VcdObj *obj);

const char *
vcd_version_string (bool full_text);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* __VCD_H__ */

/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: vcd.c ---
/*
    $Id: vcd.c,v 1.1 2005/04/04 22:29:43 dsalt-guest Exp $

    Copyright (C) 2000 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
*/
[...2368 lines suppressed...]
  return ("%s (GNU VCDImager) " VERSION "\n"
          "Written by Herbert Valerio Riedel and Rocky Bernstein.\n"
          "\n"
          "http://www.gnu.org/software/vcdimager/\n"
          "\n"
          "Copyright (C) 2000-2003 Herbert Valerio Riedel <hvr@gnu.org>\n"
	  "                   2003 Rocky Bernstein <rocky@panix.com>\n"
          "\n"
          "This is free software; see the source for copying conditions.  There is NO\n"
          "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
}


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: mpeg.c ---
/*
  $Id: mpeg.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

  Copyright (C) 2000 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
*/
[...1138 lines suppressed...]
    return PKT_TYPE_ZERO;
  else if (_info->ogt[0] 
           || _info->ogt[1] 
           || _info->ogt[2]
           || _info->ogt[3])
    return PKT_TYPE_OGT; 
  else if (_info->system_header || _info->padding)
    return PKT_TYPE_EMPTY;

  return PKT_TYPE_INVALID;
}


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: stream_stdio.h ---
/*
    $Id: stream_stdio.h,v 1.1 2005/04/04 22:29:43 dsalt-guest Exp $

    Copyright (C) 2000 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
*/


#ifndef __VCD_STREAM_STDIO_H__
#define __VCD_STREAM_STDIO_H__

/* Private headers */
#include "stream.h"

VcdDataSink*
vcd_data_sink_new_stdio(const char pathname[]);

VcdDataSource*
vcd_data_source_new_stdio(const char pathname[]);

#endif /* __VCD_STREAM_STDIO_H__ */


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: image.c ---
/*
    $Id: image.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2001 Herbert Valerio Riedel <hvr@gnu.org>
                  2002 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 Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <cdio/cdio.h>

/* Public headers */
#include <libvcd/sector.h>
#include <cdio/iso9660.h>

/* Private headers */
#include "vcd_assert.h"
#include "image_sink.h"
#include "util.h"

static const char _rcsid[] = "$Id: image.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $";

/*
 * VcdImageSink routines next.
 */

struct _VcdImageSink {
  void *user_data;
  vcd_image_sink_funcs op;
};

VcdImageSink *
vcd_image_sink_new (void *user_data, const vcd_image_sink_funcs *funcs)
{
  VcdImageSink *new_obj;

  new_obj = _vcd_malloc (sizeof (VcdImageSink));

  new_obj->user_data = user_data;
  new_obj->op = *funcs;

  return new_obj;
}

void
vcd_image_sink_destroy (VcdImageSink *obj)
{
  vcd_assert (obj != NULL);
  
  obj->op.free (obj->user_data);
  free (obj);
}

int
vcd_image_sink_set_cuesheet (VcdImageSink *obj, const VcdList *vcd_cue_list)
{
  vcd_assert (obj != NULL);

  return obj->op.set_cuesheet (obj->user_data, vcd_cue_list);
}

int
vcd_image_sink_write (VcdImageSink *obj, void *buf, uint32_t lsn)
{
  vcd_assert (obj != NULL);

  return obj->op.write (obj->user_data, buf, lsn);
}

/*!
  Set the arg "key" with "value" in the target device.
*/

int
vcd_image_sink_set_arg (VcdImageSink *obj, const char key[],
			const char value[])
{
  vcd_assert (obj != NULL);
  vcd_assert (obj->op.set_arg != NULL);
  vcd_assert (key != NULL);

  return obj->op.set_arg (obj->user_data, key, value);
}


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: stream.h ---
/*
    $Id: stream.h,v 1.1 2005/04/04 22:29:43 dsalt-guest Exp $

    Copyright (C) 2000 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
*/


#ifndef __VCD_STREAM_H__
#define __VCD_STREAM_H__

#include <libvcd/types.h>

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

/* typedef'ed IO functions prototypes */

typedef int(*vcd_data_open_t)(void *user_data);

typedef long(*vcd_data_read_t)(void *user_data, void *buf, long count);

typedef long(*vcd_data_write_t)(void *user_data, const void *buf,
                                  long count);

typedef long(*vcd_data_seek_t)(void *user_data, long offset);

typedef long(*vcd_data_stat_t)(void *user_data);

typedef int(*vcd_data_close_t)(void *user_data);

typedef void(*vcd_data_free_t)(void *user_data);


/* abstract data sink */

typedef struct _VcdDataSink VcdDataSink;

typedef struct {
  vcd_data_open_t open;
  vcd_data_seek_t seek;
  vcd_data_write_t write;
  vcd_data_close_t close;
  vcd_data_free_t free;
} vcd_data_sink_io_functions;

VcdDataSink* 
vcd_data_sink_new(void *user_data, const vcd_data_sink_io_functions *funcs);

long
vcd_data_sink_write(VcdDataSink* obj, const void *ptr, long size, long nmemb);

long
vcd_data_sink_printf (VcdDataSink *obj, const char format[], ...) GNUC_PRINTF(2, 3);

long
vcd_data_sink_seek(VcdDataSink* obj, long offset);

void
vcd_data_sink_destroy(VcdDataSink* obj);

void
vcd_data_sink_close(VcdDataSink* obj);

/* abstract data source */

typedef struct _VcdDataSource VcdDataSource;

typedef struct {
  vcd_data_open_t open;
  vcd_data_seek_t seek; 
  vcd_data_stat_t stat; 
  vcd_data_read_t read;
  vcd_data_close_t close;
  vcd_data_free_t free;
} vcd_data_source_io_functions;

VcdDataSource*
vcd_data_source_new(void *user_data, const vcd_data_source_io_functions *funcs);

long
vcd_data_source_read(VcdDataSource* obj, void *ptr, long size, long nmemb);

long
vcd_data_source_seek(VcdDataSource* obj, long offset);

long
vcd_data_source_stat(VcdDataSource* obj);

void
vcd_data_source_destroy(VcdDataSource* obj);

void
vcd_data_source_close(VcdDataSource* obj);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* __VCD_STREAM_H__ */


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: info.c ---
/*
    $Id: info.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2002,2003 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
*/
[...1951 lines suppressed...]
    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:
 */

--- NEW FILE: stream.c ---
/*
    $Id: stream.c,v 1.1 2005/04/04 22:29:43 dsalt-guest Exp $

    Copyright (C) 2000 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#include <cdio/cdio.h>

/* #define STREAM_DEBUG  */

/* Public headers */

#include <libvcd/logging.h>

/* Private headers */
#include "vcd_assert.h"
#include "stream.h"
#include "util.h"

static const char _rcsid[] = "$Id: stream.c,v 1.1 2005/04/04 22:29:43 dsalt-guest Exp $";

/* 
 * DataSource implementations
 */

struct _VcdDataSink {
  void* user_data;
  vcd_data_sink_io_functions op;
  int is_open;
  long position;
};

static void
_vcd_data_sink_open_if_necessary(VcdDataSink *obj)
{
  vcd_assert (obj != NULL);

  if (!obj->is_open) {
    if (obj->op.open(obj->user_data))
      vcd_error("could not opening output stream...");
    else {
      obj->is_open = 1;
      obj->position = 0;
    }
  }
}

VcdDataSink* 
vcd_data_sink_new(void *user_data, const vcd_data_sink_io_functions *funcs)
{
  VcdDataSink *new_obj;

  new_obj = _vcd_malloc(sizeof(VcdDataSink));

  new_obj->user_data = user_data;
  memcpy(&(new_obj->op), funcs, sizeof(vcd_data_sink_io_functions));

  return new_obj;
}

long
vcd_data_sink_seek(VcdDataSink* obj, long offset)
{
  vcd_assert (obj != NULL);

  _vcd_data_sink_open_if_necessary(obj);

  if (obj->position != offset) {
    vcd_warn("had to reposition DataSink from %ld to %ld!", obj->position, offset);
    obj->position = offset;
    return obj->op.seek(obj->user_data, offset);
  }

  return 0;
}

long
vcd_data_sink_write(VcdDataSink* obj, const void *ptr, long size, long nmemb)
{
  long written;

  vcd_assert (obj != NULL);
  
  _vcd_data_sink_open_if_necessary(obj);

  written = obj->op.write(obj->user_data, ptr, size*nmemb);
  obj->position += written;

  return written;
}

long
vcd_data_sink_printf (VcdDataSink *obj, const char format[], ...)
{
  char buf[4096] = { 0, };
  long retval;
  int len;

  va_list args;
  va_start (args, format);

  len = vsnprintf (buf, sizeof(buf), format, args);

  if (len < 0 || len > (sizeof (buf) - 1))
    vcd_error ("vsnprintf() returned %d", len);
  
  retval = vcd_data_sink_write (obj, buf, 1, len);

  va_end (args);

  return retval;
}

void
vcd_data_sink_close(VcdDataSink* obj)
{
  vcd_assert (obj != NULL);

  if (obj->is_open) {
    obj->op.close(obj->user_data);
    obj->is_open = 0;
    obj->position = 0;
  }
}

void
vcd_data_sink_destroy(VcdDataSink* obj)
{
  vcd_assert (obj != NULL);

  vcd_data_sink_close(obj);

  obj->op.free(obj->user_data);
}

/* 
 * DataSource implementations
 */

struct _VcdDataSource {
  void* user_data;
  vcd_data_source_io_functions op;
  int is_open;
  long position;
};

static void
_vcd_data_source_open_if_necessary(VcdDataSource *obj)
{
  vcd_assert (obj != NULL);

  if (!obj->is_open) {
    if (obj->op.open(obj->user_data))
      vcd_error ("could not opening input stream...");
    else {
#ifdef STREAM_DEBUG
      vcd_debug ("opened source...");
#endif
      obj->is_open = 1;
      obj->position = 0;
    }
  }
}

long
vcd_data_source_seek(VcdDataSource* obj, long offset)
{
  vcd_assert (obj != NULL);

  _vcd_data_source_open_if_necessary(obj);

  if (obj->position != offset) {
#ifdef STREAM_DEBUG
    vcd_warn("had to reposition DataSource from %ld to %ld!", obj->position, offset);
#endif
    obj->position = offset;
    return obj->op.seek(obj->user_data, offset);
  }

  return 0;
}

VcdDataSource*
vcd_data_source_new(void *user_data, const vcd_data_source_io_functions *funcs)
{
  VcdDataSource *new_obj;

  new_obj = _vcd_malloc (sizeof (VcdDataSource));

  new_obj->user_data = user_data;
  memcpy(&(new_obj->op), funcs, sizeof(vcd_data_source_io_functions));

  return new_obj;
}

long
vcd_data_source_read(VcdDataSource* obj, void *ptr, long size, long nmemb)
{
  long read_bytes;

  vcd_assert (obj != NULL);

  _vcd_data_source_open_if_necessary(obj);

  read_bytes = obj->op.read(obj->user_data, ptr, size*nmemb);
  obj->position += read_bytes;

  return read_bytes;
}

long
vcd_data_source_stat(VcdDataSource* obj)
{
  vcd_assert (obj != NULL);

  _vcd_data_source_open_if_necessary(obj);

  return obj->op.stat(obj->user_data);
}

void
vcd_data_source_close(VcdDataSource* obj)
{
  vcd_assert (obj != NULL);

  if (obj->is_open) {
#ifdef STREAM_DEBUG
    vcd_debug ("closed source...");
#endif
    obj->op.close(obj->user_data);
    obj->is_open = 0;
    obj->position = 0;
  }
}

void
vcd_data_source_destroy(VcdDataSource* obj)
{
  vcd_assert (obj != NULL);

  vcd_data_source_close(obj);

  obj->op.free(obj->user_data);
}


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: sector.c ---
/*
    $Id: sector.c,v 1.1 2005/04/04 22:29:43 dsalt-guest Exp $

    Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org>
              (C) 1998 Heiko Eissfeldt <heiko@colossus.escape.de>
                  portions used & Chris Smith

    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 <cdio/cdio.h>

#include <libvcd/types.h>

#include <libvcd/sector.h>

/* Private includes */
#include "vcd_assert.h"
#include "bytesex.h"
#include "salloc.h"
#include "sector_private.h"

static const char _rcsid[] = "$Id: sector.c,v 1.1 2005/04/04 22:29:43 dsalt-guest Exp $";

static const uint8_t sync_pattern[12] = {
  0x00, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0xff,
  0xff, 0xff, 0xff, 0x00
};

static void
build_address (void *buf, sectortype_t sectortype, uint32_t address)
{
  raw_cd_sector_t *sector = buf;
  
  vcd_assert (sizeof(raw_cd_sector_t) == CDIO_CD_FRAMESIZE_RAW-DATA_LEN);

  cdio_lba_to_msf(address, &(sector->msf));

  switch(sectortype) {
  case MODE_0:
    sector->mode = 0;
    break;
  case MODE_2:
  case MODE_2_FORM_1:
  case MODE_2_FORM_2:
    sector->mode = 2;
    break;
  default:
    vcd_assert_not_reached ();
    break;
  }
}

/* From cdrtools-1.11a25 */
static uint32_t
build_edc(const uint8_t inout[], int from, int upto)
{
  const uint8_t *p = inout+from;
  uint32_t result = 0;

  upto -= from-1;
  upto /= 4;
  while (--upto >= 0) {
    result = EDC_crctable[(result ^ *p++) & 0xffL] ^ (result >> 8);
    result = EDC_crctable[(result ^ *p++) & 0xffL] ^ (result >> 8);
    result = EDC_crctable[(result ^ *p++) & 0xffL] ^ (result >> 8);
    result = EDC_crctable[(result ^ *p++) & 0xffL] ^ (result >> 8);
  }
  return (result);
}

/* From cdrtools-1.11a40 */
static void
encode_L2_Q(uint8_t inout[4 + L2_RAW + 4 + 8 + L2_P + L2_Q])
{
  uint8_t *dps;
  uint8_t *dp;
  uint8_t *Q;
  int i, j;
        
  Q = inout + 4 + L2_RAW + 4 + 8 + L2_P;
  
  dps = inout;
  for (j = 0; j < 26; j++) {
    uint16_t a, b;

    a = b = 0;
    dp = dps;
    for (i = 0; i < 43; i++) {
      
      /* LSB */
      a ^= L2sq[i][*dp++];
      
      /* MSB */
      b ^= L2sq[i][*dp];
      
      dp += 2*44-1;
      if (dp >= &inout[(4 + L2_RAW + 4 + 8 + L2_P)]) {
        dp -= (4 + L2_RAW + 4 + 8 + L2_P);
      } 
    }
    Q[0]      = a >> 8;
    Q[26*2]   = a;
    Q[1]      = b >> 8;
    Q[26*2+1] = b;
    
    Q += 2;
    dps += 2*43;
  }
}

static void
encode_L2_P (uint8_t inout[4 + L2_RAW + 4 + 8 + L2_P])
{
  uint8_t *dp;
  unsigned char *P;
  int i, j;
  
  P = inout + 4 + L2_RAW + 4 + 8;
  
  for (j = 0; j < 43; j++) {
    uint16_t a;
    uint16_t b;
    
    a = b = 0;
    dp = inout;
    for (i = 19; i < 43; i++) {
      
      /* LSB */
      a ^= L2sq[i][*dp++];
      
      /* MSB */
      b ^= L2sq[i][*dp];
      
      dp += 2*43 -1;
    }
    P[0]      = a >> 8;
    P[43*2]   = a;
    P[1]      = b >> 8;
    P[43*2+1] = b;
    
    P += 2;
    inout += 2;
  }
}

/* Layer 2 Product code en/decoder */
static void
do_encode_L2 (void *buf, sectortype_t sectortype, uint32_t address)
{
  raw_cd_sector_t *raw_sector = buf;

  vcd_assert (buf != NULL);

  vcd_assert (sizeof (sync_pattern) == SYNC_LEN);
  vcd_assert (sizeof (mode2_form1_sector_t) == CDIO_CD_FRAMESIZE_RAW);
  vcd_assert (sizeof (mode2_form2_sector_t) == CDIO_CD_FRAMESIZE_RAW);
  vcd_assert (sizeof (mode0_sector_t) == CDIO_CD_FRAMESIZE_RAW);
  vcd_assert (sizeof (raw_cd_sector_t) == SYNC_LEN+HEADER_LEN);
  
  memset (raw_sector, 0, SYNC_LEN+HEADER_LEN);
  memcpy (raw_sector->sync, sync_pattern, sizeof (sync_pattern));

  switch (sectortype) {
  case MODE_0:
    {
      mode0_sector_t *sector = buf;

      memset(sector->data, 0, sizeof(sector->data));
    }
    break;
  case MODE_2:
    break;
  case MODE_2_FORM_1:
    {
      mode2_form1_sector_t *sector = buf;

      sector->edc = uint32_to_le(build_edc(buf, 16, 16+8+2048-1));

      encode_L2_P((uint8_t*)buf+SYNC_LEN);
      encode_L2_Q((uint8_t*)buf+SYNC_LEN);
    }
    break;
  case MODE_2_FORM_2:
    {
      mode2_form2_sector_t *sector = buf;

      sector->edc = uint32_to_le(build_edc(buf, 16, 16+8+2324-1));
    }
    break;
  default:
    vcd_assert_not_reached ();
  }

  build_address (buf, sectortype, address);
}

void
_vcd_make_mode2 (void *raw_sector, const void *data, uint32_t extent,
                 uint8_t fnum, uint8_t cnum, uint8_t sm, uint8_t ci)
{
  uint8_t *subhdr = (uint8_t*)raw_sector+16;

  vcd_assert (raw_sector != NULL);
  vcd_assert (data != NULL);
  vcd_assert (extent != SECTOR_NIL);

  memset (raw_sector, 0, CDIO_CD_FRAMESIZE_RAW);
  
  subhdr[0] = subhdr[4] = fnum;
  subhdr[1] = subhdr[5] = cnum;
  subhdr[2] = subhdr[6] = sm;
  subhdr[3] = subhdr[7] = ci;

  if (sm & SM_FORM2) 
    {
      memcpy ((char*)raw_sector+CDIO_CD_XA_SYNC_HEADER, data, 
              M2F2_SECTOR_SIZE);
      do_encode_L2 (raw_sector, MODE_2_FORM_2, extent+CDIO_PREGAP_SECTORS);
    } 
  else 
    {
      memcpy ((char*)raw_sector+CDIO_CD_XA_SYNC_HEADER, data, 
              CDIO_CD_FRAMESIZE);
      do_encode_L2 (raw_sector, MODE_2_FORM_1, extent+CDIO_PREGAP_SECTORS);
    } 
}

void
_vcd_make_raw_mode2 (void *raw_sector, const void *data, uint32_t extent)
{
  vcd_assert (raw_sector != NULL);
  vcd_assert (data != NULL);
  vcd_assert (extent != SECTOR_NIL);
  
  memset (raw_sector, 0, CDIO_CD_FRAMESIZE_RAW);

  memcpy ((char*)raw_sector+12+4, data, M2RAW_SECTOR_SIZE);
  do_encode_L2 (raw_sector, MODE_2, extent+CDIO_PREGAP_SECTORS);
}


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: bytesex_asm.h ---
/*
    $Id: bytesex_asm.h,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2001 Sven Ottemann <ac-logic@freenet.de>
                  2001 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
*/

#ifndef __VCD_BYTESEX_ASM_H__
#define __VCD_BYTESEX_ASM_H__
#if !defined(DISABLE_ASM_OPTIMIZE)

#include <libvcd/types.h>

#if defined(__powerpc__) && defined(__GNUC__)

inline static
uint32_t uint32_swap_le_be_asm(const uint32_t a)
{
  uint32_t b;

  __asm__ ("lwbrx %0,0,%1"
           :"=r"(b)
           :"r"(&a), "m"(a));

  return b;
}

inline static
uint16_t uint16_swap_le_be_asm(const uint16_t a)
{
  uint32_t b;

  __asm__ ("lhbrx %0,0,%1"
           :"=r"(b)
           :"r"(&a), "m"(a));

  return b;
}

#define UINT16_SWAP_LE_BE uint16_swap_le_be_asm
#define UINT32_SWAP_LE_BE uint32_swap_le_be_asm

#elif defined(__mc68000__) &&  defined(__STORMGCC__)

inline static 
uint32_t uint32_swap_le_be_asm(uint32_t a __asm__("d0"))
{
  /* __asm__("rolw #8,%0; swap %0; rolw #8,%0" : "=d" (val) : "0" (val)); */

  __asm__("move.l %1,d0;rol.w #8,d0;swap d0;rol.w #8,d0;move.l d0,%0"
          :"=r"(a)
          :"r"(a));

  return(a);
}

inline static
uint16_t uint16_swap_le_be_asm(uint16_t a __asm__("d0"))
{
  __asm__("move.l %1,d0;rol.w #8,d0;move.l d0,%0"
          :"=r"(a)
          :"r"(a));
  
  return(a);
}

#define UINT16_SWAP_LE_BE uint16_swap_le_be_asm
#define UINT32_SWAP_LE_BE uint32_swap_le_be_asm

#elif 0 && defined(__i386__) && defined(__GNUC__)

inline static 
uint32_t uint32_swap_le_be_asm(uint32_t a)
{
  __asm__("xchgb %b0,%h0\n\t"     /* swap lower bytes     */
	  "rorl $16,%0\n\t"       /* swap words           */
	  "xchgb %b0,%h0"         /* swap higher bytes    */
	  :"=q" (a)
	  : "0" (a));

  return(a);
}

inline static
uint16_t uint16_swap_le_be_asm(uint16_t a)
{
  __asm__("xchgb %b0,%h0"         /* swap bytes           */ 
	  : "=q" (a) 
	  :  "0" (a));
  
  return(a);
}

#define UINT16_SWAP_LE_BE uint16_swap_le_be_asm
#define UINT32_SWAP_LE_BE uint32_swap_le_be_asm

#endif

#endif /* !defined(DISABLE_ASM_OPTIMIZE) */
#endif /* __VCD_BYTESEX_ASM_H__ */


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: dict.h ---
/*
    $Id: dict.h,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2000 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
*/

#ifndef __VCD_DICT_H__
#define __VCD_DICT_H__

/* Public headers */
#include <libvcd/types.h>

/* Private headers */
#include "vcd_assert.h"
#include "obj.h"
#include "util.h"

struct _dict_t
{
  char *key;
  uint32_t sector;
  uint32_t length;
  void *buf;
  uint8_t flags;
};

static void
_dict_insert (VcdObj *obj, const char key[], uint32_t sector, uint32_t length, 
              uint8_t end_flags)
{
  struct _dict_t *_new_node;

  vcd_assert (key != NULL);
  vcd_assert (length > 0);

  if ((sector =_vcd_salloc (obj->iso_bitmap, sector, length)) == SECTOR_NIL)
    vcd_assert_not_reached ();

  _new_node = _vcd_malloc (sizeof (struct _dict_t));

  _new_node->key = strdup (key);
  _new_node->sector = sector;
  _new_node->length = length;
  _new_node->buf = _vcd_malloc (length * ISO_BLOCKSIZE);
  _new_node->flags = end_flags;

  _vcd_list_prepend (obj->buffer_dict_list, _new_node);
}

static 
int _dict_key_cmp (struct _dict_t *a, char *b)
{
  vcd_assert (a != NULL);
  vcd_assert (b != NULL);

  return !strcmp (a->key, b);
}

static 
int _dict_sector_cmp (struct _dict_t *a, uint32_t *b)
{
  vcd_assert (a != NULL);
  vcd_assert (b != NULL);

  return (a->sector <= *b && (*b - a->sector) < a->length);
}

static const struct _dict_t *
_dict_get_bykey (VcdObj *obj, const char key[])
{
  VcdListNode *node;

  vcd_assert (obj != NULL);
  vcd_assert (key != NULL);

  node = _vcd_list_find (obj->buffer_dict_list,
                         (_vcd_list_iterfunc) _dict_key_cmp,
                         (char *) key);
  
  if (node)
    return _vcd_list_node_data (node);

  return NULL;
}

static const struct _dict_t *
_dict_get_bysector (VcdObj *obj, uint32_t sector)
{
  VcdListNode *node;

  vcd_assert (obj != NULL);
  vcd_assert (sector != SECTOR_NIL);

  node = _vcd_list_find (obj->buffer_dict_list, 
                         (_vcd_list_iterfunc) _dict_sector_cmp, 
                         &sector);

  if (node)
    return _vcd_list_node_data (node);

  return NULL;
}

static uint8_t
_dict_get_sector_flags (VcdObj *obj, uint32_t sector)
{
  const struct _dict_t *p;

  vcd_assert (sector != SECTOR_NIL);

  p = _dict_get_bysector (obj, sector);

  if (p)
    return (((sector - p->sector)+1 == p->length)
            ? p->flags : 0);

  return 0;
}

static void *
_dict_get_sector (VcdObj *obj, uint32_t sector)
{
  const struct _dict_t *p;

  vcd_assert (sector != SECTOR_NIL);

  p = _dict_get_bysector (obj, sector);

  if (p)
    return ((char *)p->buf) + ((sector - p->sector) * ISO_BLOCKSIZE);

  return NULL;
}

static void
_dict_clean (VcdObj *obj)
{
  VcdListNode *node;

  while ((node = _vcd_list_begin (obj->buffer_dict_list)))
    {
      struct _dict_t *p = _vcd_list_node_data (node);

      free (p->key);
      free (p->buf);

      _vcd_list_node_free (node, true);
    }
}

#endif /* __VCD_DICT_H__ */

--- NEW FILE: inf.c ---
/*
    $Id: inf.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2002,2003 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 lower-level structures using a structure other
   than vcdinfo_t. For higher-level structures via the vcdinfo_t, see
   info.c
*/


#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdio.h>
#include <stddef.h>
#include <errno.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/util.h>

/* Eventually move above libvcd includes but having vcdinfo including. */
#include <libvcd/info.h>

/* Private headers */
#include "bytesex.h"
#include "info_private.h"
#include "pbc.h"

static const char _rcsid[] = "$Id: inf.c,v 1.1 2005/04/04 22:29:42 dsalt-guest 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];
}

const char *
vcdinf_area_str (const struct psd_area_t *_area)
{
  char *buf;

  if (!_area->x1  
      && !_area->y1
      && !_area->x2
      && !_area->y2)
    return "disabled";

  buf = _getbuf ();

  snprintf (buf, BUF_SIZE, "[%3d,%3d] - [%3d,%3d]",
            _area->x1, _area->y1,
            _area->x2, _area->y2);
            
  return buf;
}

/*!
   Return a string containing the VCD album id, or NULL if there is 
   some problem in getting this. 
*/
const char *
vcdinf_get_album_id(const InfoVcd *info)
{
  if (NULL==info) return NULL;
  return vcdinfo_strip_trail (info->album_desc, MAX_ALBUM_LEN);
}

/*!
  Return the VCD application ID.
  NULL is returned if there is some problem in getting this. 
*/
const char * 
vcdinf_get_application_id(const iso9660_pvd_t *pvd)
{
  if (NULL==pvd) return NULL;
  return(vcdinfo_strip_trail(pvd->application_id, MAX_APPLICATION_ID));
}

/*!
  Get autowait time value for PsdPlayListDescriptor *d.
  Time is in seconds unless it is -1 (unlimited).
*/
int
vcdinf_get_autowait_time (const PsdPlayListDescriptor *d) 
{
  return vcdinfo_get_wait_time (d->atime);
}

/*!
  Return the base selection number. VCD_INVALID_BSN is returned if there
  is an error.
*/
unsigned int
vcdinf_get_bsn(const PsdSelectionListDescriptor *psd)
{
  if (NULL==psd) return VCDINFO_INVALID_BSN;
  return(psd->bsn);
}

/*!
   Return a string giving VCD format (VCD 1.0 VCD 1.1, SVCD, ... 
   for this object.
*/
const char *
vcdinf_get_format_version_str (vcd_type_t vcd_type) 
{
  switch (vcd_type)
    {
    case VCD_TYPE_VCD:
      return ("VCD 1.0");
      break;
    case VCD_TYPE_VCD11:
      return ("VCD 1.1");
      break;
    case VCD_TYPE_VCD2:
      return ("VCD 2.0");
      break;
    case VCD_TYPE_SVCD:
      return ("SVCD");
      break;
    case VCD_TYPE_HQVCD:
      return ("HQVCD");
      break;
    case VCD_TYPE_INVALID:
      return ("INVALID");
      break;
    default:
      return ( "????");
    }
}

/*!
  Return loop count. 0 is infinite loop.
*/
uint16_t
vcdinf_get_loop_count (const PsdSelectionListDescriptor *psd) 
{
  return 0x7f & psd->loop;
}

/*!
  Return LOT offset
*/
uint16_t
vcdinf_get_lot_offset (const LotVcd *lot, unsigned int n) 
{
  return uint16_from_be (lot->offset[n]);
}

/*!
  Return the number of entries in the VCD.
*/
unsigned int
vcdinf_get_num_entries(const EntriesVcd *entries)
{
  if (NULL==entries) return 0;
  return (uint16_from_be (entries->entry_count));
}

/*!
  Return the number of segments in the VCD. 
*/
segnum_t
vcdinf_get_num_segments(const InfoVcd *info)
{
  if (NULL==info) return 0;
  return (uint16_from_be (info->item_count));
}

/*!
  Return number of LIDs. 
*/
lid_t
vcdinf_get_num_LIDs (const InfoVcd *info) 
{
  if (NULL==info) return 0;
  /* Should probably use _vcd_pbc_max_lid instead? */
  return uint16_from_be (info->lot_entries);
}

/*!
  Return the number of menu selections for selection list descriptor psd.
*/
unsigned int
vcdinf_get_num_selections(const PsdSelectionListDescriptor *psd)
{
  return psd->nos;
}

/*!
  Get play time value for PsdPlayListDescriptor *d.
  Time is in 1/15-second units.
*/
uint16_t
vcdinf_get_play_time (const PsdPlayListDescriptor *d) 
{
  if (NULL==d) return 0;
  return uint16_from_be (d->ptime);
}

/*!
   Return a string containing the VCD preparer id with trailing
   blanks removed.
*/
const char *
vcdinf_get_preparer_id(const iso9660_pvd_t *pvd)
{
  if (NULL==pvd) return NULL;
  return(vcdinfo_strip_trail(pvd->preparer_id, MAX_PREPARER_ID));
}

/*!
   Return a string containing the VCD publisher id with trailing
   blanks removed.
*/
const char *
vcdinf_get_publisher_id(const iso9660_pvd_t *pvd)
{
  if (NULL==pvd) return NULL;
  return(vcdinfo_strip_trail(pvd->publisher_id, MAX_PUBLISHER_ID));
}

/*!
  Return number of bytes in PSD. 
*/
uint32_t
vcdinf_get_psd_size (const InfoVcd *info)
{
  if (NULL==info) return 0;
  return uint32_from_be (info->psd_size);
}

/*!
   Return a string containing the VCD system id with trailing
   blanks removed.
*/
const char *
vcdinf_get_system_id(const iso9660_pvd_t *pvd)
{
  if (NULL==pvd) return NULL;
  return(vcdinfo_strip_trail(pvd->system_id, MAX_SYSTEM_ID));
}

/*!
  Get timeout wait time value for PsdPlayListDescriptor *d.
  Return VCDINFO_INVALID_OFFSET if d is NULL;
  Time is in seconds unless it is -1 (unlimited).
*/
uint16_t
vcdinf_get_timeout_offset (const PsdSelectionListDescriptor *d)
{
  if (NULL == d) return VCDINFO_INVALID_OFFSET;
  return uint16_from_be (d->timeout_ofs);
}

/*!
  Get timeout wait time value for PsdPlayListDescriptor *d.
  Time is in seconds unless it is -1 (unlimited).
*/
int
vcdinf_get_timeout_time (const PsdSelectionListDescriptor *d)
{
  return vcdinfo_get_wait_time (d->totime);
}

/*!
  Return the track number for entry n in obj. The first track starts
  at 1. Note this is one less than the track number reported in vcddump.
  (We don't count the header track?)
*/
track_t
vcdinf_get_track(const EntriesVcd *entries, const unsigned int entry_num)
{
  const unsigned int entry_count = uint16_from_be (entries->entry_count);
  /* Note entry_num is 0 origin. */
  return entry_num < entry_count ?
    from_bcd8 (entries->entry[entry_num].n):
    VCDINFO_INVALID_TRACK;
}

/*!
  Return the VCD volume count - the number of CD's in the collection.
*/
unsigned int 
vcdinf_get_volume_count(const InfoVcd *info) 
{
  if (NULL==info) return 0;
  return(uint16_from_be( info->vol_count));
}

/*!
  Return the VCD ID.
*/
const char *
vcdinf_get_volume_id(const iso9660_pvd_t *pvd) 
{
  if (NULL == pvd) return NULL;
  return(vcdinfo_strip_trail(pvd->volume_id, MAX_VOLUME_ID));
}

/*!
  Return the VCD volume num - the number of the CD in the collection.
  This is a number between 1 and the volume count.
*/
unsigned int
vcdinf_get_volume_num(const InfoVcd *info)
{
  if (NULL == info) return 0;
  return uint16_from_be(info->vol_id);
}

/*!
  Return the VCD volumeset ID.
  NULL is returned if there is some problem in getting this. 
*/
const char *
vcdinf_get_volumeset_id(const iso9660_pvd_t *pvd)
{
  if ( NULL == pvd ) return NULL;
  return vcdinfo_strip_trail(pvd->volume_set_id, MAX_VOLUMESET_ID);
}

/*!
  Get wait time value for PsdPlayListDescriptor *d.
  Time is in seconds unless it is -1 (unlimited).
*/
int
vcdinf_get_wait_time (const PsdPlayListDescriptor *d) 
{
  return vcdinfo_get_wait_time (d->wtime);
}

/*!
  Return true if loop has a jump delay
*/
bool
vcdinf_has_jump_delay (const PsdSelectionListDescriptor *psd) 
{
  if (NULL==psd) return false;
  return ((0x80 & psd->loop) != 0);
}
  
/*! 
  Comparison routine used in sorting. We compare LIDs and if those are 
  equal, use the offset.
  Note: we assume an unassigned LID is 0 and this compares as a high value.
  
  NOTE: Consider making static.
*/
int
vcdinf_lid_t_cmp (vcdinfo_offset_t *a, vcdinfo_offset_t *b)
{
  if (a->lid && b->lid)
    {
      if (a->lid > b->lid) return +1;
      if (a->lid < b->lid) return -1;
      vcd_warn ("LID %d at offset %d has same nunber as LID of offset %d", 
                a->lid, a->offset, b->offset);
    } 
  else if (a->lid) return -1;
  else if (b->lid) return +1;

  /* Failed to sort on LID, try offset now. */

  if (a->offset > b->offset) return +1;
  if (a->offset < b->offset) return -1;
  
  /* LIDS and offsets are equal. */
  return 0;
}

/* Get the LID from a given play-list descriptor. 
   VCDINFO_REJECTED_MASK is returned d on error or pld is NULL. 
*/
lid_t
vcdinf_pld_get_lid(const PsdPlayListDescriptor *pld)
{
  return (pld != NULL) 
    ? uint16_from_be (pld->lid) & VCDINFO_LID_MASK
    : VCDINFO_REJECTED_MASK;
}

/**
 \fn vcdinfo_pld_get_next_offset(const PsdPlayListDescriptor *pld); 
 \brief  Get next offset for a given PSD selector descriptor.  
 \return  VCDINFO_INVALID_OFFSET is returned on error or if pld has no "next"
 entry or pld is NULL. Otherwise the LID offset is returned.
 */
uint16_t
vcdinf_pld_get_next_offset(const PsdPlayListDescriptor *pld)
{
  if (NULL == pld) return VCDINFO_INVALID_OFFSET;
  return uint16_from_be (pld->next_ofs);
}

/*!
  Return number of items in LIDs. Return 0 if error or not found.
*/
int 
vcdinf_pld_get_noi (const PsdPlayListDescriptor *pld)
{
  if ( NULL == pld ) return 0;
  return pld->noi;
}

/*!
  Return the playlist item i in d. 
*/
uint16_t
vcdinf_pld_get_play_item(const PsdPlayListDescriptor *pld, unsigned int i)
{
  if (NULL==pld) return 0;
  return uint16_from_be(pld->itemid[i]);
}

/**
 \fn vcdinf_pld_get_prev_offset(const PsdPlayListDescriptor *pld);
 \brief Get prev offset for a given PSD selector descriptor. 
 \return  VCDINFO_INVALID_OFFSET is returned on error or if pld has no "prev"
 entry or pld is NULL. Otherwise the LID offset is returned.
 */
uint16_t
vcdinf_pld_get_prev_offset(const PsdPlayListDescriptor *pld)
{
  return (pld != NULL) ? 
    uint16_from_be (pld->prev_ofs) : VCDINFO_INVALID_OFFSET;
}

/**
 \fn vcdinf_pld_get_return_offset(const PsdPlayListDescriptor *pld);
 \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
vcdinf_pld_get_return_offset(const PsdPlayListDescriptor *pld)
{
  return (pld != NULL) ? 
    uint16_from_be (pld->return_ofs) : VCDINFO_INVALID_OFFSET;
}

/**
 * \fn vcdinfo_psd_get_default_offset(const PsdSelectionListDescriptor *psd);
 * \brief Get next offset for a given PSD selector descriptor. 
 * \return VCDINFO_INVALID_OFFSET is returned on error or if psd is
 * NULL. Otherwise the LID offset is returned.
 */
uint16_t
vcdinf_psd_get_default_offset(const PsdSelectionListDescriptor *psd)
{
  if (NULL == psd) return VCDINFO_INVALID_OFFSET;
  return uint16_from_be (psd->default_ofs);
}

/*!
  Get the item id for a given selection-list descriptor. 
  VCDINFO_REJECTED_MASK is returned on error or if psd is NULL. 
*/
uint16_t
vcdinf_psd_get_itemid(const PsdSelectionListDescriptor *psd)
{
  return (psd != NULL) ? uint16_from_be(psd->itemid) : VCDINFO_REJECTED_MASK;
}

/*!
  Get the LID from a given selection-list descriptor. 
  VCDINFO_REJECTED_MASK is returned on error or psd is NULL. 
*/
lid_t
vcdinf_psd_get_lid(const PsdSelectionListDescriptor *psd)
{
  return (psd != NULL) 
    ? uint16_from_be (psd->lid) & VCDINFO_LID_MASK
    : VCDINFO_REJECTED_MASK;
}

/*!
  Get the LID rejected status for a given PSD selector descriptor. 
  true is also returned d is NULL. 
*/
bool
vcdinf_psd_get_lid_rejected(const PsdSelectionListDescriptor *psd)
{
  return (psd != NULL) 
    ? vcdinfo_is_rejected(uint16_from_be(psd->lid)) 
    : true;
}

/**
 * \fn vcdinf_psd_get_next_offset(const PsdSelectionListDescriptor *psd);
 * \brief Get "next" offset for a given PSD selector descriptor. 
 * \return VCDINFO_INVALID_OFFSET is returned on error or if psd is
 * NULL. Otherwise the LID offset is returned.
 */
uint16_t
vcdinf_psd_get_next_offset(const PsdSelectionListDescriptor *psd)
{
  if (NULL == psd) return VCDINFO_INVALID_OFFSET;
  return uint16_from_be (psd->next_ofs);
}

/**
 * \fn vcdinf_psd_get_offset(const PsdSelectionListDescriptor *d, 
 *                           unsigned int entry_num);
 * \brief Get offset entry_num for a given PSD selector descriptor. 
 * \return VCDINFO_INVALID_OFFSET is returned if d on error or d is
 * NULL. Otherwise the LID offset is returned.
 */
uint16_t
vcdinf_psd_get_offset(const PsdSelectionListDescriptor *psd, 
                      unsigned int entry_num) 
{
  return (psd != NULL && entry_num < vcdinf_get_num_selections(psd))
    ? uint16_from_be (psd->ofs[entry_num]) : VCDINFO_INVALID_OFFSET;
}

/**
 \fn vcdinf_psd_get_prev_offset(const PsdSelectionListDescriptor *psd);
 \brief Get "prev" offset for a given PSD selector descriptor. 
 \return  VCDINFO_INVALID_OFFSET is returned on error or if psd has no "prev"
 entry or psd is NULL. Otherwise the LID offset is returned.
 */
uint16_t
vcdinf_psd_get_prev_offset(const PsdSelectionListDescriptor *psd)
{
  return (psd != NULL) ? 
    uint16_from_be (psd->prev_ofs) : VCDINFO_INVALID_OFFSET;
}

/**
 * \fn vcdinf_psd_get_return_offset(const PsdSelectionListDescriptor *psd);
 * \brief Get return offset for a given PSD selector descriptor. 
 \return  VCDINFO_INVALID_OFFSET is returned on error or if psd has no 
 "return" entry or psd is NULL. Otherwise the LID offset is returned.
 */
uint16_t
vcdinf_psd_get_return_offset(const PsdSelectionListDescriptor *psd)
{
  return (psd != NULL) ? 
    uint16_from_be (psd->return_ofs) : VCDINFO_INVALID_OFFSET;
}


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: files.c ---
/*
    $Id: files.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2000 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
*/
[...979 lines suppressed...]
                  "-- still assuming HQVCD", _info->version);
        _type = VCD_TYPE_HQVCD;
        break;
      }
  else
    vcd_warn ("INFO.SVD: signature not found");
  
  return _type;
}

/* eof */


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: image_cdrdao.c ---
/*
    $Id: image_cdrdao.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2001 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 <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Public headers */
#include <cdio/iso9660.h>
#include <libvcd/sector.h>
#include <libvcd/logging.h>

/* Private headers */
#include "vcd_assert.h"
#include "bytesex.h"
#include "image_sink.h"
#include "stream_stdio.h"
#include "util.h"
#include "vcd.h"

static const char _rcsid[] = "$Id: image_cdrdao.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $";

/* reader */

/****************************************************************************
 * writer
 */

typedef struct {
  bool sector_2336_flag;
  char *toc_fname;
  char *img_base;

  VcdDataSink *last_bin_snk;
  int last_snk_idx;
  bool last_pause;

  VcdList *vcd_cue_list;
} _img_cdrdao_snk_t;

static void
_sink_free (void *user_data)
{
  _img_cdrdao_snk_t *_obj = user_data;

  /* fixme -- destroy cue list */

  vcd_data_sink_destroy (_obj->last_bin_snk);
  free (_obj->toc_fname);
  free (_obj->img_base);
  free (_obj);
}

static int
_set_cuesheet (void *user_data, const VcdList *vcd_cue_list)
{
  _img_cdrdao_snk_t *_obj = user_data;
  VcdDataSink *toc_snk = vcd_data_sink_new_stdio (_obj->toc_fname);

  VcdListNode *node;

  int track_no, index_no;
  const vcd_cue_t *_last_cue = 0;
  unsigned last_track_lsn = 0;

  vcd_data_sink_printf (toc_snk, 
			"// CDRDAO TOC\n"
			"//  generated by %s\n\n"
			"CD_ROM_XA\n", vcd_version_string (false));

  _obj->vcd_cue_list = _vcd_list_new ();

  index_no = track_no = 0;
  _VCD_LIST_FOREACH (node, (VcdList *) vcd_cue_list)
    {
      const vcd_cue_t *_cue = _vcd_list_node_data (node);

      /* copy cue list while traversing */
      {
	vcd_cue_t *_cue2 = _vcd_malloc (sizeof (vcd_cue_t));
	*_cue2 = *_cue;
	_vcd_list_append (_obj->vcd_cue_list, _cue2);
      }
      
      switch (_cue->type)
	{
	case VCD_CUE_TRACK_START:
	  track_no++;
	  index_no = 0;

	  last_track_lsn = _cue->lsn;

	  vcd_data_sink_printf (toc_snk, 
				"\n// Track %d\n"
				"TRACK %s\n COPY\n",
				track_no,
				(_obj->sector_2336_flag ? "MODE2_FORM_MIX" : "MODE2_RAW"));

	  if (_last_cue && _last_cue->type == VCD_CUE_PREGAP_START)
	    vcd_data_sink_printf (toc_snk, 
				  " DATAFILE \"%s_%.2d_pregap.img\"\n"
				  " START\n",
				  _obj->img_base, track_no);

	  index_no++;

	  vcd_data_sink_printf (toc_snk, 
				" DATAFILE \"%s_%.2d.img\"\n",
				_obj->img_base, track_no);
	  break;

	case VCD_CUE_PREGAP_START:
	  /* handled in next iteration */
	  break;

	case VCD_CUE_SUBINDEX:
	  index_no++;

	  {
	    msf_t _msf = { 0, 0, 0 };

	    cdio_lba_to_msf (_cue->lsn - last_track_lsn, &_msf);

	    vcd_data_sink_printf (toc_snk, 
				  " INDEX %2.2x:%2.2x:%2.2x\n",
				  _msf.m, _msf.s, _msf.f);
	  }
	  break;

	case VCD_CUE_LEADIN:
          break;

	case VCD_CUE_END:
	  vcd_data_sink_printf (toc_snk, "\n// EOF\n");

	  vcd_data_sink_close (toc_snk);
	  vcd_data_sink_destroy (toc_snk);

	  return 0;
	  break;
	}

      _last_cue = _cue;
    }

  vcd_assert_not_reached ();

  return -1;
}
 
static int
_vcd_image_cdrdao_write (void *user_data, const void *data, uint32_t lsn)
{
  const char *buf = data;
  _img_cdrdao_snk_t *_obj = user_data;
  long offset;

  {
    VcdListNode *node;
    uint32_t _last = 0;
    uint32_t _ofs = 0;
    bool _lpregap = false;
    bool _pregap = false;

    int num = 0, in_track = 0;
    _VCD_LIST_FOREACH (node, _obj->vcd_cue_list)
      {
	const vcd_cue_t *_cue = _vcd_list_node_data (node);
    
	switch (_cue->type) 
	  {
	  case VCD_CUE_PREGAP_START:
	  case VCD_CUE_END:
	  case VCD_CUE_TRACK_START:
	    if (_cue->lsn && IN (lsn, _last, _cue->lsn - 1))
	      {
		vcd_assert (in_track == 0);
		in_track = num;
		_ofs = _last;
		_pregap = _lpregap;
	      }

	    _last = _cue->lsn;  
	    _lpregap = (_cue->type == VCD_CUE_PREGAP_START);

	    if (_cue->type == VCD_CUE_TRACK_START)
	      num++;
	    break;

	  default:
	    /* noop */
	    break;
	  }
      }

    vcd_assert (in_track != 0);
    vcd_assert (_obj->last_snk_idx <= in_track);

    if (_obj->last_snk_idx != in_track
	|| _obj->last_pause != _pregap)
      {
	char buf[4096] = { 0, };

	if (_obj->last_bin_snk)
	  vcd_data_sink_destroy (_obj->last_bin_snk);

	snprintf (buf, sizeof (buf),
		  "%s_%.2d%s.img",
		  _obj->img_base,
		  (_pregap ? in_track + 1 : in_track), 
		  (_pregap ? "_pregap" : ""));
	
	_obj->last_bin_snk = vcd_data_sink_new_stdio (buf);
	_obj->last_snk_idx = in_track;
	_obj->last_pause = _pregap;
      }

    vcd_assert (lsn >= _ofs);
    offset = lsn - _ofs;
  }

  offset *= _obj->sector_2336_flag ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE_RAW;

  vcd_data_sink_seek(_obj->last_bin_snk, offset);
  
  if (_obj->sector_2336_flag)
    vcd_data_sink_write(_obj->last_bin_snk, buf + 12 + 4, M2RAW_SECTOR_SIZE, 1);
  else
    vcd_data_sink_write(_obj->last_bin_snk, buf, CDIO_CD_FRAMESIZE_RAW, 1);
  
  return 0;
}

static int
_sink_set_arg (void *user_data, const char key[], const char value[])
{
  _img_cdrdao_snk_t *_obj = user_data;

  if (!strcmp (key, "toc"))
    {
      free (_obj->toc_fname);

      if (!value)
	return -2;

      _obj->toc_fname = strdup (value);
    }
  else if (!strcmp (key, "img_base"))
    {
      free (_obj->img_base);

      if (!value)
	return -2;

      _obj->img_base = strdup (value);
    }
  else if (!strcmp (key, "sector"))
    {
      if (!strcmp (value, "2336"))
	_obj->sector_2336_flag = true;
      else if (!strcmp (value, "2352"))
	_obj->sector_2336_flag = false;
      else
	return -2;
    }
  else
    return -1;

  return 0;
}

VcdImageSink *
vcd_image_sink_new_cdrdao (void)
{
  _img_cdrdao_snk_t *_data;
  
  vcd_image_sink_funcs _funcs = {
    .set_cuesheet = _set_cuesheet,
    .write        = _vcd_image_cdrdao_write,
    .free         = _sink_free,
    .set_arg      = _sink_set_arg
  };

  _data = _vcd_malloc (sizeof (_img_cdrdao_snk_t));

  _data->toc_fname = strdup ("videocd.toc");
  _data->img_base = strdup ("videocd");

  return vcd_image_sink_new (_data, &_funcs);
}


--- NEW FILE: bitvec.h ---
/*
    $Id: bitvec.h,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2000 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
*/

#ifndef __VCD_BITVEC_H__
#define __VCD_BITVEC_H__

#include <libvcd/types.h>

#include "vcd_assert.h"

static inline bool
_vcd_bit_set_p (const uint32_t n, const unsigned bit)
{
  return ((n >> bit) & 0x1) == 0x1;
}

static inline int
vcd_bitvec_align (int value, const int boundary)
{
  if (value % boundary)
    value += (boundary - (value % boundary));

  return value;
}

/*
 * PEEK 
 */

#define vcd_bitvec_peek_bits16(bitvec, offset) \
 vcd_bitvec_peek_bits ((bitvec), (offset), 16)

static inline uint32_t 
vcd_bitvec_peek_bits (const uint8_t bitvec[],
		      const unsigned offset, 
		      const unsigned bits)
{
  uint32_t result = 0;
  unsigned i = offset;

  vcd_assert (bits > 0 && bits <= 32);

#if 0
  j = 0;
  while (j < bits)
    if (i % 8 || (bits - j) < 8)
      {
	result <<= 1;
	if (_vcd_bit_set_p (bitvec[i >> 3], 7 - (i % 8)))
	  result |= 0x1;
	j++, i++;
      }
    else
      {
	result <<= 8;
	result |= bitvec[i >> 3];
	j += 8, i += 8;
      }
#else
  if (!(offset % 8) && !(bits % 8)) /* optimization */
    for (i = offset; i < (offset + bits); i+= 8)
      {
        result <<= 8;
        result |= bitvec[i >> 3];
      }
  else /* general case */
    for (i = offset; i < (offset + bits); i++)
      {
        result <<= 1;
        if (_vcd_bit_set_p (bitvec[i >> 3], 7 - (i % 8)))
          result |= 0x1;
      }
#endif  

  return result;
}

static inline uint32_t 
vcd_bitvec_peek_bits32 (const uint8_t bitvec[], unsigned offset)
{
  if (offset % 8)
    return vcd_bitvec_peek_bits (bitvec, offset, 32);

  offset >>= 3;

  return (bitvec[offset] << 24 
	  | bitvec[offset + 1] << 16
	  | bitvec[offset + 2] << 8
	  | bitvec[offset + 3]);
}

/* 
 * READ
 */

static inline uint32_t 
vcd_bitvec_read_bits (const uint8_t bitvec[], unsigned *offset, const unsigned bits)
{
  const unsigned i = *offset;
  
  *offset += bits;

  return vcd_bitvec_peek_bits (bitvec, i, bits);
}

static inline bool
vcd_bitvec_read_bit (const uint8_t bitvec[], unsigned *offset)
{
  const unsigned i = (*offset)++;

  return _vcd_bit_set_p (bitvec[i >> 3], 7 - (i % 8));
}

#endif /* __VCD_BITVEC_H__ */

--- NEW FILE: data_structures.h ---
/*
    $Id: data_structures.h,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2000 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
*/

#ifndef __VCD_DATA_STRUCTURES_H__
#define __VCD_DATA_STRUCTURES_H__

#include <libvcd/types.h>

/* opaque... */

typedef int (*_vcd_list_cmp_func) (void *data1, void *data2);

typedef int (*_vcd_list_iterfunc) (void *data, void *user_data);

/* methods */
VcdList *_vcd_list_new (void);

void _vcd_list_free (VcdList *list, int free_data);

unsigned _vcd_list_length (const VcdList *list);

void _vcd_list_sort (VcdList *list, _vcd_list_cmp_func cmp_func);

void _vcd_list_prepend (VcdList *list, void *data);

void _vcd_list_append (VcdList *list, void *data);

void _vcd_list_foreach (VcdList *list, _vcd_list_iterfunc func, void *user_data);

VcdListNode *_vcd_list_find (VcdList *list, _vcd_list_iterfunc cmp_func, void *user_data);

#define _VCD_LIST_FOREACH(node, list) \
 for (node = _vcd_list_begin (list); node; node = _vcd_list_node_next (node))

/* node ops */

VcdListNode *_vcd_list_at (VcdList *list, int idx);

VcdListNode *_vcd_list_begin (const VcdList *list);

VcdListNode *_vcd_list_end (VcdList *list);

VcdListNode *_vcd_list_node_next (VcdListNode *node);

void _vcd_list_node_free (VcdListNode *node, int free_data);

void *_vcd_list_node_data (VcdListNode *node);


/* n-way tree */

typedef struct _VcdTree VcdTree;
typedef struct _VcdTreeNode VcdTreeNode;

#define _VCD_CHILD_FOREACH(child, parent) \
 for (child = _vcd_tree_node_first_child (parent); child; child = _vcd_tree_node_next_sibling (child))

typedef int (*_vcd_tree_node_cmp_func) (VcdTreeNode *node1, 
                                        VcdTreeNode *node2);

typedef void (*_vcd_tree_node_traversal_func) (VcdTreeNode *node, 
                                               void *user_data);

VcdTree *_vcd_tree_new (void *root_data);

void _vcd_tree_destroy (VcdTree *tree, bool free_data);

VcdTreeNode *_vcd_tree_root (VcdTree *tree);

void _vcd_tree_node_sort_children (VcdTreeNode *node,
                                   _vcd_tree_node_cmp_func cmp_func);

void *_vcd_tree_node_data (VcdTreeNode *node);

void _vcd_tree_node_destroy (VcdTreeNode *node, bool free_data);

void *_vcd_tree_node_set_data (VcdTreeNode *node, void *new_data);

VcdTreeNode *_vcd_tree_node_append_child (VcdTreeNode *pnode, void *cdata);

VcdTreeNode *_vcd_tree_node_first_child (VcdTreeNode *node);

VcdTreeNode *_vcd_tree_node_next_sibling (VcdTreeNode *node);

VcdTreeNode *_vcd_tree_node_parent (VcdTreeNode *node);

VcdTreeNode *_vcd_tree_node_root (VcdTreeNode *node);

bool _vcd_tree_node_is_root (VcdTreeNode *node);

void _vcd_tree_node_traverse (VcdTreeNode *node, 
                              _vcd_tree_node_traversal_func trav_func,
                              void *user_data);

void
_vcd_tree_node_traverse_bf (VcdTreeNode *node, 
                            _vcd_tree_node_traversal_func trav_func,
                            void *user_data);
     
#endif /* __VCD_DATA_STRUCTURES_H__ */

/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */


--- NEW FILE: logging.c ---
/*
    $Id: logging.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org>
    Copyright (C) 2003 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 Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>

/* Public headers */
#include <libvcd/logging.h>

/* Private headers */
#include "vcd_assert.h"

static const char _rcsid[] = "$Id: logging.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $";

vcd_log_level_t vcd_loglevel_default = VCD_LOG_WARN;

static void
default_vcd_log_handler (vcd_log_level_t level, const char message[])
{
  switch (level)
    {
    case VCD_LOG_ERROR:
      if (level >= vcd_loglevel_default) {
        fprintf (stderr, "**ERROR: %s\n", message);
        fflush (stderr);
        exit (EXIT_FAILURE);
      }
      break;
    case VCD_LOG_DEBUG:
      if (level >= vcd_loglevel_default) {
        fprintf (stdout, "--DEBUG: %s\n", message);
      }
      break;
    case VCD_LOG_WARN:
      if (level >= vcd_loglevel_default) {
        fprintf (stdout, "++ WARN: %s\n", message);
      }
      break;
    case VCD_LOG_INFO:
      if (level >= vcd_loglevel_default) {
        fprintf (stdout, "   INFO: %s\n", message);
      }
      break;
    case VCD_LOG_ASSERT:
      if (level >= vcd_loglevel_default) {
        fprintf (stderr, "!ASSERT: %s\n", message);
        fflush (stderr);
      }
      abort ();
      break;
    default:
      vcd_assert_not_reached ();
      break;
    }

  fflush (stdout);
}

static vcd_log_handler_t _handler = default_vcd_log_handler;

vcd_log_handler_t
vcd_log_set_handler (vcd_log_handler_t new_handler)
{
  vcd_log_handler_t old_handler = _handler;

  _handler = new_handler;

  return old_handler;
}

static void
vcd_logv (vcd_log_level_t level, const char format[], va_list args)
{
  char buf[1024] = { 0, };
  static int in_recursion = 0;

  if (in_recursion)
    vcd_assert_not_reached ();

  in_recursion = 1;
  
  vsnprintf(buf, sizeof(buf)-1, format, args);

  _handler(level, buf);

  in_recursion = 0;
}

void
vcd_log (vcd_log_level_t level, const char format[], ...)
{
  va_list args;
  va_start (args, format);
  vcd_logv (level, format, args);
  va_end (args);
}

#define VCD_LOG_TEMPLATE(level, LEVEL) \
void \
vcd_ ## level (const char format[], ...) \
{ \
  va_list args; \
  va_start (args, format); \
  vcd_logv (VCD_LOG_ ## LEVEL, format, args); \
  va_end (args); \
} 

VCD_LOG_TEMPLATE(debug, DEBUG)
VCD_LOG_TEMPLATE(info, INFO)
VCD_LOG_TEMPLATE(warn, WARN)
VCD_LOG_TEMPLATE(error, ERROR)

#undef VCD_LOG_TEMPLATE


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: pbc.h ---
/*
    $Id: pbc.h,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2000 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
*/

#ifndef __VCD_PBC_H__
#define __VCD_PBC_H__

#include <libvcd/types.h>

/* Private includes */
#include "data_structures.h"
#include "util.h"
#include "vcd.h"

enum pbc_type_t {
  PBC_INVALID = 0,
  PBC_PLAYLIST,
  PBC_SELECTION,
  PBC_END
};

typedef struct psd_area_t pbc_area_t; /* fixme */
#define pbc_area_t_SIZEOF struct_psd_area_t_SIZEOF

static inline pbc_area_t *
vcd_pbc_area_new (uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2)
{
  pbc_area_t *_new_area = _vcd_malloc (sizeof (pbc_area_t));

  _new_area->x1 = x1;
  _new_area->y1 = y1;
  _new_area->x2 = x2;
  _new_area->y2 = y2;

  return _new_area;
}

/* typedef struct _pbc_t pbc_t; */

struct _pbc_t {
  enum pbc_type_t type;

  char *id;

  bool rejected;

  /* pbc ref check */
  bool referenced;

  /* used for play/selection lists */
  char *prev_id;
  char *next_id;
  char *retn_id;

  /* used for play lists */
  double playing_time;
  int wait_time;
  int auto_pause_time;
  VcdList *item_id_list; /* char */

  /* used for selection lists */
  enum selection_type_t {
    _SEL_NORMAL = 0,
    _SEL_MULTI_DEF,
    _SEL_MULTI_DEF_NO_NUM
  } selection_type;

  pbc_area_t *prev_area;
  pbc_area_t *next_area;
  pbc_area_t *return_area;
  pbc_area_t *default_area; /* depends on selection_type */
  VcdList *select_area_list; /* pbc_area_t */

  unsigned bsn;
  char *default_id;
  char *timeout_id;
  int timeout_time;
  unsigned loop_count;
  bool jump_delayed;
  char *item_id;
  VcdList *select_id_list; /* char */

  /* used for end lists */
  char *image_id;
  unsigned next_disc;

  /* computed values */
  unsigned lid;
  unsigned offset;
  unsigned offset_ext;
};

enum item_type_t {
  ITEM_TYPE_NOTFOUND = 0,
  ITEM_TYPE_NOOP,
  ITEM_TYPE_TRACK,
  ITEM_TYPE_ENTRY,
  ITEM_TYPE_SEGMENT,
  ITEM_TYPE_PBC
};

/* functions */

pbc_t *
vcd_pbc_new (enum pbc_type_t type);

pbc_t *
_vcd_pbc_init (pbc_t *_pbc);

void
vcd_pbc_destroy (pbc_t *obj);

unsigned
_vcd_pbc_lid_lookup (const VcdObj *obj, const char item_id[]);

enum item_type_t
_vcd_pbc_lookup (const VcdObj *obj, const char item_id[]);

uint16_t
_vcd_pbc_pin_lookup (const VcdObj *obj, const char item_id[]);

unsigned 
_vcd_pbc_list_calc_size (const pbc_t *_pbc, bool extended);

bool
_vcd_pbc_finalize (VcdObj *obj);

bool
_vcd_pbc_available (const VcdObj *obj);

uint16_t
_vcd_pbc_max_lid (const VcdObj *obj);

void
_vcd_pbc_node_write (const VcdObj *obj, const pbc_t *_pbc, void *buf,
		     bool extended);

void
_vcd_pbc_check_unreferenced (const VcdObj *obj);

#endif /* __VCD_PBC_H__ */

--- NEW FILE: image_sink.h ---
/*
    $Id: image_sink.h,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2001 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
*/

#ifndef __VCD_IMAGE_SINK_H__
#define __VCD_IMAGE_SINK_H__

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <cdio/cdio.h>
#include <libvcd/types.h>

/* Private includes */
#include "data_structures.h"
#include "stream.h"

/* VcdImageSink ( --> image writer) */

typedef struct _VcdImageSink VcdImageSink;

typedef struct {
  uint32_t lsn;
  enum {
    VCD_CUE_TRACK_START = 1, /* INDEX 0 -> 1 transition, TOC entry */
    VCD_CUE_PREGAP_START,    /* INDEX = 0 start */
    VCD_CUE_SUBINDEX,        /* INDEX++; sub-index */
    VCD_CUE_END,             /* lead-out start */
    VCD_CUE_LEADIN,          /* lead-in start */
  } type;
} vcd_cue_t;

typedef struct {
  int (*set_cuesheet) (void *user_data, const VcdList *vcd_cue_list);
  int (*write) (void *user_data, const void *buf, lsn_t lsn);
  void (*free) (void *user_data);
  int (*set_arg) (void *user_data, const char key[], const char value[]);
} vcd_image_sink_funcs;

VcdImageSink *
vcd_image_sink_new (void *user_data, const vcd_image_sink_funcs *funcs);

void
vcd_image_sink_destroy (VcdImageSink *obj);

int
vcd_image_sink_set_cuesheet (VcdImageSink *obj, const VcdList *vcd_cue_list);

int
vcd_image_sink_write (VcdImageSink *obj, void *buf, lsn_t lsn);

/*!
  Set the arg "key" with "value" in the target device.
*/
int
vcd_image_sink_set_arg (VcdImageSink *obj, const char key[], 
			const char value[]);

VcdImageSink * vcd_image_sink_new_nrg (void);
VcdImageSink * vcd_image_sink_new_bincue (void);
VcdImageSink * vcd_image_sink_new_cdrdao (void);

#endif /* __VCD_IMAGE_SINK_H__ */

--- NEW FILE: obj.h ---
/*
    $Id: obj.h,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2000 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
*/

#ifndef __VCD_OBJ_H__
#define __VCD_OBJ_H__

#include <cdio/iso9660.h>
#include <libvcd/files.h>

/* Private headers */
#include "data_structures.h"
#include "directory.h"
#include "image_sink.h"
#include "mpeg_stream.h"
#include "salloc.h"
#include "vcd.h"

typedef struct {
  double time;
  struct aps_data aps;
  char *id;
} entry_t;

typedef struct {
  double time;
  char *id;
} pause_t;

typedef struct {
  VcdMpegSource *source;
  char *id;
  const struct vcd_mpeg_stream_info *info;

  VcdList *pause_list; /* pause_t */

  char *default_entry_id;
  VcdList *entry_list; /* entry_t */

  /* pbc ref check */
  bool referenced;

  /* computed on sector allocation */
  unsigned relative_start_extent; /* relative to iso data end */
} mpeg_sequence_t;

/* work in progress -- fixme rename all occurences */
#define mpeg_track_t mpeg_sequence_t
#define mpeg_track_list mpeg_sequence_list 

typedef struct {
  VcdMpegSource *source;
  char *id;
  const struct vcd_mpeg_stream_info *info;

  VcdList *pause_list; /* pause_t */

  /* pbc ref check */
  bool referenced;

  /* computed through info */
  unsigned segment_count;

  /* computed on sector allocation */
  unsigned start_extent;
} mpeg_segment_t;


typedef struct {
  char *iso_pathname;
  VcdDataSource *file;
  bool raw_flag;
  
  uint32_t size;
  uint32_t start_extent;
  uint32_t sectors;
} custom_file_t;

struct _VcdObj {
  vcd_type_t type;

  /* VCD 3.0 chinese SVCD compat flags */
  bool svcd_vcd3_mpegav;
  bool svcd_vcd3_entrysvd;
  bool svcd_vcd3_tracksvd;
  bool svcd_vcd3_spiconsv;

  bool update_scan_offsets;
  bool relaxed_aps;

  unsigned leadout_pregap;
  unsigned track_pregap;
  unsigned track_front_margin;
  unsigned track_rear_margin;

  /* output */
  VcdImageSink *image_sink;

  /* ... */
  unsigned iso_size;
  char *iso_volume_label;
  char *iso_publisher_id;
  char *iso_application_id;
  char *iso_preparer_id;

  char *info_album_id;
  unsigned info_volume_count;
  unsigned info_volume_number;
  unsigned info_restriction;
  bool info_use_seq2;
  bool info_use_lid2;

  /* input */
  unsigned mpeg_segment_start_extent;
  VcdList *mpeg_segment_list; /* mpeg_segment_t */

  VcdList *mpeg_sequence_list; /* mpeg_sequence_t */

  unsigned relative_end_extent; /* last mpeg sequence track end extent */

  /* PBC */
  VcdList *pbc_list; /* pbc_t */
  unsigned psd_size;
  unsigned psdx_size;

  /* custom files */
  unsigned ext_file_start_extent; 
  unsigned custom_file_start_extent; 
  VcdList *custom_file_list; /* custom_file_t */
  VcdList *custom_dir_list; /* char */

  /* dictionary */
  VcdList *buffer_dict_list;

  /* aggregates */
  VcdSalloc *iso_bitmap;

  VcdDirectory *dir;

  /* state info */
  bool in_output;

  unsigned sectors_written;
  unsigned in_track;

  long last_cb_call;

  progress_callback_t progress_callback;
  void *callback_user_data;
};

/* private functions */

mpeg_sequence_t *
_vcd_obj_get_sequence_by_id (VcdObj *obj, const char sequence_id[]);

mpeg_sequence_t *
_vcd_obj_get_sequence_by_entry_id (VcdObj *obj, const char entry_id[]);

mpeg_segment_t *
_vcd_obj_get_segment_by_id (VcdObj *obj, const char segment_id[]);

enum vcd_capability_t {
  _CAP_VALID,
  _CAP_MPEG1,
  _CAP_MPEG2,
  _CAP_PBC,
  _CAP_PBC_X,
  _CAP_TRACK_MARGINS,
  _CAP_4C_SVCD,
  _CAP_PAL_BITS
};

bool
_vcd_obj_has_cap_p (const VcdObj *obj, enum vcd_capability_t capability);

#endif /* __VCD_OBJ_H__ */


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: bytesex.h ---
/*
    $Id: bytesex.h,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2000 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
*/

#ifndef __VCD_BYTESEX_H__
#define __VCD_BYTESEX_H__

#include <cdio/cdio.h>
#include <libvcd/types.h>
#include <libvcd/logging.h>

/* Private includes */
#include "bytesex_asm.h"

/* generic byteswap routines */

#define UINT16_SWAP_LE_BE_C(val) ((uint16_t) ( \
    (((uint16_t) (val) & (uint16_t) 0x00ffU) << 8) | \
    (((uint16_t) (val) & (uint16_t) 0xff00U) >> 8)))

#define UINT32_SWAP_LE_BE_C(val) ((uint32_t) ( \
    (((uint32_t) (val) & (uint32_t) 0x000000ffU) << 24) | \
    (((uint32_t) (val) & (uint32_t) 0x0000ff00U) <<  8) | \
    (((uint32_t) (val) & (uint32_t) 0x00ff0000U) >>  8) | \
    (((uint32_t) (val) & (uint32_t) 0xff000000U) >> 24)))

#define UINT64_SWAP_LE_BE_C(val) ((uint64_t) ( \
    (((uint64_t) (val) & (uint64_t) UINT64_C(0x00000000000000ff)) << 56) | \
    (((uint64_t) (val) & (uint64_t) UINT64_C(0x000000000000ff00)) << 40) | \
    (((uint64_t) (val) & (uint64_t) UINT64_C(0x0000000000ff0000)) << 24) | \
    (((uint64_t) (val) & (uint64_t) UINT64_C(0x00000000ff000000)) <<  8) | \
    (((uint64_t) (val) & (uint64_t) UINT64_C(0x000000ff00000000)) >>  8) | \
    (((uint64_t) (val) & (uint64_t) UINT64_C(0x0000ff0000000000)) >> 24) | \
    (((uint64_t) (val) & (uint64_t) UINT64_C(0x00ff000000000000)) >> 40) | \
    (((uint64_t) (val) & (uint64_t) UINT64_C(0xff00000000000000)) >> 56)))

#ifndef UINT16_SWAP_LE_BE
# define UINT16_SWAP_LE_BE UINT16_SWAP_LE_BE_C
#endif

#ifndef UINT32_SWAP_LE_BE
# define UINT32_SWAP_LE_BE UINT32_SWAP_LE_BE_C
#endif

#ifndef UINT64_SWAP_LE_BE
# define UINT64_SWAP_LE_BE UINT64_SWAP_LE_BE_C
#endif

inline static 
uint16_t uint16_swap_le_be (const uint16_t val)
{
  return UINT16_SWAP_LE_BE (val);
}

inline static 
uint32_t uint32_swap_le_be (const uint32_t val)
{
  return UINT32_SWAP_LE_BE (val);
}

inline static 
uint64_t uint64_swap_le_be (const uint64_t val)
{
  return UINT64_SWAP_LE_BE (val);
}

# define UINT8_TO_BE(val)      ((uint8_t) (val))
# define UINT8_TO_LE(val)      ((uint8_t) (val))
#ifdef WORDS_BIGENDIAN
# define UINT16_TO_BE(val)     ((uint16_t) (val))
# define UINT16_TO_LE(val)     ((uint16_t) UINT16_SWAP_LE_BE(val))

# define UINT32_TO_BE(val)     ((uint32_t) (val))
# define UINT32_TO_LE(val)     ((uint32_t) UINT32_SWAP_LE_BE(val))

# define UINT64_TO_BE(val)     ((uint64_t) (val))
# define UINT64_TO_LE(val)     ((uint64_t) UINT64_SWAP_LE_BE(val))
#else
# define UINT16_TO_BE(val)     ((uint16_t) UINT16_SWAP_LE_BE(val))
# define UINT16_TO_LE(val)     ((uint16_t) (val))

# define UINT32_TO_BE(val)     ((uint32_t) UINT32_SWAP_LE_BE(val))
# define UINT32_TO_LE(val)     ((uint32_t) (val))

# define UINT64_TO_BE(val)     ((uint64_t) UINT64_SWAP_LE_BE(val))
# define UINT64_TO_LE(val)     ((uint64_t) (val))
#endif

/* symmetric conversions */
#define UINT8_FROM_BE(val)     (UINT8_TO_BE (val))
#define UINT8_FROM_LE(val)     (UINT8_TO_LE (val))
#define UINT16_FROM_BE(val)    (UINT16_TO_BE (val))
#define UINT16_FROM_LE(val)    (UINT16_TO_LE (val))
#define UINT32_FROM_BE(val)    (UINT32_TO_BE (val))
#define UINT32_FROM_LE(val)    (UINT32_TO_LE (val))
#define UINT64_FROM_BE(val)    (UINT64_TO_BE (val))
#define UINT64_FROM_LE(val)    (UINT64_TO_LE (val))

/* converter function template */
#define CVT_TO_FUNC(bits) \
 static inline uint ## bits ## _t \
 uint ## bits ## _to_be (uint ## bits ## _t val) \
 { return UINT ## bits ## _TO_BE (val); } \
 static inline uint ## bits ## _t \
 uint ## bits ## _to_le (uint ## bits ## _t val) \
 { return UINT ## bits ## _TO_LE (val); } \

CVT_TO_FUNC(8)
CVT_TO_FUNC(16)
CVT_TO_FUNC(32)
CVT_TO_FUNC(64)

#undef CVT_TO_FUNC

#define uint16_from_be(val)    (uint16_to_be (val))
#define uint16_from_le(val)    (uint16_to_le (val))
#define uint32_from_be(val)    (uint32_to_be (val))
#define uint32_from_le(val)    (uint32_to_le (val))

#endif /* __VCD_BYTESEX_H__ */


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: pbc.c ---
/*
    $Id: pbc.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2000 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>

/* Public headers */
#include <libvcd/logging.h>
#include <libvcd/files.h>
#include <libvcd/types.h>

/* FIXME! Make this really private. */
#include <libvcd/files_private.h>

/* Private headers */
#include "vcd_assert.h"
#include "bytesex.h"
#include "obj.h"
#include "pbc.h"
#include "util.h"

static const char _rcsid[] = "$Id: pbc.c,v 1.1 2005/04/04 22:29:42 dsalt-guest 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[])
{
  VcdListNode *node;

  _VCD_LIST_FOREACH (node, obj->pbc_list)
    {
      pbc_t *_pbc = _vcd_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[])
{
  VcdListNode *node;
  unsigned n = 1;

  _VCD_LIST_FOREACH (node, obj->pbc_list)
    {
      pbc_t *_pbc = _vcd_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 < 100)
	return ITEM_TYPE_TRACK;
      else if (id < 600)
	return ITEM_TYPE_ENTRY;
      else if (id < 2980)
	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;
  VcdListNode *node;

  if (!item_id)
    return 0;

  /* check sequence items */

  n = 0;
  _VCD_LIST_FOREACH (node, obj->mpeg_sequence_list)
    {
      mpeg_sequence_t *_sequence = _vcd_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;
  _VCD_LIST_FOREACH (node, obj->mpeg_sequence_list)
    {
      mpeg_sequence_t *_sequence = _vcd_list_node_data (node);
      VcdListNode *node2;

      /* default entry point */

      if (_sequence->default_entry_id 
	  && !strcmp (item_id, _sequence->default_entry_id))
	return n + 100;
      n++;

      /* additional entry points */

      _VCD_LIST_FOREACH (node2, _sequence->entry_list)
	{
	  entry_t *_entry = _vcd_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;
  _VCD_LIST_FOREACH (node, obj->mpeg_segment_list)
    {
      mpeg_segment_t *_segment = _vcd_list_node_data (node);

      vcd_assert (n < 1980);

      if (_segment->id && !strcmp (item_id, _segment->id))
	return n + 1000;

      n += _segment->segment_count;
    }

  return 0;
}

bool
_vcd_pbc_available (const VcdObj *obj)
{
  vcd_assert (obj != NULL);
  vcd_assert (obj->pbc_list != NULL);

  if (!_vcd_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 = _vcd_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 = _vcd_list_length (_pbc->item_id_list);
      retval = __cd_offsetof (_PsdPlayListDescriptor, itemid[n]);
      break;

    case PBC_SELECTION:
      n = _vcd_list_length (_pbc->select_id_list);

      retval = __cd_offsetof (PsdSelectionListDescriptor, 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)
{
  VcdListNode *node;

  if (extended)
    vcd_assert (_vcd_obj_has_cap_p (obj, _CAP_PBC_X));

  /* disable it */
  if (!item_id)
    return PSD_OFS_DISABLED;

  _VCD_LIST_FOREACH (node, obj->pbc_list)
    {
      pbc_t *_pbc = _vcd_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:
      {
	VcdListNode *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);

	_VCD_LIST_FOREACH (node, _pbc->item_id_list)
	  {
	    const char *_id = _vcd_list_node_data (node);

	    _vcd_pin_mark_id (obj, _id);
	  }
      }
      break;

    case PBC_SELECTION:
      {
	VcdListNode *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);

	_VCD_LIST_FOREACH (node, _pbc->select_id_list)
	  {
	    const char *_id = _vcd_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)
{
  VcdListNode *node;

  /* clear all flags */

  _VCD_LIST_FOREACH (node, obj->pbc_list)
    {
      pbc_t *_pbc = _vcd_list_node_data (node);

      _pbc->referenced = false;
    }

  _VCD_LIST_FOREACH (node, obj->mpeg_sequence_list)
    {
      mpeg_sequence_t *_sequence = _vcd_list_node_data (node);

      _sequence->referenced = false;
    }

  _VCD_LIST_FOREACH (node, obj->mpeg_segment_list)
    {
      mpeg_segment_t *_segment = _vcd_list_node_data (node);

      _segment->referenced = false;
    }

  /* start from non-rejected lists */

  _VCD_LIST_FOREACH (node, obj->pbc_list)
    {
      pbc_t *_pbc = _vcd_list_node_data (node);

      vcd_assert (_pbc->id != NULL);

      if (_pbc->rejected)
	continue;

      _vcd_pbc_mark_id (obj, _pbc->id);
    }

  /* collect flags */

  _VCD_LIST_FOREACH (node, obj->pbc_list)
    {
      pbc_t *_pbc = _vcd_list_node_data (node);

      if (!_pbc->referenced)
	vcd_warn ("PSD item '%s' is unreachable", _pbc->id);
    }

  _VCD_LIST_FOREACH (node, obj->mpeg_sequence_list)
    {
      mpeg_sequence_t *_sequence = _vcd_list_node_data (node);

      if (!_sequence->referenced)
	vcd_warn ("sequence '%s' is not reachable by PBC", _sequence->id);
    }

  _VCD_LIST_FOREACH (node, obj->mpeg_segment_list)
    {
      mpeg_segment_t *_segment = _vcd_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;
	VcdListNode *node;
	int n;
	
	_md->type = PSD_TYPE_PLAY_LIST;
	_md->noi = _vcd_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;
	_VCD_LIST_FOREACH (node, _pbc->item_id_list)
	  {
	    const char *_id = _vcd_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 *_md = buf;

	const unsigned _nos = _vcd_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, 99))
	  vcd_error ("selection '%s': BSN (%d) not in range [1..99]",
		     _pbc->id, _pbc->bsn);

	if (!IN (_nos, 0, 99))
	  vcd_error ("selection '%s': too many selections (%d > 99)",
		     _pbc->id, _nos);

	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 = _vcd_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 */
	{
	  VcdListNode *node = NULL;
	  int idx = 0;

	  idx = 0;
	  _VCD_LIST_FOREACH (node, _pbc->select_id_list)
	    {
	      const char *_id = _vcd_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;
	    VcdListNode *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)
	      _VCD_LIST_FOREACH (node, _pbc->select_area_list)
	      {
		const pbc_area_t *_area = _vcd_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 = _vcd_list_new ();
      break;

    case PBC_SELECTION:
      _pbc->select_id_list = _vcd_list_new ();
      _pbc->select_area_list = _vcd_list_new ();
      break;
      
    case PBC_END:
      break;

    default:
      vcd_assert_not_reached ();
      break;
    }

  return _pbc;
}

/* 
 */

bool
_vcd_pbc_finalize (VcdObj *obj)
{
  VcdListNode *node;
  unsigned offset = 0, offset_ext = 0;
  unsigned lid;

  lid = 1;
  _VCD_LIST_FOREACH (node, obj->pbc_list)
    {
      pbc_t *_pbc = _vcd_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;
}

--- NEW FILE: Makefile.am ---
include $(top_srcdir)/misc/Makefile.common

SUBDIRS = libvcd

INCLUDES = $(LIBCDIO_CFLAGS) -I$(top_srcdir)/include -I$(top_srcdir)/lib

libvcd_SRCS = \
	vcd.c \
	data_structures.c \
	directory.c \
	files.c \
	image.c \
	image_bincue.c \
	image_cdrdao.c \
	image_nrg.c \
	logging.c \
	mpeg.c \
	mpeg_stream.c \
	pbc.c \
	salloc.c \
	sector.c \
	stream.c \
	stream_stdio.c \
	util.c 

libvcdinfo_SRCS = \
	info.c \
	inf.c \
	info_private.c \
	vcd_read.c

EXTRA_DIST = $(libvcd_SRCS) $(libvcdinfo_SRCS)

if ENABLE_VCD
if HAVE_VCDNAV
else
noinst_LTLIBRARIES = libvcd.la libvcdinfo.la
libvcd_la_SOURCES = $(libvcd_SRCS)
libvcd_la_LDFLAGS = -avoid-version -module
libvcd_la_LIBADD = $(LIBCDIO_LIBS) $(LIBISO9660_LIBS)
libvcdinfo_la_SOURCES = $(libvcdinfo_SRCS)
libvcdinfo_la_LDFLAGS = -avoid-version -module
endif
endif

noinst_HEADERS = \
	vcd_assert.h \
	data_structures.h \
	info_private.h \
	pbc.h \
	stream_stdio.h \
	bitvec.h \
	dict.h \
	mpeg.h \
	salloc.h \
	util.h \
	bytesex_asm.h \
	directory.h \
	mpeg_stream.h \
	sector_private.h \
	vcd.h \
	bytesex.h \
	image_sink.h \
	obj.h \
	stream.h \
	vcd_read.h

--- NEW FILE: vcd_read.c ---
/*
    $Id: vcd_read.c,v 1.1 2005/04/04 22:29:43 dsalt-guest Exp $

    Copyright (C) 2001,2003 Herbert Valerio Riedel <hvr@gnu.org>
    Copyright (C) 2003 Rocky Bernstein <rocky@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
*/


#include "vcd_read.h"
#include "vcd_assert.h"
#include <libvcd/inf.h>
#include <libvcd/files.h>
#include <libvcd/logging.h>

#ifdef HAVE_STRING_H
#include <string.h>
#endif

bool 
read_pvd(CdIo *cdio, iso9660_pvd_t *pvd) 
{
  if (cdio_read_mode2_sector (cdio, pvd, ISO_PVD_SECTOR, false)) {
    vcd_error ("error reading PVD sector (%d)", ISO_PVD_SECTOR);
    return false;
  }
  
  if (pvd->type != ISO_VD_PRIMARY) {
    vcd_error ("unexpected PVD type %d", pvd->type);
    return false;
  }
  
  if (strncmp (pvd->id, ISO_STANDARD_ID, strlen (ISO_STANDARD_ID)))
    {
      vcd_error ("unexpected ID encountered (expected `"
		ISO_STANDARD_ID "', got `%.5s'", pvd->id);
      return false;
    }
  return true;
}

bool 
read_entries(CdIo *cdio, EntriesVcd *entries) 
{
  if (cdio_read_mode2_sector (cdio, entries, ENTRIES_VCD_SECTOR, false)) {
    vcd_error ("error reading Entries sector (%d)", ENTRIES_VCD_SECTOR);
    return false;
  }

  /* analyze signature/type */

  if (!strncmp (entries->ID, ENTRIES_ID_VCD, sizeof (entries->ID)))
    return true;
  else if (!strncmp (entries->ID, "ENTRYSVD", sizeof (entries->ID))) {
    vcd_warn ("found (non-compliant) SVCD ENTRIES.SVD signature");
    return true;
  }  else {
    vcd_error ("unexpected ID signature encountered `%.8s'", entries->ID);
    return false;
  }
}

bool 
read_info(CdIo *cdio, InfoVcd *info, vcd_type_t *vcd_type) 
{
  if (cdio_read_mode2_sector (cdio, info, INFO_VCD_SECTOR, false)) {
    vcd_error ("error reading Info sector (%d)", INFO_VCD_SECTOR);
    return false;
  }

  *vcd_type = vcd_files_info_detect_type (info);

  /* analyze signature/type */

  switch (*vcd_type)
    {
    case VCD_TYPE_VCD:
    case VCD_TYPE_VCD11:
    case VCD_TYPE_VCD2:
    case VCD_TYPE_SVCD:
    case VCD_TYPE_HQVCD:
      vcd_debug ("%s detected", vcdinf_get_format_version_str(*vcd_type));
      break;
    case VCD_TYPE_INVALID:
      vcd_error ("unknown ID encountered -- maybe not a proper (S)VCD?");
      return false;
      break;
    default:
      vcd_assert_not_reached ();
      break;
    }

  return true;
}


--- NEW FILE: salloc.h ---
/*
    $Id: salloc.h,v 1.1 2005/04/04 22:29:43 dsalt-guest Exp $

    Copyright (C) 2000 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
*/

/* sector allocation management */

#ifndef _SALLOC_H_
#define _SALLOC_H_

#include <libvcd/types.h>

#define SECTOR_NIL ((uint32_t)(-1))

typedef struct _VcdSalloc VcdSalloc;

VcdSalloc *
_vcd_salloc_new (void);

void
_vcd_salloc_destroy (VcdSalloc *bitmap);

uint32_t
_vcd_salloc (VcdSalloc *bitmap, uint32_t hint, uint32_t size);

void
_vcd_salloc_free (VcdSalloc *bitmap, uint32_t sec, uint32_t size);

uint32_t
_vcd_salloc_get_highest (const VcdSalloc *bitmap);

#endif /* _SALLOC_H_ */


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: vcd_assert.h ---
/*
    $Id: vcd_assert.h,v 1.1 2005/04/04 22:29:43 dsalt-guest Exp $

    Copyright (C) 2000 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
*/

#ifndef __VCD_ASSERT_H__
#define __VCD_ASSERT_H__

#if defined(__GNUC__)

#include <libvcd/types.h>
#include <libvcd/logging.h>

#define vcd_assert(expr) \
 { \
   if (GNUC_UNLIKELY (!(expr))) vcd_log (VCD_LOG_ASSERT, \
     "file %s: line %d (%s): assertion failed: (%s)", \
     __FILE__, __LINE__, __PRETTY_FUNCTION__, #expr); \
 } 

#define vcd_assert_not_reached() \
 { \
   vcd_log (VCD_LOG_ASSERT, \
     "file %s: line %d (%s): should not be reached", \
     __FILE__, __LINE__, __PRETTY_FUNCTION__); \
 }

#else /* non GNU C */

#include <assert.h>

#define vcd_assert(expr) \
 assert(expr)

#define vcd_assert_not_reached() \
 assert(0)

#endif

#endif /* __VCD_ASSERT_H__ */

--- NEW FILE: stream_stdio.c ---
/*
    $Id: stream_stdio.c,v 1.1 2005/04/04 22:29:43 dsalt-guest Exp $

    Copyright (C) 2000 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> 
#include <sys/stat.h>
#include <errno.h>

#include <cdio/cdio.h>

#include <libvcd/logging.h>

/* Private headers */
#include "stream_stdio.h"
#include "util.h"

static const char _rcsid[] = "$Id: stream_stdio.c,v 1.1 2005/04/04 22:29:43 dsalt-guest Exp $";

#define VCD_STREAM_STDIO_BUFSIZE (128*1024)

typedef struct {
  char *pathname;
  FILE *fd;
  char *fd_buf;
  off_t st_size; /* used only for source */
} _UserData;

static int
_stdio_open_source (void *user_data) 
{
  _UserData *const ud = user_data;
  
  if ((ud->fd = fopen (ud->pathname, "rb")))
    {
      ud->fd_buf = _vcd_malloc (VCD_STREAM_STDIO_BUFSIZE);
      setvbuf (ud->fd, ud->fd_buf, _IOFBF, VCD_STREAM_STDIO_BUFSIZE);
    }

  return (ud->fd == NULL);
}

static int
_stdio_open_sink (void *user_data) 
{
  _UserData *const ud = user_data;

  if ((ud->fd = fopen (ud->pathname, "wb")))
    {
      ud->fd_buf = _vcd_malloc (VCD_STREAM_STDIO_BUFSIZE);
      setvbuf (ud->fd, ud->fd_buf, _IOFBF, VCD_STREAM_STDIO_BUFSIZE);
    }
  
  return (ud->fd == NULL);
}

static int
_stdio_close(void *user_data)
{
  _UserData *const ud = user_data;

  if (fclose (ud->fd))
    vcd_error ("fclose (): %s", strerror (errno));
 
  ud->fd = NULL;

  free (ud->fd_buf);
  ud->fd_buf = NULL;

  return 0;
}

static void
_stdio_free(void *user_data)
{
  _UserData *const ud = user_data;

  if (ud->pathname)
    free(ud->pathname);

  if (ud->fd) /* should be NULL anyway... */
    _stdio_close(user_data); 

  free(ud);
}

static long
_stdio_seek(void *user_data, long offset)
{
  _UserData *const ud = user_data;

  if (fseek (ud->fd, offset, SEEK_SET))
    vcd_error ("fseek (): %s", strerror (errno));

  return offset;
}

static long
_stdio_stat(void *user_data)
{
  const _UserData *const ud = user_data;

  return ud->st_size;
}

static long
_stdio_read(void *user_data, void *buf, long count)
{
  _UserData *const ud = user_data;
  long read;

  read = fread(buf, 1, count, ud->fd);

  if (read != count)
    { /* fixme -- ferror/feof */
      if (feof (ud->fd))
        {
          vcd_debug ("fread (): EOF encountered");
          clearerr (ud->fd);
        }
      else if (ferror (ud->fd))
        {
          vcd_error ("fread (): %s", strerror (errno));
          clearerr (ud->fd);
        }
      else
        vcd_debug ("fread (): short read and no EOF?!?");
    }

  return read;
}

static long
_stdio_write(void *user_data, const void *buf, long count)
{
  _UserData *const ud = user_data;
  long written;

  written = fwrite(buf, 1, count, ud->fd);
  
  if (written != count)
    vcd_error ("fwrite (): %s", strerror (errno));

  return written;
}

VcdDataSource*
vcd_data_source_new_stdio(const char pathname[])
{
  VcdDataSource *new_obj = NULL;
  vcd_data_source_io_functions funcs = { 0, };
  _UserData *ud = NULL;
  struct stat statbuf;
  
  if (stat (pathname, &statbuf) == -1) 
    {
      vcd_error ("could not stat() file `%s': %s", pathname, strerror (errno));
      return NULL;
    }

  ud = _vcd_malloc (sizeof (_UserData));

  ud->pathname = strdup(pathname);
  ud->st_size = statbuf.st_size; /* let's hope it doesn't change... */

  funcs.open = _stdio_open_source;
  funcs.seek = _stdio_seek;
  funcs.stat = _stdio_stat;
  funcs.read = _stdio_read;
  funcs.close = _stdio_close;
  funcs.free = _stdio_free;

  new_obj = vcd_data_source_new(ud, &funcs);

  return new_obj;
}


VcdDataSink*
vcd_data_sink_new_stdio(const char pathname[])
{
  VcdDataSink *new_obj = NULL;
  vcd_data_sink_io_functions funcs;
  _UserData *ud = NULL;
  struct stat statbuf;

  if (stat (pathname, &statbuf) != -1) 
    vcd_warn ("file `%s' exist already, will get overwritten!", pathname);

  ud = _vcd_malloc (sizeof (_UserData));

  memset (&funcs, 0, sizeof (funcs));

  ud->pathname = strdup (pathname);

  funcs.open = _stdio_open_sink;
  funcs.seek = _stdio_seek;
  funcs.write = _stdio_write;
  funcs.close = _stdio_close;
  funcs.free = _stdio_free;

  new_obj = vcd_data_sink_new (ud, &funcs);

  return new_obj;
}


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: salloc.c ---
/*
    $Id: salloc.c,v 1.1 2005/04/04 22:29:43 dsalt-guest Exp $

    Copyright (C) 2000 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 <stdlib.h>

#include <cdio/cdio.h>

/* Public headers */
#include <libvcd/types.h>
#include <libvcd/logging.h>

/* Private headers */
#include "vcd_assert.h"
#include "salloc.h"
#include "util.h"

static const char _rcsid[] = "$Id: salloc.c,v 1.1 2005/04/04 22:29:43 dsalt-guest Exp $";

#define VCD_SALLOC_CHUNK_SIZE 16

struct _VcdSalloc
{
  uint8_t *data;
  uint32_t len;
  uint32_t alloced_chunks;
};

static void
_vcd_salloc_set_size (VcdSalloc *bitmap, uint32_t newlen)
{
  uint32_t new_alloced_chunks;

  vcd_assert (bitmap != NULL);
  vcd_assert (newlen >= bitmap->len);

  new_alloced_chunks = newlen / VCD_SALLOC_CHUNK_SIZE;
  if (newlen % VCD_SALLOC_CHUNK_SIZE)
    new_alloced_chunks++;

  if (bitmap->alloced_chunks < new_alloced_chunks)
    {
      bitmap->data =
        realloc (bitmap->data, new_alloced_chunks * VCD_SALLOC_CHUNK_SIZE);
      memset (bitmap->data + (VCD_SALLOC_CHUNK_SIZE * bitmap->alloced_chunks),
              0,
              VCD_SALLOC_CHUNK_SIZE * (new_alloced_chunks -
                                       bitmap->alloced_chunks));
      bitmap->alloced_chunks = new_alloced_chunks;
    }

  bitmap->len = newlen;
}

static bool
_vcd_salloc_is_set (const VcdSalloc *bitmap, uint32_t sector)
{
  unsigned _byte = sector / 8;
  unsigned _bit = sector % 8;

  if (_byte < bitmap->len)
    return (bitmap->data[_byte] & (1 << _bit)) != 0;
  else
    return false;
}

static void
_vcd_salloc_set (VcdSalloc *bitmap, uint32_t sector)
{
  unsigned _byte = sector / 8;
  unsigned _bit = sector % 8;

  if (_byte >= bitmap->len)
    {
      unsigned oldlen = bitmap->len;
      _vcd_salloc_set_size (bitmap, _byte + 1);
      memset (bitmap->data + oldlen, 0x00, _byte + 1 - oldlen);
    }

  bitmap->data[_byte] |= (1 << _bit);
}

static void
_vcd_salloc_unset (VcdSalloc *bitmap, uint32_t sector)
{
  unsigned _byte = sector / 8;
  unsigned _bit = sector % 8;

  if (_byte >= bitmap->len)
    vcd_assert_not_reached ();

  bitmap->data[_byte] &= ~(1 << _bit);
}

/* exported */

uint32_t _vcd_salloc (VcdSalloc *bitmap, uint32_t hint, uint32_t size)
{
  if (!size)
    {
      size++;
      vcd_warn
        ("request of 0 sectors allocment fixed up to 1 sector (this is harmless)");
    }

  vcd_assert (size > 0);

  if (hint != SECTOR_NIL)
    {
      uint32_t i;
      for (i = 0; i < size; i++)
        if (_vcd_salloc_is_set (bitmap, hint + i))
          return SECTOR_NIL;

      /* everything's ok for allocing */

      i = size;
      while (i)
        _vcd_salloc_set (bitmap, hint + (--i));
      /* we begin with highest byte, in order to minimizing
         realloc's in sector_set */

      return hint;
    }

  /* find the lowest possible ... */

  hint = 0;

  while (_vcd_salloc (bitmap, hint, size) == SECTOR_NIL)
    hint++;

  return hint;
}

void
_vcd_salloc_free (VcdSalloc *bitmap, uint32_t sec, uint32_t size)
{
  uint32_t i;

  for (i = 0; i < size; i++) 
    {
      vcd_assert (_vcd_salloc_is_set (bitmap, sec + i));
      
      _vcd_salloc_unset (bitmap, sec + i);
    }
}

VcdSalloc *
_vcd_salloc_new (void)
{
  VcdSalloc *newobj = _vcd_malloc (sizeof (VcdSalloc));
  return newobj;
}

void
_vcd_salloc_destroy (VcdSalloc *bitmap)
{
  vcd_assert (bitmap != NULL);

  free (bitmap->data);
  free (bitmap);
}

uint32_t _vcd_salloc_get_highest (const VcdSalloc *bitmap)
{
  uint8_t last;
  unsigned n;

  vcd_assert (bitmap != NULL);

  last = bitmap->data[bitmap->len - 1];

  vcd_assert (last != 0);

  n = 8;
  while (n)
    if ((1 << --n) & last)
      break;

  return (bitmap->len - 1) * 8 + n;
}


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: directory.c ---
/*
    $Id: directory.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2000 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 <stdlib.h>
#include <string.h>

/* Public headers */
#include <cdio/iso9660.h>
#include <libvcd/logging.h>

/* Private headers */
#include "vcd_assert.h"
#include "bytesex.h"
#include "directory.h"
#include "util.h"

static const char _rcsid[] = "$Id: directory.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $";

/* CD-ROM XA */

/* tree data structure */

typedef struct 
{
  bool is_dir;
  char *name;
  uint16_t version;
  uint16_t xa_attributes;
  uint8_t xa_filenum;
  uint32_t extent;
  uint32_t size;
  unsigned pt_id;
} data_t;

typedef VcdTreeNode VcdDirNode;

#define EXTENT(anode) (DATAP(anode)->extent)
#define SIZE(anode)   (DATAP(anode)->size)
#define PT_ID(anode)  (DATAP(anode)->pt_id)

#define DATAP(anode) ((data_t*) _vcd_tree_node_data (anode))

/* important date to celebrate (for me at least =)
   -- until user customization is implemented... */
static const time_t _vcd_time = 269222400L;
                                       
/* implementation */

static void
traverse_get_dirsizes(VcdDirNode *node, void *data)
{
  data_t *d = DATAP(node);
  unsigned *sum = data;

  if (d->is_dir)
    {
      vcd_assert (d->size % ISO_BLOCKSIZE == 0);

      *sum += (d->size / ISO_BLOCKSIZE);
    }
}

static unsigned
get_dirsizes (VcdDirNode* dirnode)
{
  unsigned result = 0;

  _vcd_tree_node_traverse (dirnode, traverse_get_dirsizes, &result);

  return result;
}

static void
traverse_update_dirextents (VcdDirNode *dirnode, void *data)
{
  data_t *d = DATAP(dirnode);

  if (d->is_dir) 
    {
      VcdDirNode* child;
      unsigned dirextent = d->extent;
      
      vcd_assert (d->size % ISO_BLOCKSIZE == 0);
      
      dirextent += d->size / ISO_BLOCKSIZE;
      
      _VCD_CHILD_FOREACH (child, dirnode)
        {
          data_t *d = DATAP(child);
          
          vcd_assert (d != NULL);
          
          if (d->is_dir) 
            {
              d->extent = dirextent;
              dirextent += get_dirsizes (child);
            }
        }
    }
}

static void 
update_dirextents (VcdDirectory *dir, uint32_t extent)
{
  data_t *dirdata = DATAP(_vcd_tree_root (dir));
  
  dirdata->extent = extent;
  _vcd_tree_node_traverse (_vcd_tree_root (dir),
                           traverse_update_dirextents, NULL);
}

static void
traverse_update_sizes(VcdDirNode *node, void *data)
{
  data_t *dirdata = DATAP(node);

  if (dirdata->is_dir)
    {
      VcdDirNode* child;
      unsigned offset = 0;
      
      offset += iso9660_dir_calc_record_size (1, sizeof(iso9660_xa_t)); /* '.' */
      offset += iso9660_dir_calc_record_size (1, sizeof(iso9660_xa_t)); /* '..' */
      
      _VCD_CHILD_FOREACH (child, node)
        {
          data_t *d = DATAP(child);
          unsigned reclen;
          char *pathname = (d->is_dir 
                            ? strdup (d->name)
                            : iso9660_pathname_isofy (d->name, d->version));
          
          vcd_assert (d != NULL);
          
          reclen = iso9660_dir_calc_record_size (strlen (pathname), 
                                                 sizeof (iso9660_xa_t));

          free (pathname);
          
          offset = _vcd_ofs_add (offset, reclen, ISO_BLOCKSIZE);
        }

      vcd_assert (offset > 0);

      dirdata->size = _vcd_ceil2block (offset, ISO_BLOCKSIZE);
    }
}

static void 
update_sizes (VcdDirectory *dir)
{
  _vcd_tree_node_traverse (_vcd_tree_root(dir), traverse_update_sizes, NULL);
}


/* exported stuff: */

VcdDirectory *
_vcd_directory_new (void)
{
  data_t *data;
  VcdDirectory *dir = NULL;

  vcd_assert (sizeof(iso9660_xa_t) == 14);

  data = _vcd_malloc (sizeof (data_t));
  dir = _vcd_tree_new (data);

  data->is_dir = true;
  data->name = _vcd_memdup("\0", 2);
  data->xa_attributes = XA_FORM1_DIR;
  data->xa_filenum = 0x00;

  return dir;
}

static void
traverse_vcd_directory_done (VcdDirNode *node, void *data)
{
  data_t *dirdata = DATAP (node);

  free (dirdata->name);
}

void
_vcd_directory_destroy (VcdDirectory *dir)
{
  vcd_assert (dir != NULL);

  _vcd_tree_node_traverse (_vcd_tree_root (dir),
                           traverse_vcd_directory_done, NULL);

  _vcd_tree_destroy (dir, true);
}

static VcdDirNode* 
lookup_child (VcdDirNode* node, const char name[])
{
  VcdDirNode* child;

  _VCD_CHILD_FOREACH (child, node)
    {
      data_t *d = DATAP(child);
      
      if (!strcmp (d->name, name))
        return child;
    }
  
  return child; /* NULL */
}

static int
_iso_dir_cmp (VcdDirNode *node1, VcdDirNode *node2)
{
  data_t *d1 = DATAP(node1);
  data_t *d2 = DATAP(node2);
  int result = 0;

  result = strcmp (d1->name, d2->name);

  return result;
}

int
_vcd_directory_mkdir (VcdDirectory *dir, const char pathname[])
{
  char **splitpath;
  unsigned level, n;
  VcdDirNode* pdir = _vcd_tree_root (dir);

  vcd_assert (dir != NULL);
  vcd_assert (pathname != NULL);

  splitpath = _vcd_strsplit (pathname, '/');

  level = _vcd_strlenv (splitpath);

  for (n = 0; n < level-1; n++) 
    if (!(pdir = lookup_child(pdir, splitpath[n]))) 
      {
        vcd_error("mkdir: parent dir `%s' (level=%d) for `%s' missing!",
                  splitpath[n], n, pathname);
        vcd_assert_not_reached ();
      }

  if (lookup_child (pdir, splitpath[level-1])) 
    {
      vcd_error ("mkdir: `%s' already exists", pathname);
      vcd_assert_not_reached ();
    }

  {
    data_t *data = _vcd_malloc (sizeof (data_t));

    _vcd_tree_node_append_child (pdir, data);

    data->is_dir = true;
    data->name = strdup(splitpath[level-1]);
    data->xa_attributes = XA_FORM1_DIR;
    data->xa_filenum = 0x00;
    /* .. */
  }

  _vcd_tree_node_sort_children (pdir, _iso_dir_cmp);
  
  _vcd_strfreev (splitpath);

  return 0;
}

int
_vcd_directory_mkfile (VcdDirectory *dir, const char pathname[], 
                       uint32_t start, uint32_t size,
                       bool form2_flag, uint8_t filenum)
{
  char **splitpath;
  unsigned level, n;
  const int file_version = 1;

  VcdDirNode* pdir = NULL;

  vcd_assert (dir != NULL);
  vcd_assert (pathname != NULL);

  splitpath = _vcd_strsplit (pathname, '/');

  level = _vcd_strlenv (splitpath);

  while (!pdir)
    {
      pdir = _vcd_tree_root (dir);
      
      for (n = 0; n < level-1; n++) 
        if (!(pdir = lookup_child (pdir, splitpath[n]))) 
          {
            char *newdir = _vcd_strjoin (splitpath, n+1, "/");

            vcd_info ("autocreating directory `%s' for file `%s'",
                      newdir, pathname);
            
            _vcd_directory_mkdir (dir, newdir);
            
            free (newdir);
        
            vcd_assert (pdir == NULL);

            break;
          }
        else if (!DATAP(pdir)->is_dir)
          {
            char *newdir = _vcd_strjoin (splitpath, n+1, "/");

            vcd_error ("mkfile: `%s' not a directory", newdir);

            free (newdir);

            return -1;
          }

    }

  if (lookup_child (pdir, splitpath[level-1])) 
    {
      vcd_error ("mkfile: `%s' already exists", pathname);
      return -1;
    }
  
  {
    data_t *data = _vcd_malloc (sizeof (data_t));

    _vcd_tree_node_append_child (pdir, data);

    data->is_dir = false;
    data->name = strdup (splitpath[level-1]);
    data->version = file_version;
    data->xa_attributes = form2_flag ? XA_FORM2_FILE : XA_FORM1_FILE;
    data->xa_filenum = filenum;
    data->size = size;
    data->extent = start;
    /* .. */
  }

  _vcd_tree_node_sort_children (pdir, _iso_dir_cmp);

  _vcd_strfreev (splitpath);

  return 0;
}

uint32_t
_vcd_directory_get_size (VcdDirectory *dir)
{
  vcd_assert (dir != NULL);

  update_sizes (dir);
  return get_dirsizes (_vcd_tree_root (dir));
}

static void
traverse_vcd_directory_dump_entries (VcdDirNode *node, void *data)
{
  data_t *d = DATAP(node);
  iso9660_xa_t xa_su;

  uint32_t root_extent = EXTENT(_vcd_tree_node_root (node));

  uint32_t parent_extent = 
    (!_vcd_tree_node_is_root (node))
    ? EXTENT(_vcd_tree_node_parent (node))
    : EXTENT(node);

  uint32_t parent_size = 
    (!_vcd_tree_node_is_root (node))
    ? SIZE(_vcd_tree_node_parent (node))
    : SIZE(node);

  void *dirbufp = (char*) data + ISO_BLOCKSIZE * (parent_extent - root_extent);

  iso9660_xa_init (&xa_su, 0, 0, d->xa_attributes, d->xa_filenum);

  if (!_vcd_tree_node_is_root (node))
    {
      char *pathname = (d->is_dir 
                        ? strdup (d->name)
                        : iso9660_pathname_isofy (d->name, d->version));

      iso9660_dir_add_entry_su (dirbufp, pathname, d->extent, d->size, 
                                d->is_dir ? ISO_DIRECTORY : ISO_FILE,
                                &xa_su, sizeof (xa_su),
                                &_vcd_time);

      free (pathname);
    }

  /* if this is a directory, create the new directory node */
  if (d->is_dir) 
    {
      dirbufp = (char*)data + ISO_BLOCKSIZE * (d->extent - root_extent);

      iso9660_dir_init_new_su (dirbufp, 
                               d->extent, d->size, &xa_su, sizeof (xa_su),
                               parent_extent, parent_size, &xa_su, 
                               sizeof (xa_su), &_vcd_time);
    }
}

void
_vcd_directory_dump_entries (VcdDirectory *dir, void *buf, uint32_t extent)
{
  vcd_assert (dir != NULL);

  update_sizes (dir); /* better call it one time more than one less */
  update_dirextents (dir, extent);

  _vcd_tree_node_traverse (_vcd_tree_root (dir), 
                           traverse_vcd_directory_dump_entries, buf); 
}

typedef struct 
{
  void *ptl;
  void *ptm;
} _vcd_directory_dump_pathtables_t;

static void
_dump_pathtables_helper (_vcd_directory_dump_pathtables_t *args,
                         data_t *d, uint16_t parent_id)
{
  uint16_t id_l, id_m;

  vcd_assert (args != NULL);
  vcd_assert (d != NULL);

  vcd_assert (d->is_dir);

  id_l = iso9660_pathtable_l_add_entry (args->ptl, d->name, d->extent, 
                                        parent_id);
  
  id_m = iso9660_pathtable_m_add_entry (args->ptm, d->name, d->extent, 
                                        parent_id);

  vcd_assert (id_l == id_m);

  d->pt_id = id_m;
}

static void
traverse_vcd_directory_dump_pathtables (VcdDirNode *node, void *data)
{
  _vcd_directory_dump_pathtables_t *args = data;

  if (DATAP (node)->is_dir)
    {
      VcdDirNode* parent = _vcd_tree_node_parent (node);
      uint16_t parent_id = parent ? PT_ID (parent) : 1;

      _dump_pathtables_helper (args, DATAP (node), parent_id);
    }
}

void
_vcd_directory_dump_pathtables (VcdDirectory *dir, void *ptl, void *ptm)
{
  _vcd_directory_dump_pathtables_t args;

  vcd_assert (dir != NULL);

  iso9660_pathtable_init (ptl);
  iso9660_pathtable_init (ptm);

  args.ptl = ptl;
  args.ptm = ptm;

  _vcd_tree_node_traverse_bf (_vcd_tree_root (dir),
                              traverse_vcd_directory_dump_pathtables, &args); 
}


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: mpeg_stream.c ---
/*
    $Id: mpeg_stream.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2000 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 <stdio.h>
#include <stdlib.h>

#include <cdio/cdio.h>

#include <libvcd/logging.h>

/* Private headers */
#include "vcd_assert.h"
#include "mpeg_stream.h"
#include "bytesex.h"
#include "data_structures.h"
#include "mpeg.h"
#include "util.h"

static const char _rcsid[] = "$Id: mpeg_stream.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $";

struct _VcdMpegSource
{
  VcdDataSource *data_source;

  bool scanned;
  
  /* _get_packet cache */
  unsigned _read_pkt_pos;
  unsigned _read_pkt_no;

  struct vcd_mpeg_stream_info info;
};

/*
 * access functions
 */

VcdMpegSource *
vcd_mpeg_source_new (VcdDataSource *mpeg_file)
{
  VcdMpegSource *new_obj;
  
  vcd_assert (mpeg_file != NULL);

  new_obj = _vcd_malloc (sizeof (VcdMpegSource));

  new_obj->data_source = mpeg_file;
  new_obj->scanned = false;

  return new_obj;
}

void
vcd_mpeg_source_destroy (VcdMpegSource *obj, bool destroy_file_obj)
{
  int i;
  vcd_assert (obj != NULL);

  if (destroy_file_obj)
    vcd_data_source_destroy (obj->data_source);

  for (i = 0; i < 3; i++)
    if (obj->info.shdr[i].aps_list)
      _vcd_list_free (obj->info.shdr[i].aps_list, true);

  free (obj);
}

const struct vcd_mpeg_stream_info *
vcd_mpeg_source_get_info (VcdMpegSource *obj)
{
  vcd_assert (obj != NULL);

  vcd_assert (obj->scanned);

  return &(obj->info);
}

long
vcd_mpeg_source_stat (VcdMpegSource *obj)
{
  vcd_assert (obj != NULL);
  vcd_assert (!obj->scanned);
  
  return obj->info.packets * 2324;
}

void
vcd_mpeg_source_scan (VcdMpegSource *obj, bool strict_aps, bool fix_scan_info,
                      vcd_mpeg_prog_cb_t callback, void *user_data)
{
  unsigned length = 0;
  unsigned pos = 0;
  unsigned pno = 0;
  unsigned padbytes = 0;
  unsigned padpackets = 0;
  VcdMpegStreamCtx state;
  VcdListNode *n;
  vcd_mpeg_prog_info_t _progress = { 0, };

  vcd_assert (obj != NULL);

  if (obj->scanned)
    {
      vcd_debug ("already scanned... not rescanning");
      return;
    }

  vcd_assert (!obj->scanned);

  memset (&state, 0, sizeof (state));
  
  if (fix_scan_info)
    state.stream.scan_data_warnings = VCD_MPEG_SCAN_DATA_WARNS + 1;

  vcd_data_source_seek (obj->data_source, 0);
  length = vcd_data_source_stat (obj->data_source);

  if (callback)
    {
      _progress.length = length;
      callback (&_progress, user_data);
    }


  while (pos < length)
    {
      char buf[2324] = { 0, };
      int read_len = MIN (sizeof (buf), (length - pos));
      int pkt_len;

      vcd_data_source_read (obj->data_source, buf, read_len, 1);

      pkt_len = vcd_mpeg_parse_packet (buf, read_len, true, &state);

      if (!pkt_len)
        {
          if (!pno)
            vcd_error ("input mpeg stream has been deemed invalid -- aborting");

          vcd_warn ("bad packet at packet #%d (stream byte offset %d)"
                    " -- remaining %d bytes of stream will be ignored",
                    pno, pos, length - pos);

          pos = length; /* don't fall into assert... */
          break;
        }

      if (callback && (pos - _progress.current_pos) > (length / 100))
        {
          _progress.current_pos = pos;
          _progress.current_pack = pno;
          callback (&_progress, user_data);
        }

      switch (state.packet.aps)
        {
        case APS_NONE:
          break;

        case APS_I:
        case APS_GI:
          if (strict_aps)
            break; /* allow only if now strict aps */

        case APS_SGI:
        case APS_ASGI:
          {
            struct aps_data *_data = _vcd_malloc (sizeof (struct aps_data));
            
            _data->packet_no = pno;
            _data->timestamp = state.packet.aps_pts;

            if (!state.stream.shdr[state.packet.aps_idx].aps_list)
              state.stream.shdr[state.packet.aps_idx].aps_list = _vcd_list_new ();
            
            _vcd_list_append (state.stream.shdr[state.packet.aps_idx].aps_list, _data);
          }
          break;

        default:
          vcd_assert_not_reached ();
          break;
        }

      pos += pkt_len;
      pno++;

      if (pkt_len != read_len)
        {
          padbytes += (2324 - pkt_len);

          if (!padpackets)
            vcd_warn ("mpeg stream will be padded on the fly -- hope that's ok for you!");

          padpackets++;

          vcd_data_source_seek (obj->data_source, pos);
        }
    }

  vcd_data_source_close (obj->data_source);

  if (callback)
    {
      _progress.current_pos = pos;
      _progress.current_pack = pno;
      callback (&_progress, user_data);
    }

  vcd_assert (pos == length);

  obj->info = state.stream;
  obj->scanned = true;

  obj->info.playing_time = obj->info.max_pts - obj->info.min_pts;

  if (obj->info.min_pts)
    vcd_debug ("pts start offset %f (max pts = %f)", 
               obj->info.min_pts, obj->info.max_pts);

  vcd_debug ("playing time %f", obj->info.playing_time);

  if (!state.stream.scan_data && state.stream.version == MPEG_VERS_MPEG2)
    vcd_warn ("mpeg stream contained no scan information (user) data");

  {
    int i;

    for (i = 0; i < 3; i++)
      if (obj->info.shdr[i].aps_list)
        _VCD_LIST_FOREACH (n, obj->info.shdr[i].aps_list)
        {
          struct aps_data *_data = _vcd_list_node_data (n);
          
          _data->timestamp -= obj->info.min_pts; 
        }
  }

  if (padpackets)
    vcd_warn ("autopadding requires to insert additional %d zero bytes"
              " into MPEG stream (due to %d unaligned packets of %d total)",
              padbytes, padpackets, state.stream.packets);

  obj->info.version = state.stream.version;
}

static double
_approx_pts (VcdList *aps_list, uint32_t packet_no)
{
  double retval = 0;
  VcdListNode *node;

  struct aps_data *_laps = NULL;

  double last_pts_ratio = 0;

  _VCD_LIST_FOREACH (node, aps_list)
    {
      struct aps_data *_aps = _vcd_list_node_data (node);

      if (_laps)
        {
          long p = _aps->packet_no;
          double t = _aps->timestamp;

          p -= _laps->packet_no;
          t -= _laps->timestamp;

          last_pts_ratio = t / p;
        }

      if (_aps->packet_no >= packet_no)
        break;
      
      _laps = _aps;
    }

  retval = packet_no;
  retval -= _laps->packet_no;
  retval *= last_pts_ratio;
  retval += _laps->timestamp;

  return retval;
}

static void 
_set_scan_msf (msf_t *_msf, long lsn)
{
  if (lsn == -1)
    {
      _msf->m = _msf->s = _msf->f = 0xff;
      return;
    }

  cdio_lba_to_msf (lsn, _msf);
  _msf->s |= 0x80;
  _msf->f |= 0x80;
}

static void 
_fix_scan_info (struct vcd_mpeg_scan_data_t *scan_data_ptr,
                unsigned packet_no, double pts, VcdList *aps_list)
{
  VcdListNode *node;
  long _next = -1, _prev = -1, _forw = -1, _back = -1;

  _VCD_LIST_FOREACH (node, aps_list)
    {
      struct aps_data *_aps = _vcd_list_node_data (node);

      if (_aps->packet_no == packet_no)
        continue;
      else if (_aps->packet_no < packet_no)
        {
          _prev = _aps->packet_no;
          
          if (pts - _aps->timestamp < 10 && _back == -1)
            _back = _aps->packet_no;
        }
      else if (_aps->packet_no > packet_no)
        {
          if (_next == -1)
            _next = _aps->packet_no;

          if (_aps->timestamp - pts < 10)
            _forw = _aps->packet_no;
        }
    }

  if (_back == -1)
    _back = packet_no;

  if (_forw == -1)
    _forw = packet_no;

  _set_scan_msf (&scan_data_ptr->prev_ofs, _prev);
  _set_scan_msf (&scan_data_ptr->next_ofs, _next);
  _set_scan_msf (&scan_data_ptr->back_ofs, _back);
  _set_scan_msf (&scan_data_ptr->forw_ofs, _forw);
}

int
vcd_mpeg_source_get_packet (VcdMpegSource *obj, unsigned long packet_no,
			    void *packet_buf, struct vcd_mpeg_packet_info *flags,
                            bool fix_scan_info)
{
  unsigned length;
  unsigned pos;
  unsigned pno;
  VcdMpegStreamCtx state;

  vcd_assert (obj != NULL);
  vcd_assert (obj->scanned);
  vcd_assert (packet_buf != NULL);

  if (packet_no >= obj->info.packets)
    {
      vcd_error ("invalid argument");
      return -1;
    }

  if (packet_no < obj->_read_pkt_no)
    {
      vcd_warn ("rewinding mpeg stream...");
      obj->_read_pkt_no = 0;
      obj->_read_pkt_pos = 0;
    }

  memset (&state, 0, sizeof (state));
  state.stream.seen_pts = true;
  state.stream.min_pts = obj->info.min_pts;
  state.stream.scan_data_warnings = VCD_MPEG_SCAN_DATA_WARNS + 1;

  pos = obj->_read_pkt_pos;
  pno = obj->_read_pkt_no;
  length = vcd_data_source_stat (obj->data_source);

  vcd_data_source_seek (obj->data_source, pos);

  while (pos < length)
    {
      char buf[2324] = { 0, };
      int read_len = MIN (sizeof (buf), (length - pos));
      int pkt_len;
      
      vcd_data_source_read (obj->data_source, buf, read_len, 1);

      pkt_len = vcd_mpeg_parse_packet (buf, read_len,
                                       fix_scan_info, &state);

      vcd_assert (pkt_len > 0);

      if (pno == packet_no)
	{
          /* optimized for sequential access, 
             thus save pointer to next mpeg pack */
	  obj->_read_pkt_pos = pos + pkt_len;
	  obj->_read_pkt_no = pno + 1;

          if (fix_scan_info
              && state.packet.scan_data_ptr
              && obj->info.version == MPEG_VERS_MPEG2)
            {
              int vid_idx = 0;
              double _pts;

              if (state.packet.video[2])
                vid_idx = 2;
              else if (state.packet.video[1])
                vid_idx = 1;
              else 
                vid_idx = 0;

              if (state.packet.has_pts)
                _pts = state.packet.pts - obj->info.min_pts;
              else
                _pts = _approx_pts (obj->info.shdr[vid_idx].aps_list, packet_no);

              _fix_scan_info (state.packet.scan_data_ptr, packet_no, 
                              _pts, obj->info.shdr[vid_idx].aps_list);
            }

	  memset (packet_buf, 0, 2324);
	  memcpy (packet_buf, buf, pkt_len);

          if (flags)
            {
              *flags = state.packet;
              flags->pts -= obj->info.min_pts;
            }

	  return 0; /* breaking out */
	}

      pos += pkt_len;
      pno++;

      if (pkt_len != read_len)
	vcd_data_source_seek (obj->data_source, pos);
    }

  vcd_assert (pos == length);

  vcd_error ("shouldnt be reached...");

  return -1;
}

void
vcd_mpeg_source_close (VcdMpegSource *obj)
{
  vcd_assert (obj != NULL);

  vcd_data_source_close (obj->data_source);
}


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: sector_private.h ---
/*
    $Id: sector_private.h,v 1.1 2005/04/04 22:29:43 dsalt-guest Exp $

    Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org>
              (C) 1998 Heiko Eissfeldt <heiko@colossus.escape.de>

    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
[...1309 lines suppressed...]
   57185, 56419, 23965, 24223, 23449, 22683, 20885, 21143, 22417, 21651,
   17805, 18063, 17289, 16523, 18821, 19079, 20353, 19587, 28093, 28351,
   27577, 26811, 25013, 25271, 26545, 25779, 30125, 30383, 29609, 28843,
   31141, 31399, 32673, 31907, 15837, 16095, 15321, 14555, 12757, 13015,
   14289, 13523,  9677,  9935,  9161,  8395, 10693, 10951, 12225, 11459,
    3581,  3839,  3065,  2299,   501,   759,  2033,  1267,  5613,  5871,
    5097,  4331,  6629,  6887,  8161,  7395,
  },
};

#endif /* __VCD_CD_SECTOR_PRIVATE_H__ */


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: vcd_read.h ---
/*
    $Id: vcd_read.h,v 1.1 2005/04/04 22:29:43 dsalt-guest Exp $

    Copyright (C) 2003 Rocky Bernstein <rocky@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 <cdio/cdio.h>
#include <cdio/iso9660.h>

/* FIXME: make this really private: */
#include <libvcd/files_private.h>

bool read_pvd(CdIo *cdio, iso9660_pvd_t *pvd);
bool read_entries(CdIo *cdio, EntriesVcd *entries);
bool read_info(CdIo *cdio, InfoVcd *info, vcd_type_t *vcd_type);




--- NEW FILE: util.h ---
/*
    $Id: util.h,v 1.1 2005/04/04 22:29:43 dsalt-guest Exp $

    Copyright (C) 2000 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
*/

#ifndef __VCD_UTIL_H__
#define __VCD_UTIL_H__

#include <stdlib.h>
#include <libvcd/types.h>

#ifndef __CDIO_UTIL_H__
#undef  MAX
#define MAX(a, b)  (((a) > (b)) ? (a) : (b))

#undef  MIN
#define MIN(a, b)  (((a) < (b)) ? (a) : (b))

#undef  IN
#define IN(x, low, high) ((x) >= (low) && (x) <= (high))

#undef  CLAMP
#define CLAMP(x, low, high)  (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))

#endif

static inline unsigned
_vcd_len2blocks (unsigned len, int blocksize)
{
  unsigned blocks;

  blocks = len / blocksize;
  if (len % blocksize)
    blocks++;

  return blocks;
}

/* round up to next block boundary */
static inline unsigned 
_vcd_ceil2block (unsigned offset, int blocksize)
{
  return _vcd_len2blocks (offset, blocksize) * blocksize;
}

static inline unsigned 
_vcd_ofs_add (unsigned offset, unsigned length, int blocksize)
{
  if (blocksize - (offset % blocksize) < length)
    offset = _vcd_ceil2block (offset, blocksize);

  offset += length;

  return offset;
}

size_t
_vcd_strlenv(char **str_array);

char *
_vcd_strjoin (char *strv[], unsigned count, const char delim[]);

char **
_vcd_strsplit(const char str[], char delim);

void
_vcd_strfreev(char **strv);

void *
_vcd_malloc (size_t size);

void *
_vcd_memdup (const void *mem, size_t count);

char *
_vcd_strdup_upper (const char str[]);

static inline const char *
_vcd_bool_str (bool b)
{
  return b ? "yes" : "no";
}

#endif /* __VCD_UTIL_H__ */


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: image_nrg.c ---
/*
    $Id: image_nrg.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2001,2003 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
*/

/*! This code implements low-level access functions for Nero's native
   CD-image format residing inside a disk file (*.nrg).
*/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <cdio/cdio.h>
#include <cdio/iso9660.h>

/* Public headers */
#include <libvcd/sector.h>
#include <libvcd/logging.h>

/* Private headers */
#include "vcd_assert.h"
#include "bytesex.h"
#include "image_sink.h"
#include "stream_stdio.h"
#include "util.h"

static const char _rcsid[] = "$Id: image_nrg.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $";

/* structures used */

/* this ugly image format is typical for lazy win32 programmers... at
   least structure were set big endian, so at reverse
   engineering wasn't such a big headache... */

PRAGMA_BEGIN_PACKED
typedef struct {
  uint32_t start      GNUC_PACKED;
  uint32_t length     GNUC_PACKED;
  uint32_t type       GNUC_PACKED; /* only 0x3 seen so far... 
                                      -> MIXED_MODE2 2336 blocksize */
  uint32_t start_lsn  GNUC_PACKED; /* does not include any pre-gaps! */
  uint32_t _unknown   GNUC_PACKED; /* wtf is this for? -- always zero... */
} _etnf_array_t;

/* finally they realized that 32bit offsets are a bit outdated for IA64 *eg* */
typedef struct {
  uint64_t start      GNUC_PACKED;
  uint64_t length     GNUC_PACKED;
  uint32_t type       GNUC_PACKED;
  uint32_t start_lsn  GNUC_PACKED;
  uint64_t _unknown   GNUC_PACKED; /* wtf is this for? -- always zero... */
} _etn2_array_t;

typedef struct {
  uint8_t  _unknown1  GNUC_PACKED; /* 0x41 == 'A' */
  uint8_t  track      GNUC_PACKED; /* binary or BCD?? */
  uint8_t  index      GNUC_PACKED; /* makes 0->1 transitions */
  uint8_t  _unknown2  GNUC_PACKED; /* ?? */
  uint32_t lsn        GNUC_PACKED; 
} _cuex_array_t;

typedef struct {
  uint32_t id                    GNUC_PACKED;
  uint32_t len                   GNUC_PACKED;
  char data[EMPTY_ARRAY_SIZE]    GNUC_PACKED;
} _chunk_t;

PRAGMA_END_PACKED

/* to be converted into BE */
#define CUEX_ID  0x43554558
#define CUES_ID  0x43554553
#define DAOX_ID  0x44414f58
#define DAOI_ID  0x44414f49
#define END1_ID  0x454e4421
#define ETN2_ID  0x45544e32
#define ETNF_ID  0x45544e46
#define NER5_ID  0x4e455235
#define NERO_ID  0x4e45524f
#define SINF_ID  0x53494e46

/****************************************************************************
 * writer
 */

typedef struct {
  VcdDataSink *nrg_snk;
  char *nrg_fname;

  VcdList *vcd_cue_list;
  int tracks;
  uint32_t cue_end_lsn;

  bool init;
} _img_nrg_snk_t;

static void
_sink_init (_img_nrg_snk_t *_obj)
{
  if (_obj->init)
    return;

  if (!(_obj->nrg_snk = vcd_data_sink_new_stdio (_obj->nrg_fname)))
    vcd_error ("init failed");

  _obj->init = true;  
}


static void
_sink_free (void *user_data)
{
  _img_nrg_snk_t *_obj = user_data;

  free (_obj->nrg_fname);
  vcd_data_sink_destroy (_obj->nrg_snk);

  free (_obj);
}

static int
_set_cuesheet (void *user_data, const VcdList *vcd_cue_list)
{
  _img_nrg_snk_t *_obj = user_data;
  VcdListNode *node;
  int num;

  _sink_init (_obj);

  _obj->vcd_cue_list = _vcd_list_new ();

  num = 0;
  _VCD_LIST_FOREACH (node, (VcdList *) vcd_cue_list)
    {
      const vcd_cue_t *_cue = _vcd_list_node_data (node);
      vcd_cue_t *_cue2 = _vcd_malloc (sizeof (vcd_cue_t));
      *_cue2 = *_cue;
      _vcd_list_append (_obj->vcd_cue_list, _cue2);
  
      if (_cue->type == VCD_CUE_TRACK_START)
	num++;

      if (_cue->type == VCD_CUE_END)
	_obj->cue_end_lsn = _cue->lsn;
    }

  _obj->tracks = num;

  vcd_assert (num > 0 && num < 100);

  return 0;
}

static uint32_t
_map (_img_nrg_snk_t *_obj, uint32_t lsn)
{
  VcdListNode *node;
  uint32_t result = lsn;
  vcd_cue_t *_cue = NULL, *_last = NULL;

  vcd_assert (_obj->cue_end_lsn > lsn);

  _VCD_LIST_FOREACH (node, _obj->vcd_cue_list)
    {
      _cue = _vcd_list_node_data (node);
      
      if (lsn < _cue->lsn)
	break;

      switch (_cue->type)
	{
	case VCD_CUE_TRACK_START:
	  result -= _cue->lsn;
	  break;
	case VCD_CUE_PREGAP_START:
	  result += _cue->lsn;
	  break;
	default:
	  break;
	}

      _last = _cue;
    }
  
  vcd_assert (node != NULL);

  switch (_last->type)
    {
    case VCD_CUE_TRACK_START:
      return result;
      break;

    case VCD_CUE_PREGAP_START:
      return -1;
      break;
    
    default:
    case VCD_CUE_END:
      vcd_assert_not_reached ();
      break;
    }

  return -1;
}

static int
_write_tail (_img_nrg_snk_t *_obj, uint32_t offset)
{
  VcdListNode *node;
  int _size;
  _chunk_t _chunk;

  vcd_data_sink_seek (_obj->nrg_snk, offset);

  _size = _obj->tracks * sizeof (_etnf_array_t);
  _chunk.id = UINT32_TO_BE (ETNF_ID);
  _chunk.len = uint32_to_be (_size);

  vcd_data_sink_write (_obj->nrg_snk, &_chunk, sizeof (_chunk_t), 1);

  _VCD_LIST_FOREACH (node, _obj->vcd_cue_list)
    {
      vcd_cue_t *_cue = _vcd_list_node_data (node);
      
      if (_cue->type == VCD_CUE_TRACK_START)
	{
	  vcd_cue_t *_cue2 = _vcd_list_node_data (_vcd_list_node_next (node));

	  _etnf_array_t _etnf = { 0, };

	  _etnf.type = UINT32_TO_BE (0x3);
	  _etnf.start_lsn = uint32_to_be (_map (_obj, _cue->lsn));
	  _etnf.start = uint32_to_be (_map (_obj, _cue->lsn) * M2RAW_SECTOR_SIZE);
	  
	  _etnf.length = uint32_to_be ((_cue2->lsn - _cue->lsn) * M2RAW_SECTOR_SIZE);

	  vcd_data_sink_write (_obj->nrg_snk, &_etnf, sizeof (_etnf_array_t), 1);
	}
	
    }
  
  {
    uint32_t tracks = uint32_to_be (_obj->tracks);

    _chunk.id = UINT32_TO_BE (SINF_ID);
    _chunk.len = UINT32_TO_BE (sizeof (uint32_t));
    vcd_data_sink_write (_obj->nrg_snk, &_chunk, sizeof (_chunk_t), 1);

    vcd_data_sink_write (_obj->nrg_snk, &tracks, sizeof (uint32_t), 1);
  }

  _chunk.id = UINT32_TO_BE (END1_ID);
  _chunk.len = UINT32_TO_BE (0);
  vcd_data_sink_write (_obj->nrg_snk, &_chunk, sizeof (_chunk_t), 1);

  _chunk.id = UINT32_TO_BE (NERO_ID);
  _chunk.len = uint32_to_be (offset);
  vcd_data_sink_write (_obj->nrg_snk, &_chunk, sizeof (_chunk_t), 1);

  return 0;
}
 
static int
_vcd_image_nrg_write (void *user_data, const void *data, uint32_t lsn)
{
  const char *buf = data;
  _img_nrg_snk_t *_obj = user_data;
  uint32_t _lsn = _map (_obj, lsn);

  _sink_init (_obj);

  if (_lsn == -1)
    {
      /* vcd_debug ("ignoring %d", lsn); */
      return 0;
    }

  vcd_data_sink_seek(_obj->nrg_snk, _lsn * M2RAW_SECTOR_SIZE);
  vcd_data_sink_write(_obj->nrg_snk, buf + 12 + 4, M2RAW_SECTOR_SIZE, 1);

  if (_obj->cue_end_lsn - 1 == lsn)
    {
      vcd_debug ("ENDLSN reached! (%lu == %lu)", 
		 (long unsigned int) lsn, (long unsigned int) _lsn);
      return _write_tail (_obj, (_lsn + 1) * M2RAW_SECTOR_SIZE);
    }

  return 0;
}

static int
_sink_set_arg (void *user_data, const char key[], const char value[])
{
  _img_nrg_snk_t *_obj = user_data;

  if (!strcmp (key, "nrg"))
    {
      free (_obj->nrg_fname);

      if (!value)
	return -2;

      _obj->nrg_fname = strdup (value);
    }
  else
    return -1;

  return 0;
}

VcdImageSink *
vcd_image_sink_new_nrg (void)
{
  _img_nrg_snk_t *_data;

  vcd_image_sink_funcs _funcs = {
    .set_cuesheet = _set_cuesheet,
    .write        = _vcd_image_nrg_write,
    .free         = _sink_free,
    .set_arg      = _sink_set_arg
  };

  _data = _vcd_malloc (sizeof (_img_nrg_snk_t));
  _data->nrg_fname = strdup ("videocd.nrg");

  vcd_warn ("opening TAO NRG image for writing; TAO (S)VCD are NOT 100%% compliant!");

  return vcd_image_sink_new (_data, &_funcs);
}


--- NEW FILE: mpeg_stream.h ---
/*
    $Id: mpeg_stream.h,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2000 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
*/

#ifndef __VCD_MPEG_STREAM__
#define __VCD_MPEG_STREAM__

#include <libvcd/types.h>

/* Private includes */
#include "stream.h"
#include "data_structures.h"
#include "mpeg.h"

#define MPEG_PACKET_SIZE 2324

typedef struct _VcdMpegSource VcdMpegSource;

/* used in APS list */

struct aps_data
{
  uint32_t packet_no;
  double timestamp;
};

/* enums */

typedef enum {
  MPEG_AUDIO_NOSTREAM = 0,
  MPEG_AUDIO_1STREAM = 1,
  MPEG_AUDIO_2STREAM = 2,
  MPEG_AUDIO_EXT_STREAM = 3
} mpeg_audio_t;

typedef enum {
  MPEG_VIDEO_NOSTREAM = 0,
  MPEG_VIDEO_NTSC_STILL = 1,
  MPEG_VIDEO_NTSC_STILL2 = 2,
  MPEG_VIDEO_NTSC_MOTION = 3,

  MPEG_VIDEO_PAL_STILL = 5,
  MPEG_VIDEO_PAL_STILL2 = 6,
  MPEG_VIDEO_PAL_MOTION = 7
} mpeg_video_t;

/* mpeg stream info */

struct vcd_mpeg_stream_info;

/* mpeg packet info */

struct vcd_mpeg_packet_info;

/* access functions */

VcdMpegSource *
vcd_mpeg_source_new (VcdDataSource *mpeg_file);

/* scan the mpeg file... needed to be called only once */
typedef struct {
  long current_pack;
  long current_pos;
  long length;
} vcd_mpeg_prog_info_t;

typedef int (*vcd_mpeg_prog_cb_t) (const vcd_mpeg_prog_info_t *progress_info,
                                   void *user_data);

void
vcd_mpeg_source_scan (VcdMpegSource *obj, bool strict_aps, bool fix_scan_info,
                      vcd_mpeg_prog_cb_t callback, void *user_data);

/* gets the packet at given position */
int
vcd_mpeg_source_get_packet (VcdMpegSource *obj, unsigned long packet_no,
			    void *packet_buf, struct vcd_mpeg_packet_info *flags,
                            bool fix_scan_info);

void
vcd_mpeg_source_close (VcdMpegSource *obj);

const struct vcd_mpeg_stream_info *
vcd_mpeg_source_get_info (VcdMpegSource *obj);

long
vcd_mpeg_source_stat (VcdMpegSource *obj);

void
vcd_mpeg_source_destroy (VcdMpegSource *obj, bool destroy_file_obj);

#endif /* __VCD_MPEG_STREAM__ */

/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: directory.h ---
/*
    $Id: directory.h,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2000 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
*/

#ifndef _DIRECTORY_H_
#define _DIRECTORY_H_

#include <libvcd/types.h>

/* Private headers */
#include "data_structures.h"

/* opaque data structure representing the ISO directory tree */
typedef VcdTree VcdDirectory;

VcdDirectory *
_vcd_directory_new (void);

void
_vcd_directory_destroy (VcdDirectory *dir);

int
_vcd_directory_mkdir (VcdDirectory *dir, const char pathname[]);

int
_vcd_directory_mkfile (VcdDirectory *dir, const char pathname[], 
                       uint32_t start, uint32_t size,
                       bool form2_flag, uint8_t filenum);

uint32_t
_vcd_directory_get_size (VcdDirectory *dir);

void 
_vcd_directory_dump_entries (VcdDirectory *dir, void *buf, uint32_t extent);

void
_vcd_directory_dump_pathtables (VcdDirectory *dir, void *ptl, void *ptm);

#endif /* _DIRECTORY_H_ */


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: util.c ---
/*
    $Id: util.c,v 1.1 2005/04/04 22:29:43 dsalt-guest Exp $

    Copyright (C) 2000 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 <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/* Private includes */
#include "vcd_assert.h"
#include "bytesex.h"
#include "util.h"

static const char _rcsid[] = "$Id: util.c,v 1.1 2005/04/04 22:29:43 dsalt-guest Exp $";

size_t
_vcd_strlenv(char **str_array)
{
  size_t n = 0;

  vcd_assert (str_array != NULL);

  while(str_array[n])
    n++;

  return n;
}

void
_vcd_strfreev(char **strv)
{
  int n;
  
  vcd_assert (strv != NULL);

  for(n = 0; strv[n]; n++)
    free(strv[n]);

  free(strv);
}

char *
_vcd_strjoin (char *strv[], unsigned count, const char delim[])
{
  size_t len;
  char *new_str;
  unsigned n;

  vcd_assert (strv != NULL);
  vcd_assert (delim != NULL);

  len = (count-1) * strlen (delim);

  for (n = 0;n < count;n++)
    len += strlen (strv[n]);

  len++;

  new_str = _vcd_malloc (len);
  new_str[0] = '\0';

  for (n = 0;n < count;n++)
    {
      if (n)
        strcat (new_str, delim);
      strcat (new_str, strv[n]);
    }
  
  return new_str;
}

char **
_vcd_strsplit(const char str[], char delim) /* fixme -- non-reentrant */
{
  int n;
  char **strv = NULL;
  char *_str, *p;
  char _delim[2] = { 0, 0 };

  vcd_assert (str != NULL);

  _str = strdup(str);
  _delim[0] = delim;

  vcd_assert (_str != NULL);

  n = 1;
  p = _str;
  while(*p) 
    if (*(p++) == delim)
      n++;

  strv = _vcd_malloc (sizeof (char *) * (n+1));
  
  n = 0;
  while((p = strtok(n ? NULL : _str, _delim)) != NULL) 
    strv[n++] = strdup(p);

  free(_str);

  return strv;
}

void *
_vcd_malloc (size_t size)
{
  void *new_mem = malloc (size);

  vcd_assert (new_mem != NULL);

  memset (new_mem, 0, size);

  return new_mem;
}

void *
_vcd_memdup (const void *mem, size_t count)
{
  void *new_mem = NULL;

  if (mem)
    {
      new_mem = _vcd_malloc (count);
      memcpy (new_mem, mem, count);
    }
  
  return new_mem;
}

char *
_vcd_strdup_upper (const char str[])
{
  char *new_str = NULL;

  if (str)
    {
      char *p;

      p = new_str = strdup (str);

      while (*p)
        {
          *p = toupper (*p);
          p++;
        }
    }

  return new_str;
}


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: data_structures.c ---
/*
    $Id: data_structures.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2000 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 <stdlib.h>
#include <string.h>

#include <cdio/cdio.h>

/* Public headers */
#include <libvcd/types.h>

/* Private headers */
#include "vcd_assert.h"
#include "data_structures.h"
#include "util.h"

static const char _rcsid[] = "$Id: data_structures.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $";

struct _VcdList
{
  unsigned length;

  VcdListNode *begin;
  VcdListNode *end;
};

struct _VcdListNode
{
  VcdList *list;

  VcdListNode *next;

  void *data;
};

/* impl */

VcdList *
_vcd_list_new (void)
{
  VcdList *new_obj = _vcd_malloc (sizeof (VcdList));

  return new_obj;
}

void
_vcd_list_free (VcdList *list, int free_data)
{
  while (_vcd_list_length (list))
    _vcd_list_node_free (_vcd_list_begin (list), free_data);

  free (list);
}

unsigned
_vcd_list_length (const VcdList *list)
{
  vcd_assert (list != NULL);

  return list->length;
}

static bool
_bubble_sort_iteration (VcdList *list, _vcd_list_cmp_func cmp_func)
{
  VcdListNode **pnode;
  bool changed = false;
  
  for (pnode = &(list->begin);
       (*pnode) != NULL && (*pnode)->next != NULL;
       pnode = &((*pnode)->next))
    {
      VcdListNode *node = *pnode;
      
      if (cmp_func (node->data, node->next->data) <= 0)
        continue; /* n <= n->next */
      
      /* exch n n->next */
      *pnode = node->next;
      node->next = node->next->next;
      (*pnode)->next = node;
      
      changed = true;

      if (node->next == NULL)
        list->end = node;
    }

  return changed;
}

void _vcd_list_sort (VcdList *list, _vcd_list_cmp_func cmp_func)
{
  /* fixme -- this is bubble sort -- worst sorting algo... */

  vcd_assert (list != NULL);
  vcd_assert (cmp_func != 0);
  
  while (_bubble_sort_iteration (list, cmp_func));
}

void
_vcd_list_prepend (VcdList *list, void *data)
{
  VcdListNode *new_node;

  vcd_assert (list != NULL);

  new_node = _vcd_malloc (sizeof (VcdListNode));
  
  new_node->list = list;
  new_node->next = list->begin;
  new_node->data = data;

  list->begin = new_node;
  if (list->length == 0)
    list->end = new_node;

  list->length++;
}

void
_vcd_list_append (VcdList *list, void *data)
{
  vcd_assert (list != NULL);

  if (list->length == 0)
    {
      _vcd_list_prepend (list, data);
    }
  else
    {
      VcdListNode *new_node = _vcd_malloc (sizeof (VcdListNode));
      
      new_node->list = list;
      new_node->next = NULL;
      new_node->data = data;

      list->end->next = new_node;
      list->end = new_node;

      list->length++;
    }
}

void 
_vcd_list_foreach (VcdList *list, _vcd_list_iterfunc func, void *user_data)
{
  VcdListNode *node;

  vcd_assert (list != NULL);
  vcd_assert (func != 0);
  
  for (node = _vcd_list_begin (list);
       node != NULL;
       node = _vcd_list_node_next (node))
    func (_vcd_list_node_data (node), user_data);
}

VcdListNode *
_vcd_list_find (VcdList *list, _vcd_list_iterfunc cmp_func, void *user_data)
{
  VcdListNode *node;

  vcd_assert (list != NULL);
  vcd_assert (cmp_func != 0);
  
  for (node = _vcd_list_begin (list);
       node != NULL;
       node = _vcd_list_node_next (node))
    if (cmp_func (_vcd_list_node_data (node), user_data))
      break;

  return node;
}

/* node ops */

VcdListNode *
_vcd_list_at (VcdList *list, int idx)
{
  VcdListNode *node = _vcd_list_begin (list);

  if (idx < 0)
    return _vcd_list_at (list, _vcd_list_length (list) + idx);

  vcd_assert (idx >= 0);

  while (node && idx)
    {
      node = _vcd_list_node_next (node);
      idx--;
    }

  return node;
}

VcdListNode *
_vcd_list_begin (const VcdList *list)
{
  vcd_assert (list != NULL);

  return list->begin;
}

VcdListNode *
_vcd_list_end (VcdList *list)
{
  vcd_assert (list != NULL);

  return list->end;
}

VcdListNode *
_vcd_list_node_next (VcdListNode *node)
{
  if (node)
    return node->next;

  return NULL;
}

void 
_vcd_list_node_free (VcdListNode *node, int free_data)
{
  VcdList *list;
  VcdListNode *prev_node;

  vcd_assert (node != NULL);
  
  list = node->list;

  vcd_assert (_vcd_list_length (list) > 0);

  if (free_data)
    free (_vcd_list_node_data (node));

  if (_vcd_list_length (list) == 1)
    {
      vcd_assert (list->begin == list->end);

      list->end = list->begin = NULL;
      list->length = 0;
      free (node);
      return;
    }

  vcd_assert (list->begin != list->end);

  if (list->begin == node)
    {
      list->begin = node->next;
      free (node);
      list->length--;
      return;
    }

  for (prev_node = list->begin; prev_node->next; prev_node = prev_node->next)
    if (prev_node->next == node)
      break;

  vcd_assert (prev_node->next != NULL);

  if (list->end == node)
    list->end = prev_node;

  prev_node->next = node->next;

  list->length--;

  free (node);
}

void *
_vcd_list_node_data (VcdListNode *node)
{
  if (node)
    return node->data;

  return NULL;
}

/*
 * n-way tree based on list -- somewhat inefficent 
 */

struct _VcdTree
{
  VcdTreeNode *root;
};

struct _VcdTreeNode
{
  void *data;

  VcdListNode *listnode;
  VcdTree *tree;
  VcdTreeNode *parent;
  VcdList *children;
};

VcdTree *
_vcd_tree_new (void *root_data)
{
  VcdTree *new_tree;

  new_tree = _vcd_malloc (sizeof (VcdTree));

  new_tree->root = _vcd_malloc (sizeof (VcdTreeNode));

  new_tree->root->data = root_data;
  new_tree->root->tree = new_tree;
  new_tree->root->parent = NULL;
  new_tree->root->children = NULL;
  new_tree->root->listnode = NULL;
  
  return new_tree;
}

void
_vcd_tree_destroy (VcdTree *tree, bool free_data)
{
  _vcd_tree_node_destroy (tree->root, free_data);
  
  free (tree->root);
  free (tree);
}

void
_vcd_tree_node_destroy (VcdTreeNode *node, bool free_data)
{
  VcdTreeNode *child, *nxt_child;
  
  vcd_assert (node != NULL);

  child = _vcd_tree_node_first_child (node);
  while(child) {
    nxt_child = _vcd_tree_node_next_sibling (child);
    _vcd_tree_node_destroy (child, free_data);
    child = nxt_child;
  }

  if (node->children)
    {
      vcd_assert (_vcd_list_length (node->children) == 0);
      _vcd_list_free (node->children, true);
      node->children = NULL;
    }

  if (free_data)
    free (_vcd_tree_node_set_data (node, NULL));

  if (node->parent)
    _vcd_list_node_free (node->listnode, true);
  else
    _vcd_tree_node_set_data (node, NULL);
}

VcdTreeNode *
_vcd_tree_root (VcdTree *tree)
{
  return tree->root;
}

void *
_vcd_tree_node_data (VcdTreeNode *node)
{
  return node->data;
}

void *
_vcd_tree_node_set_data (VcdTreeNode *node, void *new_data)
{
  void *old_data = node->data;

  node->data = new_data;

  return old_data;
}

VcdTreeNode *
_vcd_tree_node_append_child (VcdTreeNode *pnode, void *cdata)
{
  VcdTreeNode *nnode;

  vcd_assert (pnode != NULL);

  if (!pnode->children)
    pnode->children = _vcd_list_new ();

  nnode = _vcd_malloc (sizeof (VcdTreeNode));

  _vcd_list_append (pnode->children, nnode);

  nnode->data = cdata;
  nnode->parent = pnode;
  nnode->tree = pnode->tree;
  nnode->listnode = _vcd_list_end (pnode->children);

  return nnode;
}

VcdTreeNode *
_vcd_tree_node_first_child (VcdTreeNode *node)
{
  vcd_assert (node != NULL);

  if (!node->children)
    return NULL;

  return _vcd_list_node_data (_vcd_list_begin (node->children));
}

VcdTreeNode *
_vcd_tree_node_next_sibling (VcdTreeNode *node)
{
  vcd_assert (node != NULL);

  return _vcd_list_node_data (_vcd_list_node_next (node->listnode));
}

void
_vcd_tree_node_sort_children (VcdTreeNode *node, _vcd_tree_node_cmp_func cmp_func)
{
  vcd_assert (node != NULL);

  if (node->children)
    _vcd_list_sort (node->children, (_vcd_list_cmp_func) cmp_func);
}

void
_vcd_tree_node_traverse (VcdTreeNode *node, 
                         _vcd_tree_node_traversal_func trav_func,
                         void *user_data) /* pre-order */
{
  VcdTreeNode *child;

  vcd_assert (node != NULL);

  trav_func (node, user_data);

  _VCD_CHILD_FOREACH (child, node)
    {
      _vcd_tree_node_traverse (child, trav_func, user_data);
    }
}

void
_vcd_tree_node_traverse_bf (VcdTreeNode *node, 
                            _vcd_tree_node_traversal_func trav_func,
                            void *user_data) /* breath-first */
{
  VcdList *queue;

  vcd_assert (node != NULL);

  queue = _vcd_list_new ();

  _vcd_list_prepend (queue, node);

  while (_vcd_list_length (queue))
    {
      VcdListNode *lastnode = _vcd_list_end (queue);
      VcdTreeNode *treenode = _vcd_list_node_data (lastnode);
      VcdTreeNode *childnode;

      _vcd_list_node_free (lastnode, false);

      trav_func (treenode, user_data);
      
      _VCD_CHILD_FOREACH (childnode, treenode)
        {
          _vcd_list_prepend (queue, childnode);
        }
    }

  _vcd_list_free (queue, false);
}

VcdTreeNode *_vcd_tree_node_parent (VcdTreeNode *node)
{
  return node->parent;
}

VcdTreeNode *_vcd_tree_node_root (VcdTreeNode *node)
{
  return node->tree->root;
}

bool _vcd_tree_node_is_root (VcdTreeNode *node)
{
  return (node->parent == NULL);
}

/* eof */


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */


--- NEW FILE: Makefile.in ---
# Makefile.in generated by automake 1.9.3 from Makefile.am.
# @configure_input@

# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
# 2003, 2004  Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.

@SET_MAKE@


SOURCES = $(libvcd_la_SOURCES) $(libvcdinfo_la_SOURCES)

[...971 lines suppressed...]

uninstall-hook:
	@if echo '$(libdir)' | egrep ^'$(XINE_PLUGINDIR)' >/dev/null; then \
	  list='$(lib_LTLIBRARIES)'; for p in $$list; do \
	    p="`echo $$p | sed -e 's/\.la$$/\.so/g;s|^.*/||'`"; \
	    echo " rm -f $(DESTDIR)$(libdir)/$$p"; \
	    rm -f $(DESTDIR)$(libdir)/$$p; \
	  done; \
	fi

mostlyclean-generic:
	-rm -f *~ \#* .*~ .\#*

maintainer-clean-generic:
	-@echo "This command is intended for maintainers to use;"
	-@echo "it deletes files that may require special tools to rebuild."
	-rm -f Makefile.in
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

--- NEW FILE: info_private.c ---
/*
    $Id: info_private.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $

    Copyright (C) 2003 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
*/
/* 
   Like vcdinfo but exposes more of the internal structure. It is probably
   better to use vcdinfo, when possible.
*/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdio.h>
#include <stddef.h>
#include <errno.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/util.h>

#include <libvcd/types.h>
#include <libvcd/files.h>

#include <libvcd/info.h>

/* Private headers */
#include "vcd_assert.h"
#include "bytesex.h"
#include "data_structures.h"
#include "info_private.h"
#include "pbc.h"

static const char _rcsid[] = "$Id: info_private.c,v 1.1 2005/04/04 22:29:42 dsalt-guest Exp $";

/*
  This fills in unassigned LIDs in the offset table.  Due to
  "rejected" LOT entries, some of these might not have gotten filled
  in while scanning PBC (if in fact there even was a PBC).

  Note: We assume that an unassigned LID is one whose value is 0.
 */
static void
vcdinf_update_offset_list(struct _vcdinf_pbc_ctx *obj, bool extended)
{
  if (NULL==obj) return;
  {
    VcdListNode *node;
    VcdList *unused_lids = _vcd_list_new();
    VcdListNode *next_unused_node = _vcd_list_begin(unused_lids);
    
    unsigned int last_lid=0;
    VcdList *offset_list = extended ? obj->offset_x_list : obj->offset_list;
    
    lid_t max_seen_lid=0;

    _VCD_LIST_FOREACH (node, offset_list)
      {
        vcdinfo_offset_t *ofs = _vcd_list_node_data (node);
        if (!ofs->lid) {
          /* We have a customer! Assign a LID from the free pool
             or take one from the end if no skipped LIDs.
          */
          VcdListNode *node=_vcd_list_node_next(next_unused_node);
          if (node != NULL) {
            lid_t *next_unused_lid=_vcd_list_node_data(node);
            ofs->lid = *next_unused_lid;
            next_unused_node=node;
          } else {
            max_seen_lid++;
            ofs->lid = max_seen_lid;
          }
        } else {
          /* See if we've skipped any LID numbers. */
          last_lid++;
          while (last_lid != ofs->lid ) {
            lid_t * lid=_vcd_malloc (sizeof(lid_t));
            *lid = last_lid;
            _vcd_list_append(unused_lids, lid);
          }
          if (last_lid > max_seen_lid) max_seen_lid=last_lid;
        }
      }
    _vcd_list_free(unused_lids, true);
  }
}

/*!
   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
vcdinf_visit_lot (struct _vcdinf_pbc_ctx *obj)
{
  const LotVcd *lot = obj->extended ? obj->lot_x : obj->lot;
  unsigned int n, tmp;
  bool ret=true;

  if (obj->extended) {
    if (!obj->psd_x_size) return false;
  } else if (!obj->psd_size) return false;

  for (n = 0; n < LOT_VCD_OFFSETS; n++)
    if ((tmp = vcdinf_get_lot_offset(lot, n)) != PSD_OFS_DISABLED)
      ret &= vcdinf_visit_pbc (obj, n + 1, tmp, true);

  _vcd_list_sort (obj->extended ? obj->offset_x_list : obj->offset_list, 
                  (_vcd_list_cmp_func) vcdinf_lid_t_cmp);

  /* Now really complete the offset table with LIDs.  This routine
     might obviate the need for vcdinf_visit_pbc() or some of it which is
     more complex. */
  vcdinf_update_offset_list(obj, obj->extended);
  return ret;
}

/*!
   Recursive routine to populate obj->offset_list or obj->offset_x_list
   by reading playback control entries referred to via lid.

   Returns false if there was some error.
*/
bool
vcdinf_visit_pbc (struct _vcdinf_pbc_ctx *obj, lid_t lid, unsigned int offset, 
                  bool in_lot)
{
  VcdListNode *node;
  vcdinfo_offset_t *ofs;
  unsigned int psd_size  = obj->extended ? obj->psd_x_size : obj->psd_size;
  const uint8_t *psd = obj->extended ? obj->psd_x : obj->psd;
  unsigned int _rofs = offset * obj->offset_mult;
  VcdList *offset_list;
  bool ret=true;

  vcd_assert (psd_size % 8 == 0);

  switch (offset)
    {
    case PSD_OFS_DISABLED:
    case PSD_OFS_MULTI_DEF:
    case PSD_OFS_MULTI_DEF_NO_NUM:
      return true;

    default:
      break;
    }

  if (_rofs >= psd_size)
    {
      if (obj->extended)
	vcd_warn ("psd offset out of range in extended PSD (%d >= %d)",
		   _rofs, psd_size);
      else
        vcd_warn ("psd offset out of range (%d >= %d)", _rofs, psd_size);
      return false;
    }

  if (!obj->offset_list)
    obj->offset_list = _vcd_list_new ();

  if (!obj->offset_x_list)
    obj->offset_x_list = _vcd_list_new ();

  if (obj->extended) {
    offset_list = obj->offset_x_list;
  } else 
    offset_list = obj->offset_list;

  _VCD_LIST_FOREACH (node, offset_list)
    {
      ofs = _vcd_list_node_data (node);

      if (offset == ofs->offset)
        {
          if (in_lot)
            ofs->in_lot = true;

          if (lid) {
            /* Our caller thinks she knows what our LID is.
               This should help out getting the LID for end descriptors
               if not other things as well.
             */
            ofs->lid = lid;
          }
          
          ofs->ext = obj->extended;

          return true; /* already been there... */
        }
    }

  ofs = _vcd_malloc (sizeof (vcdinfo_offset_t));

  ofs->ext    = obj->extended;
  ofs->in_lot = in_lot;
  ofs->lid    = lid;
  ofs->offset = offset;
  ofs->type   = psd[_rofs];

  switch (ofs->type)
    {
    case PSD_TYPE_PLAY_LIST:
      _vcd_list_append (offset_list, ofs);
      {
        const PsdPlayListDescriptor *d = (const void *) (psd + _rofs);
        const lid_t lid = vcdinf_pld_get_lid(d);

        if (!ofs->lid)
          ofs->lid = lid;
        else 
          if (ofs->lid != lid)
            vcd_warn ("LOT entry assigned LID %d, but descriptor has LID %d",
                      ofs->lid, lid);

        ret &= vcdinf_visit_pbc (obj, 0, vcdinf_pld_get_prev_offset(d), false);
        ret &= vcdinf_visit_pbc (obj, 0, vcdinf_pld_get_next_offset(d), false);
        ret &= vcdinf_visit_pbc (obj, 0, vcdinf_pld_get_return_offset(d), 
                                 false);
      }
      break;

    case PSD_TYPE_EXT_SELECTION_LIST:
    case PSD_TYPE_SELECTION_LIST:
      _vcd_list_append (offset_list, ofs);
      {
        const PsdSelectionListDescriptor *d =
          (const void *) (psd + _rofs);

        int idx;

        if (!ofs->lid)
          ofs->lid = uint16_from_be (d->lid) & 0x7fff;
        else 
          if (ofs->lid != (uint16_from_be (d->lid) & 0x7fff))
            vcd_warn ("LOT entry assigned LID %d, but descriptor has LID %d",
                      ofs->lid, uint16_from_be (d->lid) & 0x7fff);

        ret &= vcdinf_visit_pbc (obj, 0, vcdinf_psd_get_prev_offset(d), false);
        ret &= vcdinf_visit_pbc (obj, 0, vcdinf_psd_get_next_offset(d), false);
        ret &= vcdinf_visit_pbc (obj, 0, vcdinf_psd_get_return_offset(d), 
                                 false);
        ret &= vcdinf_visit_pbc (obj, 0, vcdinf_psd_get_default_offset(d), 
                                 false);
        ret &= vcdinf_visit_pbc (obj, 0, uint16_from_be (d->timeout_ofs), 
                                 false);

        for (idx = 0; idx < vcdinf_get_num_selections(d); idx++)
          ret &= vcdinf_visit_pbc (obj, 0, vcdinf_psd_get_offset(d, idx), 
                                   false);
      }
      break;

    case PSD_TYPE_END_LIST:
      _vcd_list_append (offset_list, ofs);
      break;

    default:
      vcd_warn ("corrupt PSD???????");
      free (ofs);
      return false;
      break;
    }
  return ret;
}

/*!  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
vcdinf_get_entry_lba(const EntriesVcd *entries, unsigned int entry_num)
{
  const msf_t *msf = vcdinf_get_entry_msf(entries, entry_num);
  return (msf != NULL) ? cdio_msf_to_lba(msf) : VCDINFO_NULL_LBA;
}

/*!  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 *
vcdinf_get_entry_msf(const EntriesVcd *entries, unsigned int entry_num)
{
  const unsigned int entry_count = uint16_from_be (entries->entry_count);
  return entry_num < entry_count ?
    &(entries->entry[entry_num].msf)
    : NULL;
}


/* 
 * Local variables:
 *  c-file-style: "gnu"
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */

--- NEW FILE: info_private.h ---
/*!
   \file vcdinf.h

    Copyright (C) 2002,2003 Rocky Bernstein <rocky@panix.com>

 \verbatim
    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

    Like vcdinfo but exposes more of the internal structure. It is probably
    better to use vcdinfo, when possible.
 \endverbatim
*/

#ifndef _VCD_INFO_PRIVATE_H
#define _VCD_INFO_PRIVATE_H

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <cdio/cdio.h>
#include <cdio/iso9660.h>
#include <libvcd/types.h>
#include <libvcd/files_private.h>

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

  struct _VcdInfo {
    vcd_type_t vcd_type;
    
    CdIo *img;
    
    iso9660_pvd_t pvd;
    
    InfoVcd info;
    EntriesVcd entries;
    
    VcdList *offset_list;
    VcdList *offset_x_list;
    uint32_t *seg_sizes; 
    lsn_t   first_segment_lsn;
    
    LotVcd *lot;
    LotVcd *lot_x;
    uint8_t *psd;
    uint8_t *psd_x;
    unsigned int psd_x_size;
    bool extended;

    bool has_xa;           /* True if has extended attributes (XA) */
    
    void *tracks_buf;
    void *search_buf;
    void *scandata_buf;
    
    char *source_name; /* VCD device or file currently open */
    
  };
  
  /*!  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 * vcdinf_get_entry_msf(const EntriesVcd *entries, 
				     unsigned int entry_num);

  struct _vcdinf_pbc_ctx {
    unsigned int psd_size;
    lid_t maximum_lid;
    unsigned offset_mult;
    VcdList *offset_x_list;
    VcdList *offset_list;
    
    LotVcd *lot;
    LotVcd *lot_x;
    uint8_t *psd;
    uint8_t *psd_x;
    unsigned int psd_x_size;
    bool extended;
  };

  /*!
     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 vcdinf_visit_lot (struct _vcdinf_pbc_ctx *obj);
  
  /*! 
     Recursive routine to populate obj->offset_list or obj->offset_x_list
     by reading playback control entries referred to via lid.

     Returns false if there was some error.
  */
  bool vcdinf_visit_pbc (struct _vcdinf_pbc_ctx *obj, lid_t lid, 
			 unsigned int offset, bool in_lot);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /*_VCD_INFO_PRIVATE_H*/