
/*
**
**  $VER: dispatch.c 1.5 (1.6.98)
**  mpegsystem.datatype 1.5
**
**  Dispatch routine for a DataTypes class
**
**  Written 1997/1998 by Roland 'Gizzy' Mainz
**  Original example source from David N. Junod
**
*/

/* main includes */
#include "classbase.h"
#include "classdata.h"

/*****************************************************************************/

/* is animation.datatype V41 ? */
#define ISV41 ((cb -> cb_SuperClassBase -> lib_Version) == 41U)

/*****************************************************************************/


/* local prototypes */

/* virtual demux fs-handler related */
DISPATCHERFLAGS static void              Handler( void );
                static void              RunHandler( struct ClassBase *cb, struct MPEGSystemInstData *msid );
                static void              KillHandler( struct ClassBase *cb, struct MPEGSystemInstData *msid );
                static void              dispatch_packet( struct ClassBase *cb, struct MPEGSystemInstData *msid, struct DosPacket *dp );
                static BPTR              GetStreamLock( struct ClassBase *cb, struct MPEGSystemInstData *msid, ULONG num, BOOL isaudio );
                static void              FreeLock( struct ClassBase *cb, struct MPEGSystemInstData *msid, struct FileLock *fl );

                static BOOL              SendSeek( struct ClassBase *, struct MPEGSystemInstData *, struct FileHandle *, LONG, LONG );
                static BOOL              SendRead( struct ClassBase *, struct MPEGSystemInstData *, struct FileHandle *, UBYTE *, ULONG );
                static struct DosPacket *ObtainDOSPacket( struct ClassBase *, struct MPEGSystemInstData * );
                static void              ReleaseDOSPacket( struct ClassBase *, struct MPEGSystemInstData *, struct DosPacket * );

                static void              STRPTR2BSTR( STRPTR s );

/* MPEG system stream demultiplexer related */
                static UBYTE            *get_buf_data( struct ClassBase *cb, struct MPEGSystemInstData *msid, long read_len );
                static UBYTE            *calc_time_stamp( struct ClassBase *cb, struct MPEGSystemInstData *msid, UBYTE *buf_ptr , long *time_stamp );
                static UBYTE             get_next_start_code( struct ClassBase *cb, struct MPEGSystemInstData *msid );
                static void              read_pack_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, PACK_header *pki );
                static void              read_system_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, SYSTEM_header *si );
                static void              read_packet_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, PACKET_header *pi );
                static void              print_pack_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, PACK_header *pki );
                static void              print_system_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, SYSTEM_header *si );
                static void              print_packet_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, PACKET_header *pi );

                static void              exitdemux( struct ClassBase *cb, struct MPEGSystemInstData *msid, LONG result, LONG result2 );
                static void              demux( struct ClassBase *cb, struct MPEGSystemInstData *msid, BPTR file );

/* class related */
                static BOOL              ScanFrames( struct ClassBase *, Object * );
                static void              mysprintf( struct ClassBase *, STRPTR, STRPTR, ... );

                static ULONG             SaveMPEGSystem( struct ClassBase *cb, struct IClass *cl, Object *o, struct dtWrite *dtw );

/*****************************************************************************/

/* Create "mpegsystem.datatype" BOOPSI class */
struct IClass *initClass( struct ClassBase *cb )
{
    struct IClass *cl;

    /* Create our class... */
    if( cl = MakeClass( MPEGSYSTEMDTCLASS, ANIMATIONDTCLASS, NULL, (ULONG)sizeof( struct MPEGSystemInstData ), 0UL ) )
    {
#define DTSTACKSIZE (16384UL)
      cl -> cl_Dispatcher . h_Entry    = (HOOKFUNC)StackSwapDispatch; /* see stackswap.c */
      cl -> cl_Dispatcher . h_SubEntry = (HOOKFUNC)Dispatch;          /* see stackswap.c */
      cl -> cl_Dispatcher . h_Data     = (APTR)DTSTACKSIZE;           /* see stackswap.c */
      cl -> cl_UserData                = (ULONG)cb;

      AddClass( cl );
    }

    return( cl );
}

/*****************************************************************************/

