
/*
**
**  $VER: dispatch.c 1.3 (5.2.98)
**  mpegsystem.datatype 1.3
**
**  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 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 STRPTR   GetPrefsVar( struct ClassBase *, STRPTR );
                static BOOL     matchstr( struct ClassBase *, STRPTR, STRPTR );
                static void     ReadENVPrefs( struct ClassBase *, struct MPEGSystemInstData * );
                static LONG     LoadFrames( struct ClassBase *, Object * );
                static void     OpenLogfile( struct ClassBase *, struct MPEGSystemInstData * );
                static void     mysprintf( struct ClassBase *, STRPTR, STRPTR, ... );
                static void     verbose_printf( struct ClassBase *, struct MPEGSystemInstData *, STRPTR, ... );
                static void     error_printf( struct ClassBase *, struct MPEGSystemInstData *, STRPTR, ... );
                static void     syntax_printf( struct ClassBase *, struct MPEGSystemInstData *, STRPTR, ... );
                static void     debug_printf( struct ClassBase *, struct MPEGSystemInstData *, 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 ) )
            {
              LONG error;

              /* Load frames... */
              if( error = LoadFrames( cb, (Object *)retval ) )
              {
                /* Something went fatally wrong, dispose object */
                CoerceMethod( cl, (Object *)retval, OM_DISPOSE );
                retval = 0UL;
              }

              SetIoErr( error );
            }
          }
#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 )
          {
            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 not supported yet... */
          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 );
}


/****** mpegsystem.datatype/preferences *****************************************
*
*   NAME
*       preferences
*
*   DESCRIPTION
*       The "ENV:Classes/DataTypes/mpegsystem.prefs" file contains global
*       settings for the datatype.
*       The preferences file is an ASCII file containing one line where the
*       preferences can be set.
*       It can be superset by a local variable with the same name.
*
*       Each line can contain settings, special settings for some projects
*       can be set using the MATCHPROJECT option.
*       Lines beginning with a '#' or ';' chars are treated as comments.
*       Lines are limitted to 256 chars.
*
*   TEMPLATE
*       MATCHPROJECT/K,VERBOSE/S,VERBOSESYNTAX/S,NOVERBOSESYNTAX/S,
*       VERBOSEDEBUG=DEBUG/S,NOVERBOSEDEBUG=NODEBUG/S,IGNOREERRORS/S,
*       NOIGNOREERRORS/S
*
*       MATCHPROJECT -- The settings in this line belongs only to this
*           project(s), e.g. if the case-insensitive pattern does not match,
*           this line is ignored.
*           The maximum length of the pattern is 128 chars.
*           Defaults to #?, which matches any project.
*
*       VERBOSE -- Print information about the animation. Currently
*          the frame numbers and the used compression are printed, after all
*          number of scanned/loaded frames, set FPS rate, dimensions (width/
*          height/depth), sample information etc.
*
*       VERBOSESYNTAX -- Print information about syntax errors
*
*       NOVERBOSESYNTAX -- Turns the VERBOSESYNTAX option off.
*
*       DEBUG
*       VERBOSEDEBUG -- Turns debugging mode on to get internal debugging
*           info without recompiling the whole source.
*
*       NODEBUG
*       NOVERBOSEDEBUG -- Turns the DEBUG option off.
*
*       IGNOREERRORS -- Ignores errors if system stream parsing fails;
*           the video+audio parts attempts to load the data until the
*           last scanned packet occurs.
*
*       NOIGNOREERRORS -- Turns the IGNOREERRORS option off.
*
*   NOTE
*       - An invalid prefs file line will be ignored and forces the VERBOSE
*         output.
*
*   BUGS
*       - Low memory may cause that the prefs file won't be parsed.
*
*       - Lines are limitted to 256 chars
*
*       - An invalid prefs file line will be ignored.
*
*       - The sample path length is limitted to 200 chars. A larger
*         value may crash the machine if an error occurs.
*
******************************************************************************
*
*/