/* class dispatcher */
DISPATCHERFLAGS
ULONG Dispatch( REGA0 struct IClass *cl, REGA2 Object *o, REGA1 Msg msg )
{
    struct ClassBase           *cb = (struct ClassBase *)(cl -> cl_UserData);
    struct MPEGSystemInstData  *msid;
    ULONG                       retval = 0UL;

    switch( msg -> MethodID )
    {
/****** mpegsystem.datatype/OM_NEW *******************************************
*
*    NAME
*        OM_NEW -- Create a mpegsystem.datatype object.
*
*    FUNCTION
*        The OM_NEW method is used to create an instance of the
*        mpegsystem.datatype class.  This method is passed to the superclass
*        first. After this, mpegsystem.datatype parses the prefs file and 
*        makes a scan through the system stream to get index information 
*        about the single system stream packets.
*        After all, the mpegsystem.datatype starts two objects which reads
*        the single video and audio streams trougth an internal
*        "demultiplexer" (system stream splitter) filesystem.
*
*        Subclasses of mpegsystem.datatype are not supported. Any attempt to
*        create a subclass object of mpegsystem.datatype will be rejected by
*        this method.
*
*    ATTRIBUTES
*        The following attributes can be specified at creation time.
*
*        DTA_SourceType (ULONG) -- Determinates the type of DTA_Handle
*            attribute. Only DTST_FILE is supported.
*            If any other type was set in a given DTA_SourceType,
*            OM_NEW will be rejected.
*            Defaults to DTST_FILE.
*
*        DTA_Handle -- For DTST_FILE, a BPTR filehandle is expected. This
*            handle will be created by datatypesclass depeding on the DTF_#?
*            flag, which is DTF_BINARY here.  DTST_FILE, datatypesclass
*            creates a file handle from the given DTA_Name and DTA_Handle
*            (a BPTR returned by Lock).
*            A DTST_RAM (create empty object) source type requires a NULL
*            handle.
*
*    RESULT
*        If the object was created a pointer to the object is returned,
*        otherwise NULL is returned.
*
******************************************************************************
*
*/
      case OM_NEW:
      {
          struct TagItem *ti;

          /* We only support DTST_FILE or DTST_RAM as source type */
          if( ti = FindTagItem( DTA_SourceType, (((struct opSet *)msg) -> ops_AttrList) ) )
          {
            if( ((ti -> ti_Data) != DTST_FILE)
#ifdef HAS_ENCODER
&& ((ti -> ti_Data) != DTST_RAM)
#endif /* HAS_ENCODER */
 )
            {
              SetIoErr( ERROR_OBJECT_WRONG_TYPE );

              break;
            }
          }

#ifndef HAS_ENCODER
          /* This must not be a subclass of mpegsystem.datatype
           * (not implemented yet)
           */
          if( o == (Object *)cl )
#endif /* !HAS_ENCODER */
          {
            if( retval = DoSuperMethodA( cl, o, msg ) )
            {
              /* Load/scan frames... */
              if( !ScanFrames( cb, (Object *)retval ) )
              {
                /* Something went fatally wrong, dispose object */
                CoerceMethod( cl, (Object *)retval, OM_DISPOSE );
                retval = 0UL;
              }
            }
          }
#ifndef HAS_ENCODER
          else
          {
            /* Subclasses of mpegsystem.datatype are not implemented */
            SetIoErr( ERROR_NOT_IMPLEMENTED );
          }
#endif /* !HAS_ENCODER */
      }
          break;

/****** mpegsystem.datatype/OM_DISPOSE ***************************************
*
*    NAME
*        OM_DISPOSE -- Delete a mpegsystem.datatype object.
*
*    FUNCTION
*        The OM_DISPOSE method is used to delete an instance of the
*        mpegsystem.datatype class. This method is passed to the superclass
*        when it has completed.
*
*    RESULT
*        The object is deleted. 0UL is returned.
*
******************************************************************************
*
*/
      case OM_DISPOSE:
      {
          LONG saved_ioerr = IoErr(); /* Preserve I/O error */

          /* Get a pointer to our object data */
          msid = (struct MPEGSystemInstData *)INST_DATA( cl, o );

          /* Wait for any outstanding blitter usage (which may use one of our bitmaps) */
          WaitBlit();

          /* Dispose the current audio and video objects */
          DisposeDTObject( (msid -> msid_CurrVideo) );
          DisposeDTObject( (msid -> msid_CurrAudio) );

          /* Kill our virtual demux handler */
          KillHandler( cb, msid );

          /* Delete the frame pool */
          DeletePool( (msid -> msid_Pool) );

          /* Close verbose output file */
          if( (msid -> msid_VerboseOutput) && ((msid -> msid_VerboseOutput) != -1L) )
          {
            Close( (msid -> msid_VerboseOutput) );
          }

          /* Dispose object */
          DoSuperMethodA( cl, o, msg );

          /* Restore I/O error */
          SetIoErr( saved_ioerr );
      }
          break;

      case OM_UPDATE:
      {
          if( DoMethod( o, ICM_CHECKLOOP ) )
          {
            break;
          }
      }
      case OM_SET:
      {
          /* Pass the attributes to the animation class and force a refresh if we need it */
          if( retval = DoSuperMethodA( cl, o, msg ) )
          {
/* The following statement is commented out because mpegsystem.datatype does not allow
 * subclasses when the executable was compiled without the HAS_ENCODER define. Thus, the
 * following statement is NOP unless subclasses are supported...
 */
#ifdef HAS_ENCODER
            /* Top instance ? */
            if( OCLASS( o ) == cl )
#endif /* HAS_ENCODER */
            {
              struct RastPort *rp;

              /* Get a pointer to the rastport */
              if( rp = ObtainGIRPort( (((struct opSet *)msg) -> ops_GInfo) ) )
              {
                struct gpRender gpr;

                /* Force a redraw */
                gpr . MethodID   = GM_RENDER;
                gpr . gpr_GInfo  = ((struct opSet *)msg) -> ops_GInfo;
                gpr . gpr_RPort  = rp;
                gpr . gpr_Redraw = GREDRAW_UPDATE;

                DoMethodA( o, (Msg)(&gpr) );

                /* Release the temporary rastport */
                ReleaseGIRPort( rp );

                /* We did an update... */
                retval = 0UL;
              }
            }
          }
      }
          break;

/****** mpegsystem.datatype/DTM_WRITE *******************************************
*
*    NAME
*        DTM_WRITE -- Save data
*
*    FUNCTION
*        This method saves the object's contents to disk.
*
*        If dtw_Mode is DTWM_IFF, the method is passed unchanged to the
*        superclass, animation.datatype, which writes a single IFF ILBM
*        picture.
*
*        If dtw_mode is DTWM_RAW, the object saved an MPEG System stream to
*        the filehandle given, starting with the current frame until
*        the end is reached.
*        The sequence saved can be controlled by the ADTA_Frame, ADTA_Frames
*        and ADTA_FrameIncrement attributes (see TAGS section below).
*
*    TAGS
*        When writing the local ("raw") format, MPEG System movie, the
*        following attributes are recognized:
*
*        ADTA_Frame (ULONG) - start frame, saving starts here.
*            Defaults to the current frame displayed.
*
*        ADTA_Frames (ULONG) - the number of frames to be saved,
*            Defaults to (max_num_of_frames - curr_frame).
*
*        ADTA_FrameIncrement (ULONG) - frame increment when saving.
*            Defaults to 1, which means: "jump to next frame".
*
*    NOTE
*        This function is not implemented yet. A possible implementation 
*        would implement ACTION_WRITE in the virtial implementation and uses
*        mpegvideo.datatype and mpegaudio.datatype encoders...
*
*    RESULT
*        Returns 0 for failure (IoErr() returns result2), non-zero
*        for success.
*
******************************************************************************
*
*/
      case DTM_WRITE:
      {
          struct dtWrite *dtw;

          dtw = (struct dtWrite *)msg;

          /* Local data format (MPEG System) requested ? */
          if( (dtw -> dtw_Mode) == DTWM_RAW )
          {
            retval = SaveMPEGSystem( cb, cl, o, dtw );
          }
          else
          {
            /* Pass msg to superclass (which writes a single frame as an IFF ILBM picture)... */
            retval = DoSuperMethodA( cl, o, msg );
          }
      }
          break;


/****** mpegsystem.datatype/ADTM_LOADFRAME ***********************************
*
*    NAME
*        ADTM_LOADFRAME -- Load frame
*
*    FUNCTION
*        The ADTM_LOADFRAME method is used to obtain the bitmap and timing
*        data of the animation.
*        The method is passed to the embedded mpegvideo.datatype object.
*        If successfull, the sample data of struct adtFrame are replaced
*        by the corresponsing audio data from the embedded 
*        mpegaudio.datatype object.
*
*    RESULT
*        Returns the result from the embedded mpegvideo.datatype object
*        (including Result2).
*
******************************************************************************
*
*/
      case ADTM_LOADFRAME:
      {
          struct adtFrame *alf = (struct adtFrame *)msg;

          /* Get a pointer to our object data */
          msid = (struct MPEGSystemInstData *)INST_DATA( cl, o );

          if( msid -> msid_CurrVideo )
          {
            if( retval = DoMethodA( (msid -> msid_CurrVideo), msg ) )
            {
              /* Add here the audio part..
               * A better way would be to send SDTM_LOADSAMPLE to load the sample
               * fragment
               */
              if( msid -> msid_Sample )
              {
                UBYTE *sample        = msid -> msid_Sample;
                ULONG  sample_offset = (alf -> alf_Frame)            * (msid -> msid_SamplesPerFrame);
                ULONG  len           = ((alf -> alf_Duration) + 1UL) * (msid -> msid_SamplesPerFrame);

                /* Sample completely out-of-range ? */
                if( sample_offset >= (msid -> msid_SampleLength) )
                {
                  /* No sample data ! */
                  sample        = NULL;
                  sample_offset = 0UL;
                  len           = 0UL;
                }
                else
                {
                  /* Sample partial out of range ? */
                  if( (sample_offset + len) > (msid -> msid_SampleLength) )
                  {
                    /* Cut it ! */
                    len -= ((sample_offset + len) - (msid -> msid_SampleLength));
                  }
                }

                /* Fill in the message body */
                alf -> alf_Sample       = sample + sample_offset;
                alf -> alf_SampleLength = len;
                alf -> alf_Period       = msid -> msid_Period;
              }
            }
          }
          else
          {
            /* No mpegvideo.datatype object (Should not occur, but...). */
            SetIoErr( ERROR_OBJECT_NOT_FOUND );
          }
      }
          break;

/****** mpegsystem.datatype/ADTM_UNLOADFRAME ************************************
*
*    NAME
*        ADTM_UNLOADFRAME -- Unload frame contents
*
*    FUNCTION
*        The ADTM_UNLOADFRAME method is used to release the contents of a
*        animation frame.
*
*        This method is passed to the embedded mpegvideo.datatype object.
*
*    RESULT
*        Returns the result from the embedded mpegvideo.datatype object.
*
******************************************************************************
*
*/
      case ADTM_UNLOADFRAME:
      {
          /* Get a pointer to our object data */
          msid = (struct MPEGSystemInstData *)INST_DATA( cl, o );

          if( msid -> msid_CurrVideo )
          {
            retval = DoMethodA( (msid -> msid_CurrVideo), msg );

            /* Here we should do an SDTM_UNLOADSAMPLE if the sample data were
             * obtained using SDTM_LOADSAMPLE
             */
          }
      }
          break;

      /* Let the superclass handle everything else */
      default:
      {
          retval = DoSuperMethodA( cl, o, msg );
      }
          break;
    }

    return( retval );
}