static
STRPTR GetPrefsVar( struct ClassBase *cb, STRPTR name )
{
          STRPTR buff;
    const ULONG  buffsize = 16UL;

    if( buff = (STRPTR)AllocVec( (buffsize + 2UL), (MEMF_PUBLIC | MEMF_CLEAR) ) )
    {
      if( GetVar( name, buff, buffsize, GVF_BINARY_VAR ) != (-1L) )
      {
        ULONG varsize = IoErr();

        varsize += 2UL;

        if( varsize > buffsize )
        {
          FreeVec( buff );

          if( buff = (STRPTR)AllocVec( (varsize + 2UL), (MEMF_PUBLIC | MEMF_CLEAR) ) )
          {
            if( GetVar( name, buff, varsize, GVF_BINARY_VAR ) != (-1L) )
            {
              return( buff );
            }
          }
        }
        else
        {
          return( buff );
        }
      }

      FreeVec( buff );
    }

    return( NULL );
}


static
BOOL matchstr( struct ClassBase *cb, STRPTR pat, STRPTR s )
{
    TEXT buff[ 512 ];

    if( pat && s )
    {
      if( ParsePatternNoCase( pat, buff, (sizeof( buff ) - 1) ) != (-1L) )
      {
        if( MatchPatternNoCase( buff, s ) )
        {
          return( TRUE );
        }
      }
    }

    return( FALSE );
}


static
void ReadENVPrefs( struct ClassBase *cb, struct MPEGSystemInstData *msid )
{
    struct RDArgs envvarrda =
    {
      NULL,
      256L,
      0L,
      0L,
      NULL,
      0L,
      NULL,
      RDAF_NOPROMPT
    };

    struct
    {
      STRPTR  matchproject;
      long   *verbose;
      long   *dosyntax;
      long   *nodosyntax;
      long   *dodebug;
      long   *nododebug;
      long   *ignoreerrors;
      long   *noignoreerrors;
    } mpegsystemargs;

    TEXT   varbuff[ 258 ];
    STRPTR var;

    if( var = GetPrefsVar( cb, "Classes/DataTypes/mpegsystem.prefs" ) )
    {
      STRPTR prefsline      = var,
             nextprefsline;
      ULONG  linecount      = 1UL;

      /* Be sure that "var" contains at least one break-char */
      strcat( var, "\n" );

      while( nextprefsline = strpbrk( prefsline, "\n" ) )
      {
        stccpy( varbuff, prefsline, (int)MIN( (sizeof( varbuff ) - 2UL), (((ULONG)(nextprefsline - prefsline)) + 1UL) ) );

        /* be sure that this line isn't a comment line or an empty line */
        if( (varbuff[ 0 ] != '#') && (varbuff[ 0 ] != ';') && (varbuff[ 0 ] != '\n') && (strlen( varbuff ) > 2UL) )
        {
          /* Prepare ReadArgs processing */
          strcat( varbuff, "\n" );                                       /* Add NEWLINE-char            */
          envvarrda . RDA_Source . CS_Buffer = varbuff;                  /* Buffer                      */
          envvarrda . RDA_Source . CS_Length = strlen( varbuff ) + 1UL;  /* Set up input buffer length  */
          envvarrda . RDA_Source . CS_CurChr = 0L;
          envvarrda . RDA_Buffer = NULL;
          envvarrda . RDA_BufSiz = 0L;
          memset( (void *)(&mpegsystemargs), 0, sizeof( mpegsystemargs ) );          /* Clear result array          */

          if( ReadArgs( "MATCHPROJECT/K,"
                        "VERBOSE/S,"
                        "VERBOSESYNTAX/S,"
                        "NOVERBOSESYNTAX/S,"
                        "VERBOSEDEBUG=DEBUG/S,"
                        "NOVERBOSEDEBUG=NODEBUG/S,"
                        "IGNOREERRORS/S,"
                        "NOIGNOREERRORS/S", (LONG *)(&mpegsystemargs), (&envvarrda) ) )
          {
            BOOL noignore = TRUE;

            if( (mpegsystemargs . matchproject) && (msid -> msid_ProjectName) )
            {
              noignore = matchstr( cb, (mpegsystemargs . matchproject), (msid -> msid_ProjectName) );
            }

            if( noignore )
            {
              if( mpegsystemargs . verbose )
              {
                OpenLogfile( cb, msid );
              }

              if( mpegsystemargs . dosyntax )
              {
                msid -> msid_DoSyntax = TRUE;
              }

              if( mpegsystemargs . nodosyntax )
              {
                msid -> msid_DoSyntax = FALSE;
              }

              if( mpegsystemargs . dodebug )
              {
                msid -> msid_DoDebug = TRUE;
              }

              if( mpegsystemargs . nododebug )
              {
                msid -> msid_DoDebug = FALSE;
              }

              if( mpegsystemargs . ignoreerrors )
              {
                msid -> msid_IgnoreErrors = TRUE;
              }

              if( mpegsystemargs . noignoreerrors )
              {
                msid -> msid_IgnoreErrors = FALSE;
              }
            }
            else
            {
              verbose_printf( cb, msid, "prefs line %lu ignored\n", linecount );
            }

            FreeArgs( (&envvarrda) );
          }
          else
          {
            LONG ioerr = IoErr();
            TEXT errbuff[ 256 ];

            Fault( ioerr, "Classes/DataTypes/mpegsystem.prefs", errbuff, (LONG)sizeof( errbuff ) );

            error_printf( cb, msid, "preferences \"%s\" line %lu\n", errbuff, linecount );
          }
        }

        prefsline = ++nextprefsline;
        linecount++;
      }

      FreeVec( var );
    }
}