static
BOOL ScanFrames( struct ClassBase *cb, Object *o )
{
    struct MPEGSystemInstData *msid     = (struct MPEGSystemInstData *)INST_DATA( (cb -> cb_Lib . cl_Class), o );
    BOOL                       success  = FALSE;
    LONG                       error    = 0L;

    InitSemaphore( (&(msid -> msid_SigSem)) );

    /* Create a memory pool for frame nodes */
    if( msid -> msid_Pool = CreatePool( MEMF_PUBLIC, 8192UL, 8192UL ) )
    {
      BPTR  fh;               /* handle (file handle)      */
      ULONG sourcetype;       /* type of stream (either DTST_FILE or DTST_RAM) */

      /* Prefs defaults */

      /* Read prefs */
      ReadENVPrefs( cb, msid );

      /* Get file handle, handle type and BitMapHeader */
      if( GetDTAttrs( o, DTA_SourceType,    (&sourcetype),
                         DTA_Handle,        (&fh),
                         DTA_Name,          (&(msid -> msid_ProjectName)),
                         TAG_DONE ) == 3UL )
      {
        switch( sourcetype )
        {
          case DTST_FILE:
          {
              /* we have the filehandle - anything what we want... */
          }
              break;

#ifdef HAS_ENCODER
          case DTST_RAM:
          {
              /* do nothing */
          }
              break;
#endif /* HAS_ENCODER */

          default:
          {
              /* unsupported source type */
              error = ERROR_NOT_IMPLEMENTED;
          }
              break;
        }

        /* Any error ? */
        if( error == 0L )
        {
          if( fh )
          {
            /* Run system-stream demultiplexer... */
            demux( cb, msid, fh );

            /* ...success ? */
            if( msid -> Handler . Process )
            {
              BPTR v_lock;

              /* Get first video stream */
              if( v_lock = GetStreamLock( cb, msid, 0UL, FALSE ) )
              {
                if( msid -> msid_CurrVideo = NewDTObject( NULL, DTA_SourceType, DTST_FILE,
                                                                DTA_Handle,     v_lock,
                                                                DTA_GroupID,    GID_ANIMATION,
                                                                TAG_DONE ) )
                {
                  ULONG                 video_modeid;
                  ULONG                *video_cregs;
                  struct ColorRegister *video_cm;
                  ULONG                 video_numcolors;
                  ULONG                 video_animwidth,
                                        video_animdepth,
                                        video_animheight;
                  ULONG                 video_frame,
                                        video_numframes,
                                        video_fps = 0UL,
                                        video_tpf = 0UL;
                  struct BitMap        *video_keyframe;

                  if( GetDTAttrs( (msid -> msid_CurrVideo),
                                  ADTA_ModeID,                           (&video_modeid),
                                  ADTA_CRegs,                            (&video_cregs),
                                  ADTA_ColorRegisters,                   (&video_cm),
                                  ADTA_NumColors,                        (&video_numcolors),
                                  ADTA_Width,                            (&video_animwidth),
                                  ADTA_Height,                           (&video_animheight),
                                  ADTA_Depth,                            (&video_animdepth),
                                  ADTA_Frame,                            (&video_frame),
                                  ADTA_Frames,                           (&video_numframes),
                                  XTAG( !ISV41, ADTA_FramesPerSecond ),  (&video_fps),
                                  XTAG(  ISV41, ADTA_TicksPerFrame   ),  (&video_tpf),
                                  ADTA_KeyFrame,                         (&video_keyframe),
                                  TAG_DONE ) == 11UL )
                  {
                    /* This should NEVER fail, but... */
                    if( video_numframes && video_keyframe && (video_fps || video_tpf) )
                    {
                      ULONG                *cregs;
                      struct ColorRegister *cm;
                      ULONG                 numcolors;

                      SetDTAttrs( o, NULL, NULL, ADTA_NumColors, video_numcolors, TAG_DONE );

                      if( GetDTAttrs( o, ADTA_CRegs,          (&cregs),
                                         ADTA_ColorRegisters, (&cm),
                                         ADTA_NumColors,      (&numcolors),
                                         TAG_DONE ) == 3UL )
                      {
                        /* Check if we got memory for the palette (if we have one) */
                        if( (cregs && cm && (numcolors == video_numcolors)) || (video_numcolors == 0UL) )
                        {
                          BPTR a_lock;

                          /* Copy base palette (if we have one) */
                          if( video_numcolors )
                          {
                            CopyMem( video_cregs, cregs, (video_numcolors * 3UL * sizeof( ULONG )) );
                            CopyMem( video_cm,    cm,    (video_numcolors * 3UL * sizeof( struct ColorRegister )) );
                          }

                          msid -> msid_TicksPerFrame = ((ISV41)?(video_tpf):(TICK_FREQ / video_fps));

                          SetDTAttrs( o, NULL, NULL, DTA_ObjName,                           (msid -> msid_ProjectName),
                                                     ADTA_ModeID,                           video_modeid,
                                                     ADTA_Width,                            video_animwidth,
                                                     ADTA_Height,                           video_animheight,
                                                     ADTA_Depth,                            video_animdepth,
                                                     ADTA_Frame,                            video_frame,
                                                     ADTA_Frames,                           video_numframes,
                                                     XTAG( !ISV41, ADTA_FramesPerSecond ),  video_fps,
                                                     XTAG(  ISV41, ADTA_TicksPerFrame   ),  video_tpf,
                                                     ADTA_KeyFrame,                         video_keyframe,
                                                     TAG_DONE );

                          /* Get first audio stream */
                          if( a_lock = GetStreamLock( cb, msid, 0UL, TRUE ) )
                          {
                            if( msid -> msid_CurrAudio = NewDTObject( NULL, DTA_SourceType, DTST_FILE,
                                                                            DTA_Handle,     a_lock,
                                                                            DTA_GroupID,    GID_SOUND,
                                                                            TAG_DONE ) )
                            {
                              ULONG volume;

                              GetDTAttrs( (msid -> msid_CurrAudio), SDTA_Sample,       (&(msid -> msid_Sample)),
                                                                    SDTA_SampleLength, (&(msid -> msid_SampleLength)),
                                                                    SDTA_Period,       (&(msid -> msid_Period)),
                                                                    SDTA_Volume,       (&volume),
                                                                    TAG_DONE );

                              msid -> msid_SamplesPerFrame = ((SysBase -> ex_EClockFrequency) * 10UL) / ((msid -> msid_Period) * (TICK_FREQ / (msid -> msid_TicksPerFrame)) * 2UL);

                              SetDTAttrs( o, NULL, NULL, ADTA_Sample,       (msid -> msid_Sample),
                                                         ADTA_SampleLength, (msid -> msid_SamplesPerFrame),
                                                         ADTA_Period,       (msid -> msid_Period),
                                                         ADTA_Volume,       volume,
                                                         TAG_DONE );
                                                         
                              success = TRUE;
                            }
                            else
                            {
                              /* NewDTObjectA failed */
                              error = IoErr();
                            }
                          }
                          else
                          {
                            /* No audio lock */
                            error = IoErr();
                          }
                        }
                        else
                        {
                          /* no palette memory */
                          error = ERROR_NO_FREE_STORE;
                        }
                      }
                      else
                      {
                        /* We did not get all attributes we need... */
                        error = ERROR_OBJECT_WRONG_TYPE;
                      }
                    }
                  }
                  else
                  {
                    /* We did not get all attributes we need... */
                    error = ERROR_OBJECT_WRONG_TYPE;
                  }
                }
                else
                {
                  /* NewDTObject failed */
                  error = IoErr();
                }
              }
              else
              {
                /* no video stream lock */
                error = IoErr();
              }
            }
            else
            {
              /* demuxer failed */
              error = msid -> Demux . retval2;
            }
          }
          else
          {
            /* No file handle ? - Be sure we got a DTST_RAM sourcetype */
            if( sourcetype == DTST_RAM )
            {
              success = TRUE;
            }
            else
            {
              /* No handle ! */
              error = ERROR_REQUIRED_ARG_MISSING;
            }
          }
        }
      }
      else
      {
        /* can't get required attributes from superclass */
        error = ERROR_OBJECT_WRONG_TYPE;
      }
    }
    else
    {
      /* no memory pool */
      error = ERROR_NO_FREE_STORE;
    }

    SetIoErr( error );

    return( success );
}


/*****************************************************************************/


void OpenLogfile( struct ClassBase *cb, struct MPEGSystemInstData *msid )
{
    if( ((msid -> msid_VerboseOutput) == NULL) || ((msid -> msid_VerboseOutput) == -1L) )
    {
      STRPTR confile;

      if( confile = (STRPTR)AllocVec( (((msid -> msid_ProjectName)?(strlen( (msid -> msid_ProjectName) )):(0UL)) + 100UL), MEMF_PUBLIC ) )
      {
        mysprintf( cb, confile, "CON:////MPEG System DataType %s/auto/wait/close/inactive",
                   ((msid -> msid_ProjectName)?(FilePart( (msid -> msid_ProjectName) )):(NULL)) );

        msid -> msid_VerboseOutput = Open( confile, MODE_READWRITE );

        FreeVec( confile );
      }
    }
}


static
void mysprintf( struct ClassBase *cb, STRPTR buffer, STRPTR fmt, ... )
{
    APTR args;

    args = (APTR)((&fmt) + 1);

    RawDoFmt( fmt, args, (void (*))"\x16\xc0\x4e\x75", buffer );
}


void error_printf( struct ClassBase *cb, struct MPEGSystemInstData *msid, STRPTR format, ... )
{
    OpenLogfile( cb, msid );

    if( (msid -> msid_VerboseOutput) && ((msid -> msid_VerboseOutput) != -1L) )
    {
      VFPrintf( (msid -> msid_VerboseOutput), format, (APTR)((&format) + 1) );
    }
}


void syntax_printf( struct ClassBase *cb, struct MPEGSystemInstData *msid, STRPTR format, ... )
{
    if( msid -> msid_DoSyntax )
    {
      OpenLogfile( cb, msid );

      if( (msid -> msid_VerboseOutput) && ((msid -> msid_VerboseOutput) != -1L) )
      {
        VFPrintf( (msid -> msid_VerboseOutput), format, (APTR)((&format) + 1) );
      }
    }
}


void debug_printf( struct ClassBase *cb, struct MPEGSystemInstData *msid, STRPTR format, ... )
{
    if( msid -> msid_DoDebug )
    {
      OpenLogfile( cb, msid );

      if( (msid -> msid_VerboseOutput) && ((msid -> msid_VerboseOutput) != -1L) )
      {
        VFPrintf( (msid -> msid_VerboseOutput), format, (APTR)((&format) + 1) );
      }
    }
}


void verbose_printf( struct ClassBase *cb, struct MPEGSystemInstData *msid, STRPTR format, ... )
{
    if( (msid -> msid_VerboseOutput) && ((msid -> msid_VerboseOutput) != -1L) )
    {
      VFPrintf( (msid -> msid_VerboseOutput), format, (APTR)((&format) + 1) );
    }
}


static
ULONG SaveMPEGSystem( struct ClassBase *cb, struct IClass *cl, Object *o, struct dtWrite *dtw )
{
    ULONG retval = 0UL;
    LONG  error  /*= 0L*/;

#ifdef HAS_ENCODER
    /* A NULL file handle is a nop (GMultiView uses this to test if a datatype supports RAW writing) */
    if( dtw -> dtw_FileHandle )
    {
      struct MPEGSystemInstData *msid = (struct MPEGSystemInstData *)INST_DATA( cl, o );

      ULONG                modeid;
      ULONG               *cregs;
      ULONG                numcolors;
      ULONG                startframe = 0UL,
                           numframes  = 0UL,
                           framestep  = 1UL;
      ULONG                fps        = 0UL;
      struct BitMap       *keyframe;
      ULONG                animwidth,
                           animheight,
                           animdepth;

      if( GetDTAttrs( o, ADTA_ModeID,           (&modeid),
                         ADTA_CRegs,            (&cregs),
                         ADTA_NumColors,        (&numcolors),
                         ADTA_Width,            (&animwidth),
                         ADTA_Height,           (&animheight),
                         ADTA_Depth,            (&animdepth),
                         ADTA_Frame,            (&startframe),
                         ADTA_Frames,           (&numframes),
                         ADTA_FramesPerSecond,  (&fps),
                         ADTA_KeyFrame,         (&keyframe),
                         TAG_DONE ) == 10UL )
      {
        struct TagItem     *tstate,
                           *ti;

        numframes -= startframe;

        tstate = dtw -> dtw_AttrList;

        while( ti = NextTagItem( (&tstate) ) )
        {
          switch( ti -> ti_Tag )
          {
            case ADTA_Frame:            startframe = ti -> ti_Data; break;
            case ADTA_Frames:           numframes  = ti -> ti_Data; break;
            case ADTA_FrameIncrement:   framestep  = ti -> ti_Data; break;
          }
        }

        if( framestep == 0UL ) framestep = 1UL;

        verbose_printf( cb, msid, "saving mpeg system movie %lu %lu %lu\n", startframe, numframes, framestep );

        /* here should follow the encoder part... */
      }
      else
      {
        error_printf( cb, msid, "not enougth attributes\n" );
      }
    }
#else
    error = ERROR_NOT_IMPLEMENTED; /* no encoder yet */
#endif /* HAS_ENCODER */

    SetIoErr( error );

    return( retval );
}

/*****************************************************************************/

static
UBYTE *get_buf_data( struct ClassBase *cb, struct MPEGSystemInstData *msid, long read_len )
{
    long   len_left;
    long   len_read;
    long   temp1;
    UBYTE *buf_ptr;

    /* Calculate how much data is left in the buffer */
    len_left = (msid -> Demux . raw_data_buf_len) - ((msid -> Demux . raw_data_buf_ptr) - (msid -> Demux . raw_data_buf));

    /* Not enough for the calling function's needs */
    if( len_left < read_len )
    {
      /* There is some data left, so copy to start of buffer  */
      if( len_left > 0 )
      {
        memcpy( (msid -> Demux . raw_data_buf), (msid -> Demux . raw_data_buf_ptr), (size_t)len_left );
      }

      msid -> Demux . raw_data_buf_ptr = &msid -> Demux . raw_data_buf[ 0 ];

      temp1 = MAX_DATA_BUF_LEN - len_left;  /* To fill the rest of the buffer */

      /* block pos */
      msid -> Demux . curr_file_pos = msid -> Demux . fdin_pos; /* Get file pos */

      msid -> Demux . curr_file_pos_in_buffer = ((msid -> Demux . raw_data_buf_ptr) + len_left);

      debug_printf( cb, msid, "read: %ld, len %ld, left %ld\n", (LONG)(msid -> Demux . curr_file_pos), (LONG)temp1, (LONG)len_left );

      len_read = Read( (msid -> Demux . fdin), (msid -> Demux . curr_file_pos_in_buffer), (LONG)temp1 );

      if( len_read == -1L )
      {
        LONG err = IoErr();

        error_printf( cb, msid, "Error: reading system file\n" );
        exitdemux( cb, msid, RETURN_FAIL, err );
      }
      else
      {
        msid -> Demux . fdin_pos += len_read;
      }

      /* no data left in file - data should never have been requested */
      if( len_read == 0 )
      {
        error_printf( cb, msid, "Error: reached EOF, requested %ld bytes\n", temp1 );
        exitdemux( cb, msid, RETURN_FAIL, DTERROR_NOT_ENOUGH_DATA );
      }

      msid -> Demux . raw_data_buf_len = len_left + len_read;

      /* Read done to end of file but still not enough data to complete request */
      if( (msid -> Demux . raw_data_buf_len) < read_len )
      {
        error_printf( cb, msid, "Error: insufficient data for read\n" );
        exitdemux( cb, msid, RETURN_FAIL, DTERROR_NOT_ENOUGH_DATA );
      }
    }

    buf_ptr                           = msid -> Demux . raw_data_buf_ptr; /* For return to calling function.       */
    msid -> Demux . raw_data_buf_ptr += read_len;                         /* Global advanced for use at next call. */

    return( buf_ptr );
}


static
UBYTE *calc_time_stamp( struct ClassBase *cb, struct MPEGSystemInstData *msid, UBYTE *buf_ptr, long *time_stamp )
{
    /* Determine Time Stamp
     * Contents:
     * Byte  0: 4 bits: depend on SCR/PTS/DTS
     *          3 bits: Time32..30,
     *          1 bit marker:1
     * Byte  1: 8 bits Time29..22.
     * Byte  2: 7 bits Time21..15,
     *          1 bit marker
     * Byte  3: 8 bits Time14..7.
     * Byte  4: 7 bits Time6..0,
     *          1 bit marker
     */

    *time_stamp = ((((long)(*buf_ptr)) & 0x0E) << 29);

    /* Marker_bit check */
    if( (*buf_ptr & (UBYTE)0x01) != (UBYTE)0x01 )
    {
      syntax_printf( cb, msid, "Expecting marker_bit value '1' at calc_time_stamp A.\n" );
      exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
    }

    buf_ptr++;

    *time_stamp |= ((((long)(*buf_ptr)) & 0xFF) << 22);
    buf_ptr++;

    *time_stamp |= ((((long)(*buf_ptr)) & 0xFE) << 14);

    /* Marker_bit check */
    if( (*buf_ptr & (UBYTE)0x01) != (UBYTE)0x01 )
    {
      syntax_printf( cb, msid, "Expecting marker_bit value '1' at calc_time_stamp B.\n" );
      exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
    }

    buf_ptr++;

    *time_stamp |= ((((long)(*buf_ptr)) & 0xFF) <<  7);
    buf_ptr++;

    *time_stamp |= ((((long)(*buf_ptr)) & 0xFE) >>  1);

    /* Marker_bit check */
    if( (*buf_ptr & (UBYTE)0x01) != (UBYTE)0x01 )
    {
      syntax_printf( cb, msid, "Expecting marker_bit value '1' at calc_time_stamp C.\n" );
      exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
    }

    buf_ptr++;

    return( buf_ptr );
}


static
UBYTE get_next_start_code( struct ClassBase *cb, struct MPEGSystemInstData *msid )
{
    UBYTE *buf_ptr = NULL; /* dead assignment, but avoids SAS/C "Warning 317: possibly uninitialized variable" */
    UWORD  zeros;

    /* Scan for start code; should be hex 00 00 01 for Pack Start Code,
     * System Start Code, Packet Start Code or ISO_11172_end_code.
     * Leading zeros are ignored.
     */
    for( ;; )
    {
      zeros = 0;

      for( ;; )
      {
        buf_ptr = get_buf_data( cb, msid, 1 );

        if( *buf_ptr == 0x00 )
        {
          zeros++;
        }
        else
        {
          if( zeros >= 2 )
          {
            if( *buf_ptr != 0x01 )
            {
              zeros = 0;
            }
            else
            {
              break;
            }
          }
        }
      }

      buf_ptr = get_buf_data( cb, msid, 1 );

      if( (msid -> Demux . system_found) || (*buf_ptr == 0xBB) )
      {
        break;
      }
    }
    
    switch( *buf_ptr )
    {
      case (UBYTE)0xB9:
          return( (UBYTE)ISO_11172_END_CODE );

      case (UBYTE)0xBA:
          return( (UBYTE)PACK_START_CODE );

      case (UBYTE)0xBB:
      {
          msid -> Demux . system_found = TRUE;
          verbose_printf( cb, msid, "system start\n" );

          return( (UBYTE)SYSTEM_START_CODE );
      }
    }

    return( *buf_ptr );  /* It must be a stream_id. */
}


static
void read_pack_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, PACK_header *pki )
{
    UBYTE *buf_ptr;

    pki -> SCR = 0;

    buf_ptr = get_buf_data( cb, msid, PACK_HEADER_LENGTH );

    /* Determine System Clock Reference (SCR)
     * Check marker_bits: '0010' = 0x2
     */

    if( (*buf_ptr & (UBYTE)0xF0) != (UBYTE)0x20 )
    {
      syntax_printf( cb, msid, "Expecting '0010' after pack_start_code.\n" );
      exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
    }

    buf_ptr = calc_time_stamp( cb, msid, buf_ptr, (&(pki -> SCR)) );

    /* Determine Mux Rate
     * Contents:
     * Byte 5: 1 bit marker,
     *         7 bits mux_rate.
     * Byte 6: 8 bits mux_rate.
     * Byte 7: 7 bits mux_rate,
     *         1 bit marker
     */
    pki -> mux_rate = ((((long)(*buf_ptr)) & 0x7F) << 14);

    /* Marker_bit check */
    if( (*buf_ptr & (UBYTE)0x80) != (UBYTE)0x80 )
    {
      syntax_printf( cb, msid, "byte= %lx.\n", *buf_ptr );
      syntax_printf( cb, msid, "Expecting marker_bit value '1' at read_pack_header A.\n" );
      exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
    }

    buf_ptr++;

    pki -> mux_rate |= ((((long)(*buf_ptr)) & 0xFF) << 7);
    buf_ptr++;

    pki -> mux_rate |= ((((long)(*buf_ptr)) & 0xFE) >> 1);

    /* Marker_bit check */
    if( (*buf_ptr & (UBYTE)0x01) != (UBYTE)0x01 )
    {
      syntax_printf( cb, msid, "Expecting marker_bit value '1' at read_pack_header B.\n" );
      exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
    }
}