static
LONG LoadFrames( struct ClassBase *cb, Object *o )
{
    struct MPEGSystemInstData *msid  = (struct MPEGSystemInstData *)INST_DATA( (cb -> cb_Lib . cl_Class), o );
    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 video 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);

#if 0
                              /* test test - this fixed some possible rounding errors in integer math */
                              {
                                ULONG xperiod,
                                      xspf;

                                xspf    = (msid -> msid_SampleLength) / video_numframes;
                                xperiod = ((SysBase -> ex_EClockFrequency) * 10UL) / (xspf * (TICK_FREQ / (msid -> msid_TicksPerFrame)) * 2UL);

                                if( ((xperiod * 100) / (msid -> msid_Period)) < 110 )
                                {
                                  msid -> msid_SamplesPerFrame = xspf;
                                  msid -> msid_Period          = xperiod;
                                }
                              }
#endif

                              SetDTAttrs( o, NULL, NULL, ADTA_Sample,       (msid -> msid_Sample),
                                                         ADTA_SampleLength, (msid -> msid_SamplesPerFrame),
                                                         ADTA_Period,       (msid -> msid_Period),
                                                         ADTA_Volume,       volume,
                                                         TAG_DONE );
                            }
                            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 )
            {
              /* 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;
    }

    return( error );
}


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


static
void OpenLogfile( struct ClassBase *cb, struct MPEGSystemInstData *msid )
{
    if( (msid -> msid_VerboseOutput) == NULL )
    {
      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 );
}


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

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


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

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


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

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


static
void verbose_printf( struct ClassBase *cb, struct MPEGSystemInstData *msid, STRPTR format, ... )
{
    if( msid -> msid_VerboseOutput )
    {
      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           = Seek( (msid -> Demux . fdin), 0L, OFFSET_CURRENT ); /* 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 == -1 )
      {
        LONG err = IoErr();

        error_printf( cb, msid, "Error: reading system file\n" );
        exitdemux( cb, msid, RETURN_FAIL, err );
      }

      /* 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)0xBA:
          return( (UBYTE)PACK_START_CODE );
          break;

      case (UBYTE)0xB9:
          return( (UBYTE)ISO_11172_END_CODE );
          break;

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

          return( (UBYTE)SYSTEM_START_CODE );
      }
          break;

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

    /* This statement should never be reached */
    return( (UBYTE)ISO_11172_END_CODE );
}


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;

        /* 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,        Handler,
                                                       NP_Name,         "MPEG System Demux",
                                                       NP_Priority,     9L,
                                                       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;

    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) );

    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 */

          struct PacketBlock *worknode,
                             *nextnode;

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

          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) );

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

              /* 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( Read( (sh -> sl -> sl_handle), dest, curr_readlen ) != curr_readlen )
              {
                error = IoErr();
                break;
              }

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

              if( read_size < 0L )
              {
                error = ERROR_BUFFER_OVERFLOW;
                break;
              }
            }

            stream_offset += worknode -> length;

            worknode = nextnode;
          }

          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;
            dp -> dp_Res2  = 0L;

            sh -> currpos = newpos;
          }
      }
          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 );
    }
}