static
void read_system_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, SYSTEM_header *si )
{
    UBYTE *buf_ptr;
    long   j;
    UBYTE  test;

    /* Read System Header Length: 2 Bytes after start code.
     * Length of header after header length bytes.
     * Bytes  0 & 1: 8 bits header_length--high & low
     */
    buf_ptr = get_buf_data( cb, msid, 2 );
    si -> header_length  = (ULONG)(((long)(*buf_ptr)) << 8);
    buf_ptr++;
    si -> header_length |= (ULONG)(((long)(*buf_ptr)) & 0xFF);

    /* Read the rest of the header */
    buf_ptr = get_buf_data( cb, msid, (si -> header_length) );

    /* Byte  0: 1 bit marker, 
     *          7 bits rate_bound--high
     */
    si -> rate_bound  = (ULONG)((((long)*buf_ptr) & 0x7F) << 15);

    /* Marker bit check */
    if( (*buf_ptr & (UBYTE)0x80) != (UBYTE)0x80 )
    {
      syntax_printf( cb, msid, "Expecting marker_bit value '1' before rate_bound--high.\n" );
      exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
    }

    buf_ptr++;

    /* Byte  1: 8 bits rate_bound--mid */
    si -> rate_bound += (ULONG)((((long)*buf_ptr) & 0xFF) << 7);
    buf_ptr++;

    /* Byte  2: 7 bits rate_bound-low,
     *          1 bit marker
     */
    si -> rate_bound += (ULONG)((((long)*buf_ptr) & 0xFE) >>  1);

    /* Marker bit check */
    if( (*buf_ptr & (UBYTE)0x01) != (UBYTE)0x01 )
    {
      syntax_printf( cb, msid, "Expecting marker_bit value '1' after rate_bound.\n" );
      exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
    }

    buf_ptr++;

    /* Byte  3: 6 bits audio_bound, 
     * 1 bit fixed_flag,
     * 1bit CSPS_flag 
     */
    si -> audio_bound = (ULONG)((((long)*buf_ptr) & 0xFF) >> 2);
    si -> fixed_flag  =  (BOOL)((((long)*buf_ptr) & 0x02) >> 1);
    si -> CSPS_flag   =  (BOOL) (((long)*buf_ptr) & 0x01);
    buf_ptr++;

    /* Byte 4: 1b system_audio_lock_flag,
     *         1b system_video_lock_flag,
     *         1b marker,
     *         5b video_bound
     */
    si -> system_audio_lock_flag =  (BOOL)((((long)*buf_ptr) & 0x80) >> 7);
    si -> system_video_lock_flag =  (BOOL)((((long)*buf_ptr) & 0x40) >> 6);
    si -> video_bound            = (ULONG) (((long)*buf_ptr) & 0x1F);

    /* Marker bit check */
    if( (*buf_ptr & (UBYTE)0x20) != (UBYTE)0x20 )
    {
      syntax_printf( cb, msid, "Expecting marker_bit value '1' after system_video_lock_flag.\n" );
      exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
    }

    buf_ptr++;

    if( *buf_ptr != (UBYTE)0xFF )
    {
      syntax_printf( cb, msid, "Reserved byte not set to default\n" );
      exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
    }

    buf_ptr++;

    if( (si -> header_length) == 6 )
    {
      return;
    }

    j = 0;

    while( (ULONG)buf_ptr < (ULONG)(msid -> Demux . raw_data_buf_ptr) )
    {
      /* All lmntry stream_id's ... */
      test = (*buf_ptr & (UBYTE)0x80);

      if( test == 0 )
      {
        error_printf( cb, msid, "system header length incorrect\n" );
        exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
      }

      si -> STD_buffer_info[ j ] . stream_id = (*buf_ptr & (UBYTE)0xFF);

      buf_ptr++;

      /* Check marker_bits */
      if( (*buf_ptr & (UBYTE)0xC0) != (UBYTE)0xC0 )
      {
        syntax_printf( cb, msid, "Expecting placeholder bit values '11' after stream_id.\n" );
        exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
      }

      si -> STD_buffer_info[ j ] . STD_buffer_bound_scale = (*buf_ptr & (UBYTE)0x20) >> 5;
      si -> STD_buffer_info[ j ] . STD_buffer_size_bound  = (*buf_ptr & (UBYTE)0x1F) << 8;

      buf_ptr++;

      si -> STD_buffer_info[ j ] . STD_buffer_size_bound  |= *buf_ptr & (UBYTE)0xFF;

      buf_ptr++;

      j++;
    }
}


static
void read_packet_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, PACKET_header *pi )
{
    UBYTE *buf_ptr;
    UBYTE  test,
           test1;
    long   i;

    pi -> PTS = 0;
    pi -> DTS = 0;

    /* read packet length */
    buf_ptr = get_buf_data( cb, msid, 2 );

    /* Bytes 0 & 1: 8 bits packet_length -- high & low */
    pi -> packetlength  = (ULONG)(((long)*buf_ptr) << 8);
    buf_ptr++;
    pi -> packetlength |= (ULONG)(((long)*buf_ptr) & 0xFF);

    /* Get the remainder of the packet. */
    buf_ptr = get_buf_data( cb, msid, (pi -> packetlength) );

    /* private stream 2 */
    if( (pi -> stream_id) <= (UBYTE)0xBF )
    {
      pi -> packet_ptr = buf_ptr;
      return;
    }

    /* Skip over any stuffing Bytes 1111 1111 (16 max)
     * Count to 17 to see if there are more than allowed in the spec.
     */

    i = 0;

    while( (*buf_ptr == (UBYTE)0xFF) && (i < 17) )
    {
      buf_ptr++;
      pi -> packetlength--;
      i++;
    }

    if( i == 17 )
    {
      error_printf( cb, msid, "Too many stuffing bytes after packet_length\n" );
      exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
    }

    /* If nextbits =='01' read STD_buffer_scale & _size
     * Byte: 2b '01',
     *       1b STD_buffer_scale,
     *       5b STD_buffer_size-hi
     * Byte: 8 bits STD_buffer_size--lo
     */
    test1 = *buf_ptr & (UBYTE)0xC0;

    if( test1 == (UBYTE)0x40 )
    {
     /* read STD values */
      pi -> STD_buffer_scale = (*buf_ptr & (UBYTE)0x20) >> 5;
      pi -> STD_buffer_size  = (*buf_ptr & (UBYTE)0x1F) << 8;
      buf_ptr++;

      pi -> packetlength--;
      pi -> STD_buffer_size |= (*buf_ptr & (UBYTE)0xFF);
      buf_ptr++;

      pi -> packetlength--;
    }

    /* Determine whether to read in PTS, PTS & DTS, or neither before data. */
    test = (*buf_ptr & (UBYTE)0xF0);

    switch( test )
    {
      case (UBYTE)0x20:   /* Get PTS only */
      {
          buf_ptr = calc_time_stamp( cb, msid, buf_ptr, (&(pi -> PTS)) );
          pi -> packetlength -= 5;
      }
          break;

      case (UBYTE)0x30:   /* Get PTS & DTS. Both same format as PTS above.
                           * pts and data
                           */
      {
          buf_ptr = calc_time_stamp( cb, msid, buf_ptr, (&(pi -> PTS)) );

          /* Get DTS */
          if( (*buf_ptr & (UBYTE)0xF0) != (UBYTE)0x10 )
          {
            syntax_printf( cb, msid, "Expecting bits '0001' before DTS in read_packet_header\n" );
            exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
          }

          buf_ptr = calc_time_stamp( cb, msid, buf_ptr, (&(pi -> DTS)) );
          pi -> packetlength -= 10;
      }
          break;

      case (UBYTE)0x00:
      {
          if( *buf_ptr != (UBYTE)0x0F )
          {
            error_printf( cb, msid, "not a valid packet time sequence\n" );
            exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
          }

          buf_ptr++;
          pi -> packetlength--;
      }
          break;

      default:
      {
          error_printf( cb, msid, "invalid time code in packet\n" );
          exitdemux( cb, msid, RETURN_FAIL, DTERROR_INVALID_DATA );
      }
          break;
    }

    /* packetlength should have been properly set by the above */

    pi -> packet_ptr = buf_ptr;
}


static
void print_pack_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, PACK_header *pki )
{
    long mux_byte_rate = (pki -> mux_rate) * 50;

    verbose_printf( cb, msid, "PACK HEADER: SCR = %ld, Mux Rate = %ld, or %ld Bytes per second\n",
                    (LONG)(pki -> SCR), (LONG)(pki -> mux_rate), mux_byte_rate );
}


static
void print_system_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, SYSTEM_header *si )
{
    long  i            = 0;
    long  stream_num   = 0;
    long  buf_byte_size_bound;
    UBYTE stream_type  = 0;

    verbose_printf( cb, msid, "SYSTEM HEADER: Hdr Len = %ld, Rate bound = %ld, Audio bound = %ld,\n",
                    (LONG)(si -> header_length),
                    (LONG)(si -> rate_bound),
                    (LONG)(si -> audio_bound) );

    verbose_printf( cb, msid, "Fixed flag = %ld, CSPS flag = %ld, Sys audio lock flag = %ld,\n",
             (LONG)(si -> fixed_flag),
             (LONG)(si -> CSPS_flag),
             (LONG)(si -> system_audio_lock_flag) );

    verbose_printf( cb, msid, "Sys video lock flag = %ld, Video bound = %ld\n",
            (LONG)(si -> system_video_lock_flag), (LONG)(si -> video_bound) );

    while( si -> STD_buffer_info[ i ] . stream_id != 0 )
    {
      if( ((si -> STD_buffer_info[ i ] . stream_id >> 5) & 0x6) == 0x6 )
      {
        stream_type = 'A';
        stream_num  = (long)((si -> STD_buffer_info[ i ] . stream_id) & 0x1F);
      }

      if( ((si -> STD_buffer_info[ i ] . stream_id >> 4) & 0xE) == 0xE )
      {
        stream_type = 'V';
        stream_num  = (long)((si -> STD_buffer_info[ i]  . stream_id) & 0x0F);
      }

      if( si -> STD_buffer_info[ i ] . STD_buffer_bound_scale == 0 )
      {
        buf_byte_size_bound = si -> STD_buffer_info[ i ] . STD_buffer_size_bound * 128;
      }
      else
      {
        buf_byte_size_bound= si -> STD_buffer_info[ i ] . STD_buffer_size_bound * 1024;
      }

      verbose_printf( cb, msid, "STD buf %ld: Stream %lc%ld, Buf bound scale %ld, Buf size bound %ld or %ld bytes\n",
              i, stream_type, stream_num,
              (si -> STD_buffer_info[ i ] . STD_buffer_bound_scale),
              (si -> STD_buffer_info[ i ] . STD_buffer_size_bound),
              buf_byte_size_bound );
      i++;
    }
}


static
void print_packet_header( struct ClassBase *cb, struct MPEGSystemInstData *msid, PACKET_header *pi )
{
    long   stream_num  = 0;
    UBYTE  stream_type = 0;

    if( ((pi -> stream_id >> 5) & 0x6) == 0x6 )
    {
      stream_type = 'A';
      stream_num = (long)((pi -> stream_id) & 0x1F);
    }

    if( ((pi -> stream_id >> 4) & 0xE) == 0xE )
    {
      stream_type = 'V';
      stream_num = (long)((pi -> stream_id) & 0x0F);
    }

    verbose_printf( cb, msid, "PACKET HDR %lc%ld: %ldB, STD buf scale %ld, ..size %ld, PTS %ld, DTS %ld\n",
            stream_type,
            stream_num,
            (pi -> packetlength),
            (pi -> STD_buffer_scale),
            (pi -> STD_buffer_size),
            (pi -> PTS),
            (pi -> DTS) );
}


static
void demux( struct ClassBase *cb, struct MPEGSystemInstData *msid, BPTR file )
{
    PACK_header    pack_info;
    PACK_header   *pack_info_p = &pack_info;
    SYSTEM_header  sys_info;
    SYSTEM_header *sys_info_p  = &sys_info;
    PACKET_header  pkt_info;
    PACKET_header *pkt_info_p  = &pkt_info;

    long           i,
                   j;
    long           current_audio = 0;
    long           current_video = 0;
    long           demux_id[ MAX_MPEG_STREAMS ];
    UBYTE          next_code;

    /* Alloc input buffer */
    if( msid -> Demux . raw_data_buf = (UBYTE *)AllocVec( MAX_DATA_BUF_LEN, MEMF_PUBLIC ) )
    {
      /* Set "point of return" on decoder exit/error */
      if( setjmp( (msid -> Demux . exit_buf) ) == 0 )
      {
        /* Init remaining fields */
        msid -> Demux . raw_data_buf_len = 0;
        msid -> Demux . raw_data_buf_ptr = msid -> Demux . raw_data_buf;

        msid -> Demux . fdin     = file;
        msid -> Demux . fdin_pos = 0L;

        /* Read and write data. */
        for( ;; )
        {
          next_code = get_next_start_code( cb, msid );

          verbose_printf( cb, msid, "next_code = %lx\n", (LONG)next_code );

          /* next_code identifies whether the stream is at
           *  the beginning of a system stream, a pack, or a packet,
           *  or if it is at the end of a system stream.
           */
          switch( next_code )
          {
            case ((UBYTE)PACK_START_CODE):      /* 0xBA */
            {
                read_pack_header( cb, msid, pack_info_p );
                print_pack_header( cb, msid, pack_info_p );
            }
                break;

            case ((UBYTE)SYSTEM_START_CODE):      /* 0xBB */
            {
                read_system_header( cb, msid, sys_info_p );
                print_system_header( cb, msid, sys_info_p );

                msid -> Demux . N = sys_info_p -> audio_bound;
                msid -> Demux . M = sys_info_p -> video_bound;

                verbose_printf( cb, msid, " %ld audio, %ld video\n", (msid -> Demux . N), (msid -> Demux . M) );

                current_audio = 0;
                current_video = msid -> Demux . N;

                for( i = 0 ; i < MAX_MPEG_STREAMS ; i++ )
                {
                  demux_id[ i ] = -1;
                }
            }
                break;

            case ((UBYTE)ISO_11172_END_CODE):      /* 0xB9 */
            {
                verbose_printf( cb, msid, "end of system stream reached\n" );

                exitdemux( cb, msid, RETURN_OK, 0L );
            }
                break;

            default:   /* PACKET START CODE & stream_id */
            {
                pkt_info_p -> stream_id = next_code;
                read_packet_header( cb, msid, pkt_info_p );
                print_packet_header( cb, msid, pkt_info_p );

                /* The stream_id identifies the stream by type and number */
                switch( pkt_info_p -> stream_id )
                {
                  case( (UBYTE)0xB8 ): /* may need more work */
                  {
                      /* Following STD_buffer_scale and _size refer to
                       * all audio streams in the muxed stream
                       */
                      error_printf( cb, msid, "Error handling common audio STD_buffer_scale and _size.\n" );
                      exitdemux( cb, msid, RETURN_FAIL, DTERROR_UNKNOWN_COMPRESSION );
                  }
                      break;

                  case( (UBYTE)0xB9 ): /* may need more work */
                  {
                      /* Following STD_buffer_scale and _size refer to
                       * all video streams in the muxed stream
                       */
                      error_printf( cb, msid, "Error handling common video STD_buffer_scale and _size.\n" );
                      exitdemux( cb, msid, RETURN_FAIL, DTERROR_UNKNOWN_COMPRESSION );
                  }
                      break;

                  case( (UBYTE)0xBC ):
                  {
                      /* Reserved stream: discard */
                      error_printf( cb, msid, "Received reserved stream\n" );
                      exitdemux( cb, msid, RETURN_FAIL, DTERROR_UNKNOWN_COMPRESSION );
                  }
                      break;

                  case( (UBYTE)0xBD ):
                  {
                      /* private_stream_1: discard */
                      error_printf( cb, msid, "Received private_stream_1\n" );
                      exitdemux( cb, msid, RETURN_FAIL, DTERROR_UNKNOWN_COMPRESSION );
                  }
                      break;

                  case( (UBYTE)0xBE ):  /* data bytes all FF */
                  {
                      /* Padding stream: discard */
                      verbose_printf( cb, msid, "Received padding stream\n" );
                  }
                      break;

                  case( (UBYTE)0xBF ):
                  {
                      /* private_stream_2: discard */
                      error_printf( cb, msid, "Received private_stream_2\n" );

                      exitdemux( cb, msid, RETURN_FAIL, DTERROR_UNKNOWN_COMPRESSION );
                  }
                      break;

                  default:
                  {
                      /* The stream is an audio, video, or reserved_data stream.
                       * Convert stream_id to C0 to 0, EF to 48
                       */
                      i = (((long)pkt_info_p -> stream_id) & 0xFF) - 192;

                      if( i < 0 )
                      {
                        /* Invalid stream: do nothing */
                      }
                      else
                      {
                        /* Audio or Video stream. */
                        if( i < 48 )
                        {
                          BOOL                isaudio = (i < 32);
                          struct PacketBlock *pb;

                          if( demux_id[ i ] == -1 )
                          {
                            /* A new stream */
                            if( isaudio )
                            {
                              /* Audio */
                              demux_id[ i ] = current_audio;
                              current_audio++;
                            }
                            else
                            {
                              demux_id[ i ] = current_video;
                              current_video++;
                            }
                          }

                          j = demux_id[ i ];

                          /* No file ? - Then create it ! */
                          if( (msid -> Demux . fd_demuxa[ j ] . sl_handle) == NULL )
                          {
                            NewList( (struct List *)(&(msid -> Demux . fd_demuxa[ j ] . sl_packetblocklist)) );

                            msid -> Demux . fd_demuxa[ j ] . sl_handle = msid -> Demux . fdin;
                          }

                          /* block pos */
                          verbose_printf( cb, msid, "write: off %ld len %ld\n", (msid -> Demux . curr_file_pos +  /* file pos */
                                          (long)((pkt_info_p -> packet_ptr) - msid -> Demux . curr_file_pos_in_buffer)),
                                          (pkt_info_p -> packetlength) );

                          /* Create, fill and queue a PacketBlock node */
                          pb = (struct PacketBlock *)AllocPooled( (msid -> msid_Pool), (ULONG)sizeof( struct PacketBlock ) );
                          if( pb == NULL ) exitdemux( cb, msid, RETURN_FAIL, ERROR_NO_FREE_STORE );

                          pb -> system_offset = (msid -> Demux . curr_file_pos + (long)((pkt_info_p -> packet_ptr) - msid -> Demux . curr_file_pos_in_buffer)); /* file pos */
                          pb -> length        = (pkt_info_p -> packetlength);

                          /* Inc stream size */
                          msid -> Demux . fd_demuxa[ j ] . sl_size += pb -> length;

                          AddTail( (struct List *)(&(msid -> Demux . fd_demuxa[ j ] . sl_packetblocklist)), (struct Node *)(&(pb -> node)) );
                        }
                        else
                        {
                          if( i >= 48 )
                          {
                            /* It's a reserved data stream.
                             * Additional functionality will be implementation dependent
                             */
                            verbose_printf( cb, msid, "Reserved data stream %ld is being ignored.\n", i );
                            break;
                          }
                          else
                          {
                            /* Should never be reached. */
                            error_printf( cb, msid, "Unrecognized stream ID: %ld %lx %lc\n",
                                          (pkt_info_p -> stream_id),
                                          (pkt_info_p -> stream_id),
                                          (long)(pkt_info_p -> stream_id) );

                            exitdemux( cb, msid, RETURN_FAIL, DTERROR_UNKNOWN_COMPRESSION );
                          }
                        }
                      }
                  }
                      break;
              }
            }
          }
        }
      }

      /* Success ? */
      if( ((msid -> Demux . retval) <= RETURN_WARN) || (msid -> msid_IgnoreErrors) )
      {
        RunHandler( cb, msid );
      }

      FreeVec( (msid -> Demux . raw_data_buf) );
    }
}


static
void exitdemux( struct ClassBase *cb, struct MPEGSystemInstData *msid, LONG result, LONG result2 )
{
    msid -> Demux . retval  = result;
    msid -> Demux . retval2 = result2;

    longjmp( (msid -> Demux . exit_buf), 1 );
}


/*****************************************************************************/

static
BPTR GetStreamLock( struct ClassBase *cb, struct MPEGSystemInstData *msid, ULONG num, BOOL isaudio )
{
      if( msid -> Handler . Process )
      {
        BPTR lock;

        num += (isaudio)?(0UL):(msid -> Demux . N); /* Add video ID-offset if neccesary */

        lock = (BPTR)DoPkt( (&(msid -> Handler . Process -> pr_MsgPort)), ACTION_COPY_DIR, MKBADDR( (num * 16) ), 0L, 0L, 0L, 0L );

        return( lock );
      }

      return( NULL );
}


static
void RunHandler( struct ClassBase *cb, struct MPEGSystemInstData *msid )
{
    if( msid -> Handler . Process = CreateNewProcTags( NP_Entry,        (ULONG)Handler,
                                                       NP_Name,         (ULONG)"mpegsystem.datatype (MPEG System Demux Handler)",
                                                       NP_Priority,     15L,
                                                       NP_StackSize,    16384UL,
                                                       TAG_DONE ) )
    {
      /* Initialize the message */
      msid -> Handler . Startup . sm_Message . mn_Node . ln_Type = NT_MESSAGE;
      msid -> Handler . Startup . sm_Message . mn_Length         = sizeof( struct StartupMsg );
      msid -> Handler . Startup . sm_CB                          = cb;
      msid -> Handler . Startup . sm_msid                        = msid;

      /* Send the information to the process */
      PutMsg( (&(msid -> Handler . Process -> pr_MsgPort)), (&(msid -> Handler . Startup . sm_Message)) );
    }
}


/* Kill our virtual demux filesystem and wait until it is really dead...
 * (Should be replaced by a version which uses exec.library/Wait)
 */
static
void KillHandler( struct ClassBase *cb, struct MPEGSystemInstData *msid )
{
    while( (msid -> Handler . Process) )
    {
      Forbid();

        if( msid -> Handler . Process )
        {
          Signal( (&(msid -> Handler . Process -> pr_Task)), SIGBREAKF_CTRL_C );
        }

      Permit();

      /* Wait a 1/25 sec before trying it again... */
      Delay( (TICKS_PER_SECOND / 25UL) );
    }
}


/* virtual demux filesystem handler entry */
DISPATCHERFLAGS
static
void Handler( void )
{
#ifdef SysBase
#undef SysBase
#endif
    struct ExecBase           *SysBase = (*((struct ExecBase **)4UL));
    struct Process            *pr;
    struct StartupMsg         *sm;
    struct ClassBase          *cb;
    struct MPEGSystemInstData *msid;
    ULONG                      sigflags;
    struct Message            *msg;
    BOOL                       done = FALSE;

    /* Find out who we are */
    pr = (struct Process *)FindTask( NULL );

    /* Get the startup message */
    WaitPort( (&(pr -> pr_MsgPort)) );
    sm = (struct StartupMsg *)GetMsg( (&(pr -> pr_MsgPort)) );

    /* Pull all the information required from the startup message */
    cb   = sm -> sm_CB;
    msid = sm -> sm_msid;

    /* BUG: The parent process does currently not handle a failure if this process... */
    while( (msid -> Handler . mp = CreateMsgPort()) == NULL )
    {
    }

    /* Init DOS_STDPKT queue which is used within the handler to avoid a AllocDOSObject
     * and fill it up with some packets...
     */
    {
      ULONG i;

      NewList( (struct List *)(&(msid -> Handler . pktpool)) );

      for( i = 0UL ; i < 16UL ; i++ )
      {
        struct DosPacket *dp;

        if( dp = (struct DosPacket *)AllocDosObject( DOS_STDPKT, NULL ) )
        {
          AddTail( (struct List *)(&(msid -> Handler . pktpool)), (&(dp -> dp_Link -> mn_Node)) );
        }
        else
        {
          break;
        }
      }
    }

    do
    {
      sigflags = Wait( (1UL << (pr -> pr_MsgPort . mp_SigBit)) | SIGBREAKF_CTRL_C );

      if( sigflags & SIGBREAKF_CTRL_C )
      {
        done = TRUE;
      }

      while( msg = GetMsg( (&(pr -> pr_MsgPort)) ) )
      {
        dispatch_packet( cb, msid, (struct DosPacket *)(msg -> mn_Node . ln_Name) );
      }
    } while( (!done) || (msid -> Handler . OpenCount) );

    /* Free DOS_STDPKT queue */
    {
      struct Node *node;

      while( node = RemHead( (struct List *)(&(msid -> Handler . pktpool)) ) )
      {
        struct DosPacket *dp = (struct DosPacket *)(node -> ln_Name);

        FreeDosObject( DOS_STDPKT, (APTR)dp );
      }
    }

    DeleteMsgPort( (msid -> Handler . mp) );

    Forbid();

      msid -> Handler . Process = NULL;
#define SysBase (cb -> cb_SysBase)
}


static
void dispatch_packet( struct ClassBase *cb, struct MPEGSystemInstData *msid, struct DosPacket *dp )
{
    struct MsgPort      *rport;
    struct StreamHandle *sh;

    switch( dp -> dp_Type )
    {
      case ACTION_FINDINPUT:
      case ACTION_FH_FROM_LOCK:
      {
          struct FileHandle  *fh             = (struct FileHandle  *)BADDR( (dp -> dp_Arg1) );
          struct FileLock    *fl             = (struct FileLock    *)BADDR( (dp -> dp_Arg2) );
          LONG                stream_index   = fl -> fl_Key;

          if( sh = (struct StreamHandle *)AllocVec( sizeof( struct StreamHandle ), MEMF_PUBLIC ) )
          {
            /* init StreamHandle */
            sh -> sl      = (&(msid -> Demux . fd_demuxa[ stream_index ]));
            sh -> currpos = 0L;

            /* Init FileHandle */
            fh -> fh_Port     = (struct MsgPort *)DOSFALSE;                   /* non-interactive handler    */
            fh -> fh_Arg1     = (LONG)sh;                                     /* set to struct StreamHandle */
            fh -> fh_Type     = (&(msid -> Handler . Process -> pr_MsgPort));

            /* Handler in-use */
            msid -> Handler . OpenCount++;

            if( (dp -> dp_Type) == ACTION_FH_FROM_LOCK )
            {
              /* Here we can "eat" the given filelock... */
              FreeLock( cb, msid, fl );
            }

            dp -> dp_Res1 = DOSTRUE;  /* success */
            dp -> dp_Res2 = 0L;       /* No error */
          }
          else
          {
            dp -> dp_Res1 = DOSFALSE; /* failure */
            dp -> dp_Res2 = IoErr();  /* Cause ? */
          }
      }
          break;

      case ACTION_END:
      {
          sh = (struct StreamHandle *)(dp -> dp_Arg1);

          if( sh )
          {
            FreeVec( sh );
          }

          msid -> Handler . OpenCount--;

          dp -> dp_Res1 = DOSTRUE;
          dp -> dp_Res2 = 0L;
      }
          break;

      case ACTION_READ:
      {
          LONG                stream_offset  = 0L;
          LONG                read_size      = (dp -> dp_Arg3);
          UBYTE              *dest           = (UBYTE *)(dp -> dp_Arg2);
          LONG                error          = 0L;
          LONG                curr_read_size = 0L; /* bytes already in buffer */
          LONG                numpkt         = 0L; /* pending packets */
          struct FileHandle  *fh;
          struct PacketBlock *worknode,
                             *nextnode;

          sh = (struct StreamHandle *)(dp -> dp_Arg1);
/* We use "fdin" instead of "sl_handle" here because our whole bunch of virtual streams are mapped to
 * one real system stream. "sl_handle" would only be used if we have one real stream handle for each
 * of our virtual handles. The same refers to the actual file position of the real stream handle
 * (see "classdata.h", "sl_handle_pos".
 */
#ifdef COMMENTED_OUT
          fh = BADDR( (sh -> sl -> sl_handle) );
#else
          fh = BADDR( (msid -> Demux . fdin) );
#endif /* COMMENTED_OUT */

          worknode = (struct PacketBlock *)(sh -> sl -> sl_packetblocklist . mlh_Head);

          while( (nextnode = (struct PacketBlock *)(worknode -> node . mln_Succ)) && (read_size > 0UL) )
          {
            /* Find start block */
            if( (stream_offset <= (sh -> currpos)) &&
                ((stream_offset + (worknode -> length)) > (sh -> currpos)) )
            {
              LONG cut          = ((sh -> currpos) - stream_offset),
                   curr_readlen = MIN( read_size, ((worknode -> length) - cut) );
              LONG abs_seek_pos; /* seek position, relative from the beginning of the file */

#if 0
              if( Seek( (sh -> sl -> sl_handle), ((worknode -> system_offset) + cut), OFFSET_BEGINNING ) == -1L )
              {
                break;
              }
#else
              abs_seek_pos = ((worknode -> system_offset) + cut);

              if( SendSeek( cb, msid, fh, (abs_seek_pos - (LONG)(msid -> Demux . fdin_pos)), OFFSET_CURRENT ) )
              {
                numpkt++;

                msid -> Demux . fdin_pos = abs_seek_pos;
              }
              else
              {
                error = IoErr();
                break;
              }
#endif

              /* We only need to check for a "complete fill" or failure, because the previous scan
               * (in the demuxer part) gurantees that the buffer (packet) has at least this ("curr_readlen") size
               */
#if 0
              if( Read( (sh -> sl -> sl_handle), dest, curr_readlen ) != curr_readlen )
              {
                error = IoErr();
                break;
              }
#else
              if( SendRead( cb, msid, fh, dest, curr_readlen ) )
              {
                numpkt++;

                msid -> Demux . fdin_pos += curr_readlen;
              }
              else
              {
                error = IoErr();
                break;
              }
#endif

              dest           += curr_readlen;
              read_size      -= curr_readlen;
              curr_read_size += curr_readlen;
              sh -> currpos  += curr_readlen;

              /* Should never ever happen, but... */
              if( read_size < 0L )
              {
                error = ERROR_BUFFER_OVERFLOW;
                break;
              }
            }

            stream_offset += worknode -> length;

            worknode = nextnode;
          }

#if 1
          /* Wait for outstanding msgs and fetch success/errorc codes from them */
          while( numpkt > 0L )
          {
            struct Message *msg;

            WaitPort( (msid -> Handler . mp) );

            while( msg = GetMsg( (msid -> Handler . mp) ) )
            {
              struct DosPacket *rdp = (struct DosPacket *)(msg -> mn_Node . ln_Name);

              /* This assumes that we only get ACTION_SEEK/ACTION_READ here back... */
              if( (rdp -> dp_Res1) == -1L )
              {
                error = rdp -> dp_Res2;
              }

              ReleaseDOSPacket( cb, msid, rdp );
              numpkt--;
            }
          }
#endif

          /* Resync on error. Required because we don't know whether the ACTION_READ (see above)
           * was executed or not.
           */
          if( error )
          {
            msid -> Demux . fdin_pos = Seek( (msid -> Demux . fdin), 0L, OFFSET_CURRENT );
          }

          dp -> dp_Res1 = ((error)?(-1L):(curr_read_size)); /* bytes read */
          dp -> dp_Res2 = error;                            /* result2    */
      }
          break;

      case ACTION_SEEK:
      {
          LONG newpos = 0L;

          sh = (struct StreamHandle *)(dp -> dp_Arg1);

          switch( dp -> dp_Arg3 )
          {
            case OFFSET_BEGINNING:
            {
                newpos = dp -> dp_Arg2;
            }
                break;

            case OFFSET_CURRENT:
            {
                newpos += (sh -> currpos) + (dp -> dp_Arg2);
            }
                break;

            case OFFSET_END:
            {
                newpos += (sh -> sl -> sl_size) + (dp -> dp_Arg2);
            }
                break;

            default: /* should return ERROR_SEEK_ERROR */
            {
                newpos = -1L; /* forces ERROR_SEEK_ERROR by bounds check below */
            }
                break;
          }

          /* Check bounds */
          if( (newpos > (sh -> sl -> sl_size)) || (newpos < 0L) )
          {
            dp -> dp_Res1  = -1L;
            dp -> dp_Res2  = ERROR_SEEK_ERROR;
          }
          else
          {
            dp -> dp_Res1  = sh -> currpos; /* Return abolsute position BEFORE the seek took place */
            dp -> dp_Res2  = 0L;

            sh -> currpos = newpos;
          }
      }
          break;

      case ACTION_COPY_DIR_FH:
      {
          struct FileLock     *dest;
          struct StreamList   *sl  = ((struct StreamHandle *)(dp -> dp_Arg1)) -> sl;

          if( dest = (struct FileLock *)AllocVec( sizeof( struct FileLock ), MEMF_PUBLIC ) )
          {
            LONG stream_index = (sl - (&(msid -> Demux . fd_demuxa[ 0 ]))); /* Return array index of "sl" */

            dest -> fl_Link    = NULL;
            dest -> fl_Key     = stream_index;
            dest -> fl_Access  = ACCESS_READ;
            dest -> fl_Task    = (&(msid -> Handler . Process -> pr_MsgPort));
            dest -> fl_Volume  = NULL;

            msid -> Handler . OpenCount++;
          }

          dp -> dp_Res1 = MKBADDR( dest );
          dp -> dp_Res2 = (dp -> dp_Res1)?(0L):(IoErr());
      }
          break;

      case ACTION_COPY_DIR:
      {
          struct FileLock *src  = (struct FileLock *)BADDR( (dp -> dp_Arg1) ),
                          *dest;

          if( dest = (struct FileLock *)AllocVec( sizeof( struct FileLock ), MEMF_PUBLIC ) )
          {
            if( (LONG)src > 256L )
            {
              *dest = *src;

              msid -> Handler . OpenCount++;
            }
            else
            {
              LONG key = ((LONG)src / 16); /* src is now the index... */

              /* Does we have a system stream to work on ? */
              if( msid -> Demux . fd_demuxa[ key ] . sl_handle )
              {
                dest -> fl_Link    = NULL;
                dest -> fl_Key     = key;
                dest -> fl_Access  = ACCESS_READ;
                dest -> fl_Task    = (&(msid -> Handler . Process -> pr_MsgPort));
                dest -> fl_Volume  = NULL;

                msid -> Handler . OpenCount++;
              }
              else
              {
                FreeVec( dest );
                dest = NULL;

                SetIoErr( ERROR_OBJECT_NOT_FOUND );
              }
            }
          }

          dp -> dp_Res1 = MKBADDR( dest );
          dp -> dp_Res2 = (dp -> dp_Res1)?(0L):(IoErr());
      }
          break;

      case ACTION_FREE_LOCK:
      {
          struct FileLock *fl = (struct FileLock *)BADDR( (dp -> dp_Arg1) );

          FreeLock( cb, msid, fl );

          dp -> dp_Res1 = DOSTRUE;
          dp -> dp_Res2 = 0L;
      }
          break;

      case ACTION_IS_FILESYSTEM:
      {
          dp -> dp_Res1 = DOSTRUE;  /* We are a filesystem */
          dp -> dp_Res2 = 0L;       /* Avoid Lock( ":", SHARED_LOCK ); overhead,
                                     * see Ralf Babel's "The Amiga GURU Book",
                                     * section 21.2.3.17, page 621
                                     */
      }
          break;

      case ACTION_EXAMINE_OBJECT:
      {
          struct FileLock      *fl             = (struct FileLock      *)BADDR( (dp -> dp_Arg1) );
          struct FileInfoBlock *fib            = (struct FileInfoBlock *)BADDR( (dp -> dp_Arg2) );
          LONG                  stream_index   = fl -> fl_Key;
          struct StreamList    *sl             = (&(msid -> Demux . fd_demuxa[ stream_index ]));

          dp -> dp_Res1 = ExamineFH( (sl -> sl_handle), fib );

          if( dp -> dp_Res1 )
          {
            TEXT buffer[ 256 ];

            mysprintf( cb, buffer, "%s@%ld", (fib -> fib_FileName), stream_index );
            stccpy( (fib -> fib_FileName), buffer, sizeof( (fib -> fib_FileName) ) );

            /* Set our stream size */
            fib -> fib_Size = sl -> sl_size;

            /* Bump number of blocks */
            fib -> fib_NumBlocks = INTDIVR( (sl -> sl_size), 1024UL ); /* BUG: Should be packet size to allow faster reading
                                                                        * in conjunction with async I/O
                                                                        */

            /* Convert to BCPL strings */
            STRPTR2BSTR( (fib -> fib_FileName) );
            STRPTR2BSTR( (fib -> fib_Comment)  );

            dp -> dp_Res2 = 0L;
          }
          else
          {
            dp -> dp_Res2 = IoErr();
          }
      }
          break;

      case ACTION_PARENT:
      {
          struct FileLock      *fl             = (struct FileLock *)BADDR( (dp -> dp_Arg1) );
          LONG                  stream_index   = fl -> fl_Key;
          struct StreamList    *sl             = (&(msid -> Demux . fd_demuxa[ stream_index ]));

          dp -> dp_Res1 = ParentOfFH( (sl -> sl_handle) ); /* Get parent of system stream */
          dp -> dp_Res2 = IoErr();
      }
          break;

      case ACTION_INFO: /* Required for async I/O */
      default:
      {
          dp -> dp_Res1 = DOSFALSE;
          dp -> dp_Res2 = ERROR_ACTION_NOT_KNOWN;
      }
          break;
    }

    rport = dp -> dp_Port;
    dp -> dp_Port = (&(msid -> Handler . Process -> pr_MsgPort));

    PutMsg( rport, (dp -> dp_Link) );
}


/* Convert a C string into a BCPL string */
static
void STRPTR2BSTR( STRPTR s )
{
    size_t len = strlen( s );

    memmove( (&s[ 1 ]), s, len );
    s[ 0 ] = len; /* WARNING: Assumes that the string is less than < 255 bytes ! */
}


static
void FreeLock( struct ClassBase *cb, struct MPEGSystemInstData *msid, struct FileLock *fl )
{
    if( fl )
    {
      fl -> fl_Task = NULL; /* Any attempt to use the FileLock again should be DEADLY !! */

      msid -> Handler . OpenCount--;

      FreeVec( fl );
    }
}


/* Fetch a DosPacket from our private pool */
static
struct DosPacket *ObtainDOSPacket( struct ClassBase *cb, struct MPEGSystemInstData *msid )
{
    struct DosPacket *dp;
    struct Node      *node;

    if( node = RemHead( (struct List *)(&(msid -> Handler . pktpool)) ) )
    {
      dp = (struct DosPacket *)(node -> ln_Name);
    }
    else
    {
      dp = (struct DosPacket *)AllocDosObject( DOS_STDPKT, NULL );
    }

    return( dp );
}


/* Return a DosPacket to our private pool */
static
void ReleaseDOSPacket( struct ClassBase *cb, struct MPEGSystemInstData *msid, struct DosPacket *dp )
{
    AddTail( (struct List *)(&(msid -> Handler . pktpool)), (&(dp -> dp_Link -> mn_Node)) );
}


/* Send a seek async */
static
BOOL SendSeek( struct ClassBase *cb, struct MPEGSystemInstData *msid, struct FileHandle *fh, LONG pos, LONG mode )
{
    struct DosPacket *dp;

    if( dp = ObtainDOSPacket( cb, msid ) )
    {
      dp -> dp_Type = ACTION_SEEK;
      dp -> dp_Arg1 = fh -> fh_Arg1;
      dp -> dp_Arg2 = pos;
      dp -> dp_Arg3 = mode;

      dp -> dp_Port = dp -> dp_Link -> mn_ReplyPort = msid -> Handler . mp;
      PutMsg( (fh -> fh_Type), (dp -> dp_Link) );

      return( TRUE );
    }

    return( FALSE );
}


/* Send a read async */
static
BOOL SendRead( struct ClassBase *cb, struct MPEGSystemInstData *msid, struct FileHandle *fh, UBYTE *buffer, ULONG len )
{
    struct DosPacket *dp;

    if( dp = ObtainDOSPacket( cb, msid ) )
    {
      dp -> dp_Type = ACTION_READ;
      dp -> dp_Arg1 = fh -> fh_Arg1;
      dp -> dp_Arg2 = (LONG)buffer;
      dp -> dp_Arg3 = len;

      dp -> dp_Port = dp -> dp_Link -> mn_ReplyPort = msid -> Handler . mp;
      PutMsg( (fh -> fh_Type), (dp -> dp_Link) );

      return( TRUE );
    }

    return( FALSE );
}





