
/*
**
**  $VER: dispatch.c 1.12 (12.11.97)
**  anim.datatype 1.12
**
**  Dispatch routine for a DataTypes class
**
**  Written 1996/1997 by Roland 'Gizzy' Mainz
**  Original example source from David N. Junod
**
*/

/* Use asyncronous I/O below. Disabled due problems with Seek'ing */
#if 0
#define DOASYNCIO 1
#endif

#if 0
#define D( x ) x
#else
#define D( x )
#endif

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

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

/* IFF errors to DOS errors */
static const
LONG ifferr2doserr[] =
{
  0L,                         /* End of file (not an error).                  */
  0L,                         /* End of context (not an error).               */
  DTERROR_INVALID_DATA,       /* No lexical scope.                            */
  ERROR_NO_FREE_STORE,        /* Insufficient memory.                         */
  ERROR_SEEK_ERROR,           /* Stream read error.                           */
  ERROR_SEEK_ERROR,           /* Stream write error.                          */
  ERROR_SEEK_ERROR,           /* Stream seek error.                           */
  DTERROR_INVALID_DATA,       /* File is corrupt.                             */
  DTERROR_INVALID_DATA,       /* IFF syntax error.                            */
  ERROR_OBJECT_WRONG_TYPE,    /* Not an IFF file.                             */
  ERROR_REQUIRED_ARG_MISSING, /* Required call-back hook missing.             */
  0xDEADDEADUL                /* Return to client.  You should never see this */
};

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

/* local prototypes */
static                 BOOL                 FreeAbleFrame( struct AnimInstData *, struct FrameNode * );
static                 STRPTR               GetPrefsVar( struct ClassBase *, STRPTR );
static                 void                 YouShouldRegister( struct ClassBase *, struct AnimInstData * );
static                 BOOL                 matchstr( struct ClassBase *, STRPTR, STRPTR );
static                 void                 ReadENVPrefs( struct ClassBase *, struct AnimInstData * );
static                 LONG                 LoadFrames( struct ClassBase *, Object * );
static                 struct FrameNode    *AllocFrameNode( struct ClassBase *, APTR );
static                 struct FrameNode    *FindFrameNode( struct MinList *, ULONG );
static                 void                 FreeFrameNodeResources( struct ClassBase *, struct MinList * );
static                 void                 CopyBitMap( struct ClassBase *, struct BitMap *, struct BitMap * );
static                 void                 XCopyMem( struct ClassBase *, APTR, APTR, ULONG );
static                 void                 ClearBitMap( struct BitMap * );
static                 void                 XORBitMaps( struct BitMap *, struct BitMap * );
static                 struct BitMap       *AllocBitMapPooled( struct ClassBase *, ULONG, ULONG, ULONG, APTR );
static                 BOOL                 CMAP2Object( struct ClassBase *, Object *, UBYTE *, ULONG );
static                 struct ColorMap     *CMAP2ColorMap( struct ClassBase *, struct AnimInstData *, UBYTE *, ULONG );
static                 struct ColorMap     *CopyColorMap( struct ClassBase *, struct ColorMap * );
static                 APTR                 AllocVecPooled( struct ClassBase *, APTR, ULONG );
static                 void                 FreeVecPooled( struct ClassBase *, APTR, APTR );
static                 LONG                 DrawDLTA( struct ClassBase *, struct AnimInstData *, struct BitMap *, struct BitMap *, struct AnimHeader *, UBYTE *, ULONG );
static                 void                 DumpAnimHeader( struct ClassBase *, struct AnimInstData *, ULONG, struct AnimHeader * );
static                 struct FrameNode    *GetPrevFrameNode( struct FrameNode *, ULONG );
static                 void                 OpenLogfile( struct ClassBase *, struct AnimInstData * );
static                 void                 mysprintf( struct ClassBase *, STRPTR, STRPTR, ... );
static                 void                 verbose_printf( struct ClassBase *, struct AnimInstData *, STRPTR, ... );
static                 void                 error_printf( struct ClassBase *, struct AnimInstData *, STRPTR, ... );
static                 void                 AttachSample( struct ClassBase *, struct AnimInstData * );

static                 ULONG                SaveIFFAnim( struct ClassBase *, struct IClass *, Object *, struct dtWrite * );
static                 struct IFFHandle    *CreateDOSIFFHandle( struct ClassBase *, BPTR );
static                 LONG                 StartIFFAnim3( struct ClassBase *, struct AnimInstData *, struct IFFHandle *iff, struct AnimContext *, struct BitMapHeader *, ULONG, ULONG *, ULONG, ULONG, ULONG, struct BitMap * );
static                 void                 EndIFFAnim3( struct ClassBase *, struct AnimInstData *, struct IFFHandle * );
static                 LONG                 WriteIFFAnim3( struct ClassBase *, struct IFFHandle *, struct AnimContext *, ULONG, ULONG, struct BitMapHeader *, ULONG *, ULONG, struct BitMap * );
static                 LONG                 PutAnim3Delta( struct ClassBase *, struct IFFHandle *, struct AnimContext *, struct BitMap *, struct BitMap * );
static                 LONG                 PutILBMCMAP( struct ClassBase *, struct IFFHandle *, ULONG *, ULONG );
static                 LONG                 PutILBMBody( struct ClassBase *, struct IFFHandle *, struct BitMap *, struct BitMapHeader * );
static                 struct AnimContext  *CreateAnimContext( struct ClassBase *, ULONG, ULONG, ULONG );
#if 0
static                 struct BitMap       *PrevFrame( struct ClassBase *, struct AnimContext * );
#endif
static                 void                 SwapFrames( struct ClassBase *, struct AnimContext * );
static                 struct BitMap       *CurrFrame( struct ClassBase *, struct AnimContext * );
static                 void                 DeleteAnimContext( struct ClassBase *, struct AnimContext * );


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

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

    /* Create our class... */
    if( cl = MakeClass( ANIMDTCLASS, ANIMATIONDTCLASS, NULL, (ULONG)sizeof( struct AnimInstData ), 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;                   /* class library base as expected by datatypes.library */

      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 AnimInstData  *aid;
    ULONG                 retval = 0UL;

    switch( msg -> MethodID )
    {
/****** anim.datatype/OM_NEW *************************************************
*
*    NAME
*        OM_NEW -- Create a anim.datatype object.
*
*    FUNCTION
*        The OM_NEW method is used to create an instance of the anim.datatype
*        class.  This method is passed to the superclass first. After this,
*        anim.datatype parses the prefs file and makes a scan through
*        the data to get index information. Frame bitmaps are loaded if the
*        input stream isn't seekable (e.g. IFF handle/clipboard),
*        colormaps and the first frame are loaded immediately.
*        If a sample was set in the prefs, it will be loaded and attached
*        to the animation.
*
*    ATTRIBUTES
*        The following attributes can be specified at creation time.
*
*        DTA_SourceType (ULONG) -- Determinates the type of DTA_Handle
*            attribute. DTST_FILE, DTST_CLIPBOARD and DTST_RAM are supported.
*            If any other type was set in a given DTA_SourceType,
*            OM_NEW will be rejected.
*            A sourcetype of DTST_CLIPBOARD forces the LOADALL prefs
*            switch (can't seek on clipboard).
*            Defaults to DTST_FILE.
*
*        DTA_Handle -- For both DTST_FILE and DTST_CLIPBOARD, a
*            (struct IFFHandle *) is expected. This handle will be
*            created by datatypesclass depeding on the DTF_#? flag, which
*            is DTF_IFF here.  DTST_FILE, datatypesclass creates
*            a IFF handle from the given DTA_Name and DTA_Handle (a
*            BPTR returned by Lock), if DTST_CLIPBOARD, datatypesclass
*            passes the given (IFF) handle through.
*            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, DTST_CLIPBOARD or DTST_RAM as source type */
          if( ti = FindTagItem( DTA_SourceType, (((struct opSet *)msg) -> ops_AttrList) ) )
          {
            if( ((ti -> ti_Data) != DTST_FILE)      &&
                ((ti -> ti_Data) != DTST_CLIPBOARD) &&
                ((ti -> ti_Data) != DTST_RAM) )
            {
              SetIoErr( ERROR_OBJECT_WRONG_TYPE );

              break;
            }
          }

          /* Create object */
          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 );
          }
      }
          break;

/****** anim.datatype/OM_DISPOSE *********************************************
*
*    NAME
*        OM_DISPOSE -- Delete a anim.datatype object.
*
*    FUNCTION
*        The OM_DISPOSE method is used to delete an instance of the
*        anim.datatype class. This method is passed to the superclass when
*        it has completed.
*        This method frees all frame nodes and their contents (bitmaps,
*        colormaps, samples etc.)
*
*    RESULT
*        The object is deleted. 0UL is returned.
*
******************************************************************************
*
*/
      case OM_DISPOSE:
      {
          LONG saved_ioerr = IoErr();

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

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

          /* Free colormaps etc. */
          FreeFrameNodeResources( cb, (&(aid -> aid_FrameList)) );

          /* Free our key bitmap */
          FreeBitMap( (aid -> aid_KeyBitMap) );

          /* Delete the pools */
          DeletePool( (aid -> aid_FramePool) );
          DeletePool( (aid -> aid_Pool) );

          /* Close input file */
          if( aid -> aid_FH )
          {
#ifdef DOASYNCIO
            CloseAsync( cb, (aid -> aid_FH) );
#else
            Close( (aid -> aid_FH) );
#endif /* DOASYNCIO */
          }

          /* Close verbose output file */
          if( aid -> aid_VerboseOutput )
          {
            Close( (aid -> aid_VerboseOutput) );
          }

          /* Dispose object */
          DoSuperMethodA( cl, o, msg );
          
          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 ) )
          {
            /* Top instance ? */
            if( OCLASS( o ) == cl )
            {
              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 a refresh... */
                retval = 0UL;
              }
            }
          }
      }
          break;

/****** anim.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 IFF ANIM 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, IFF ANIM, 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
*        - Any sound attached to the animation will NOT be saved.
*
*        - A CTRL-D signal to the writing process aborts the save.
*
*    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 = SaveIFFAnim( 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;

/****** anim.datatype/ADTM_START *********************************************
*
*    NAME
*        ADTM_START -- Prepare for playback
*
*    FUNCTION
*        ADTM_START tells the subclass (us) to prepare for continous
*        playback. To avoid a long "search" for a full frame during the
*        playback clock is running, we load here the given timestamp
*        (asa_Frame) manually (and preseves it using a small trick, see
*        source).
*
*        After all, the method is passed to superclass, which starts the
*        playback (and the master clock).
*
*    RESULT
*        Returns result from superclass (animation.datatype)
*
*    NOTE
*
******************************************************************************
*
*/

      case ADTM_START:
      {
          struct FrameNode *fn;
          struct adtStart  *asa;
          ULONG             timestamp;

          aid       = (struct AnimInstData *)INST_DATA( cl, o );
          asa       = (struct adtStart *)msg;
          timestamp = asa -> asa_Frame;

          ObtainSemaphore( (&(aid -> aid_SigSem)) );

          /* Turn on async IO */
          aid -> aid_AsyncIO = TRUE;

          /* Find frame by timestamp */
          if( fn = FindFrameNode( (&(aid -> aid_FrameList)), timestamp ) )
          {
            /* Load bitmaps only if we don't cache the whole anim and
             * if we have a filehandle to load from (an empty object created using DTST_RAM don't have this)...
             */
            if( ((aid -> aid_LoadAll) == FALSE) && (aid -> aid_FH) )
            {
              /* If no bitmap is loaded, load it... */
              if( (fn -> fn_BitMap) == NULL )
              {
                struct adtFrame alf;

                /* reset method msg */
                memset( (void *)(&alf), 0, sizeof( struct adtFrame ) );

                /* load frame */
                alf . MethodID      = ADTM_LOADFRAME;
                alf . alf_TimeStamp = timestamp;
                alf . alf_Frame     = timestamp;

                /* Load frame */
                if( DoMethodA( o, (Msg)(&alf) ) )
                {
                  /* Success ! */

                  /* The "trick" used here is to decrase the fn_UseCount
                   * WITHOUT unloading the bitmap. The first following
                   * ADTM_LOADFRAME triggered by animation.datatypes playback
                   * clock gets this frame without any problems
                   */
                  fn -> fn_UseCount--;
                }
                else
                {
                  /* Failure ! */
                  error_printf( cb, aid, "ADTM_START load error %ld", IoErr() );

                  /* Unload frame... */
                  alf . MethodID = ADTM_UNLOADFRAME;
                  DoMethodA( o, (Msg)(&alf) );
                }
              }
            }
          }

          ReleaseSemaphore( (&(aid -> aid_SigSem)) );

          retval = DoSuperMethodA( cl, o, msg );
      }
          break;

/****** anim.datatype/ADTM_PAUSE **********************************************
*
*    NAME
*        ADTM_PAUSE -- Pause playback
*
*    FUNCTION
*        ADTM_PAUSE tells the subclass (use) to pause playback.
*
*        The method is passed to animation.datatype first, which pauses the
*        playback clock.
*
*    RESULT
*        Returns result from superclass (animation.datatype)
*
*    NOTE
*
******************************************************************************
*
*/

      case ADTM_PAUSE:
      {
          aid = (struct AnimInstData *)INST_DATA( cl, o );

          /* Pass msg to superclass first ! */
          retval = DoSuperMethodA( cl, o, msg );

          ObtainSemaphore( (&(aid -> aid_SigSem)) );

            /* Return to syncrounous IO */
            aid -> aid_AsyncIO = FALSE;

          ReleaseSemaphore( (&(aid -> aid_SigSem)) );
      }
          break;

/****** anim.datatype/ADTM_STOP **********************************************
*
*    NAME
*        ADTM_STOP -- Stop playback
*
*    FUNCTION
*        ADTM_STOP tells the subclass (use) to stop playback.
*
*        The method is passed to animation.datatype first, which stops the
*        playback clock.
*
*        After clock has been stopped, we search for frames which have a
*        0 UseCount and have not been unloaded yet (a small cleaup to get
*        rid of frames which are loaded using the "trick" in ADTM_START
*        code).
*
*    RESULT
*        Returns result from superclass (animation.datatype)
*
*    NOTE
*
******************************************************************************
*
*/


      case ADTM_STOP:
      {
          aid = (struct AnimInstData *)INST_DATA( cl, o );

          /* Pass msg to superclass first ! */
          retval = DoSuperMethodA( cl, o, msg );

          ObtainSemaphore( (&(aid -> aid_SigSem)) );

#if 0
          if( ((aid -> aid_LoadAll) == FALSE) && (aid -> aid_FH) )
          {
            struct FrameNode *worknode,
                             *nextnode;

            worknode = (struct FrameNode *)(aid -> aid_FrameList . mlh_Head);

            while( nextnode = (struct FrameNode *)(worknode -> fn_Node . mln_Succ) )
            {
              /* Free an existing bitmap if it isn't in use and if it is NOT the first bitmap */
              if( ((worknode -> fn_UseCount) == 0) && (worknode -> fn_BitMap) && (worknode != (struct FrameNode *)(aid -> aid_FrameList . mlh_Head)) )
              {
                /* Don't free the current nor the previous nor the next bitmap (to avoid problems with delta frames) */
                if( (worknode != (aid -> aid_CurrFN)) &&
                    (worknode != (struct FrameNode *)(aid -> aid_CurrFN -> fn_Node . mln_Succ)) &&
                    (worknode != (struct FrameNode *)(aid -> aid_CurrFN -> fn_Node . mln_Pred)) )
                {
                  FreeVecPooled( cb, (aid -> aid_FramePool), (worknode -> fn_BitMap) );
                  worknode -> fn_BitMap = NULL;
                }
              }

              worknode = nextnode;
            }
          }
#endif

          /* Return to syncrounous IO */
          aid -> aid_AsyncIO = FALSE;

          ReleaseSemaphore( (&(aid -> aid_SigSem)) );
      }
          break;


/****** anim.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 given timestamp will be used to find a matching timestamp
*        in the internal FrameNode list. If it was found, the corresponding
*        timing, bitmap and colormap data are stored into the struct
*        adtFrame. If the bitmap wasn't loaded at this time, this method 
*        attempts to load it from disk.
*
*    RESULT
*        Returns the bitmap ptr if a bitmap was found, 0UL otherwise;
*        in case of failure Result2 contains the cause:
*        ERROR_OBJECT_NOT_FOUND: Given timestamp does not exist
*        ERROR_NO_FREE_STORE:    No memory
*        and so on...
*
*    NOTE
*        It is expected that a 0 return code (error) causes an
*        ADTM_UNLOADFRAME that the invalid bitmap etc. will be freed.
*
******************************************************************************
*
*/
      case ADTM_LOADFRAME:
      {
          struct FrameNode *fn;
          struct adtFrame  *alf;

          aid = (struct AnimInstData *)INST_DATA( cl, o );
          alf = (struct adtFrame *)msg;

          ObtainSemaphore( (&(aid -> aid_SigSem)) );

          /* Like "realloc": Free any given frame here */
          if( alf -> alf_UserData )
          {
            msg -> MethodID = ADTM_UNLOADFRAME;

              DoMethodA( o, msg );

            msg -> MethodID = ADTM_LOADFRAME;
          }

          /* Find frame by timestamp */
          if( fn = FindFrameNode( (&(aid -> aid_FrameList)), (alf -> alf_TimeStamp) ) )
          {
            LONG error = 0L;

            aid -> aid_CurrFN = fn;

            /* Load bitmaps only if we don't cache the whole anim and
             * if we have a filehandle to load from (an empty object created using DTST_RAM don't have this)...
             */
            if( ((aid -> aid_LoadAll) == FALSE) && (aid -> aid_FH) )
            {
              /* If no bitmap is loaded, load it... */
              if( (fn -> fn_BitMap) == NULL )
              {
                if( fn -> fn_BitMap = AllocBitMapPooled( cb, (ULONG)(aid -> aid_BMH -> bmh_Width), (ULONG)(aid -> aid_BMH -> bmh_Height), (ULONG)(aid -> aid_BMH -> bmh_Depth), (aid -> aid_FramePool) ) )
                {
                  struct FrameNode *worknode = fn;
                  ULONG             rollback = 0UL;
                  UBYTE            *buff;
                  ULONG             buffsize;

                  /* Buffer to fill. Below we try to read some more bytes
                   * (the size value is stored in worknode -> fn_LoadSize)
                   * (ANHD chunk (~68 bytes), maybe a CMAP) to save
                   * the Seek in the next cycle.
                   * This makes only much sense when doing async io (during playback)...
                   */

                  /* Not the last frame !
                   * Note that this code is replicated in the loop below !!
                   */

                  worknode -> fn_LoadSize = worknode -> fn_BMSize;

                  if( (worknode -> fn_Node . mln_Succ -> mln_Succ) && (aid -> aid_AsyncIO) )
                  {
                    ULONG nextpos = ((((struct FrameNode *)(worknode -> fn_Node . mln_Succ)) -> fn_BMOffset) + 8UL);

                    worknode -> fn_LoadSize = MAX( (worknode -> fn_LoadSize), (nextpos - ((worknode -> fn_BMOffset) + 8UL)) );

                    /* Don't alloc a too large buffer... */
                    worknode -> fn_LoadSize = MIN( (worknode -> fn_LoadSize), ((worknode -> fn_BMSize) * 2UL) );
                  }

                  buffsize = worknode -> fn_LoadSize;

                  do
                  {
                    worknode = worknode -> fn_PrevFrame;
                    rollback++;

                    worknode -> fn_LoadSize = worknode -> fn_BMSize;

                    if( (worknode -> fn_Node . mln_Succ -> mln_Succ) && (aid -> aid_AsyncIO) )
                    {
                      ULONG nextpos = ((((struct FrameNode *)(worknode -> fn_Node . mln_Succ)) -> fn_BMOffset) + 8UL);

                      worknode -> fn_LoadSize = MAX( (worknode -> fn_LoadSize), (nextpos - ((worknode -> fn_BMOffset) + 8UL)) );

                      /* Don't alloc a too large buffer... */
                      worknode -> fn_LoadSize = MIN( (worknode -> fn_LoadSize), ((worknode -> fn_BMSize) * 2UL) );
                    }

                    buffsize = MAX( buffsize, (worknode -> fn_LoadSize) );
                  } while( ((worknode -> fn_BitMap) == NULL) && ((worknode -> fn_TimeStamp) != 0UL) );

                  if( ((worknode -> fn_BitMap) == NULL) && ((worknode -> fn_TimeStamp) == 0UL) )
                  {
                    verbose_printf( cb, aid, "first frame without bitmap ... !\n" );
                    ClearBitMap( (fn -> fn_BitMap) );
                  }

                  /* Alloc buffer for compressed frame (DLTA) data */
                  if( buff = (UBYTE *)AllocVecPooled( cb, (aid -> aid_Pool), (buffsize + 32UL) ) )
                  {
                    do
                    {
                      ULONG current = rollback;

                      worknode = fn;

                      while( current-- )
                      {
                        worknode = worknode -> fn_PrevFrame;
                      }

                      if( (worknode -> fn_BitMap) && (worknode != fn) )
                      {
                        CopyBitMap( cb, (worknode -> fn_BitMap), (fn -> fn_BitMap) );
                      }
                      else
                      {
                        LONG seekdist; /* seeking distance (later Seek result, if Seek'ed) */

                        seekdist = (((worknode -> fn_BMOffset) + 8UL) - (aid -> aid_CurrFilePos));

                        /* Seek needed ? */
                        if( seekdist != 0L )
                        {
#ifdef DOASYNCIO
                          seekdist = SeekAsync( cb, (aid -> aid_FH), seekdist, OFFSET_CURRENT );
#else
                          seekdist = Seek( (aid -> aid_FH), seekdist, OFFSET_CURRENT );
#endif /* DOASYNCIO */
                        }

                        /* "Seek" success ? */
                        if( seekdist != (-1L) )
                        {
                          LONG bytesread;

#ifdef DOASYNCIO
                          bytesread = ReadAsync( cb, (aid -> aid_FH), buff, (worknode -> fn_LoadSize) );
#else
                          bytesread = Read( (aid -> aid_FH), buff, (worknode -> fn_LoadSize) );
#endif /* DOASYNCIO */

                          /* No error during reading ? */
                          if( (bytesread >= (worknode -> fn_BMSize)) && (bytesread != -1L) )
                          {
                            LONG ifferror;

                            if( ifferror = DrawDLTA( cb, aid, (fn -> fn_BitMap), (fn -> fn_BitMap), (&(worknode -> fn_AH)), buff, (worknode -> fn_BMSize) ) )
                            {
                              error_printf( cb, aid, "dlta unpacking error %lu\n", ifferror );

                              /* convert IFFParse error to DOS error */
                              error = ifferr2doserr[ (-ifferror - 1) ];
                            }

                            /* Bump file pos */
                            aid -> aid_CurrFilePos = ((worknode -> fn_BMOffset) + 8UL) + bytesread;
                          }
                          else
                          {
                            /* Read error */
                            error = IoErr();

                            /* Error, rewind stream */
#ifdef DOASYNCIO
                            SeekAsync( cb, (aid -> aid_FH), 0L, OFFSET_BEGINNING );
#else
                            Seek( (aid -> aid_FH), 0L, OFFSET_BEGINNING );
#endif /* DOASYNCIO */
                            aid -> aid_CurrFilePos = 0L;
                          }

                          worknode -> fn_LoadSize = 0UL; /* destroy that this value won't affect anything else */
                        }
                        else
                        {
                          /* Seek error */
                          error = IoErr();
                        }
                      }
                    } while( rollback-- && (error == 0L) );

                    FreeVecPooled( cb, (aid -> aid_Pool), buff );
                  }
                  else
                  {
                    /* No memory for compressed frame data */
                    error = ERROR_NO_FREE_STORE;
                  }
                }
                else
                {
                  /* No memory for frame bitmap */
                  error = ERROR_NO_FREE_STORE;
                }
              }
            }

            /* Store frame/context information */
            alf -> alf_Frame    = fn -> fn_Frame;
            alf -> alf_Duration = fn -> fn_Duration;
            alf -> alf_UserData = (APTR)fn;        /* Links back to this FrameNode (used by ADTM_UNLOADFRAME) */

            /* Store bitmap information */
            alf -> alf_BitMap = fn -> fn_BitMap;
            alf -> alf_CMap   = fn -> fn_CMap;

            /* Is there a sample to play ? */
            if( fn -> fn_Sample )
            {
              /* Store sound information */
              alf -> alf_Sample       = fn -> fn_Sample;
              alf -> alf_SampleLength = fn -> fn_SampleLength;
              alf -> alf_Period       = fn -> fn_Period;
            }
            else
            {
              /* No sound */
              alf -> alf_Sample       = NULL;
              alf -> alf_SampleLength = 0UL;
              alf -> alf_Period       = 0UL;
            }

            /* Frame "in use", even for a unsuccessful result; on error
             * animation.datatype send an ADTM_UNLOADFRAME which frees
             * allocated resources and decreases the "UseCount"...
             */
            fn -> fn_UseCount++;

            /* Is this node in the posted-free queue ? */
            if( fn -> fn_PostedFree )
            {
              Remove( (struct Node *)(&(fn -> fn_PostedFreeNode)) );
              fn -> fn_PostedFree = FALSE;
            }

            retval = ((error)?(0UL):(ULONG)(alf -> alf_BitMap)); /* Result  */
            SetIoErr( error );                                   /* Result2 */
          }
          else
          {
            /* no matching frame found */
            SetIoErr( ERROR_OBJECT_NOT_FOUND );
          }

          ReleaseSemaphore( (&(aid -> aid_SigSem)) );
      }
          break;

/****** anim.datatype/ADTM_UNLOADFRAME ***************************************
*
*    NAME
*        ADTM_UNLOADFRAME -- Load frame contents
*
*    FUNCTION
*        The ADTM_UNLOADFRAME method is used to release the contents of a
*        animation frame.
*
*        This method frees the bitmap data found in adtFrame.
*
*    RESULT
*        Returns always 0UL.
*
******************************************************************************
*
*/
      case ADTM_UNLOADFRAME:
      {
          struct FrameNode *fn;
          struct adtFrame  *alf;

          aid = (struct AnimInstData *)INST_DATA( cl, o );
          alf = (struct adtFrame *)msg;

          /* Free bitmaps only if we don't cache the whole anim */
          if( (aid -> aid_LoadAll) == FALSE )
          {
            struct MinNode *pfn;
            UWORD           i   = 10;

            ObtainSemaphore( (&(aid -> aid_SigSem)) );

            if( fn = (struct FrameNode *)(alf -> alf_UserData) )
            {
              if( (fn -> fn_UseCount) > 0 )
              {
                fn -> fn_UseCount--;

                /* Free an existing bitmap if it isn't in use and if it is NOT the first bitmap */
                if( ((fn -> fn_UseCount) == 0) && (fn -> fn_BitMap) && (fn != (struct FrameNode *)(aid -> aid_FrameList . mlh_Head)) )
                {
                  if( FALSE /*FreeAbleFrame( aid, fn )*/ )
                  {
                    /* Is this node in the posted-free queue ? */
                    if( fn -> fn_PostedFree )
                    {
                      Remove( (struct Node *)(&(fn -> fn_PostedFreeNode)) );
                      fn -> fn_PostedFree = FALSE;

                      D( kprintf( "free posted 1 %lu\n", (fn -> fn_TimeStamp) ) );
                    }

                    FreeVecPooled( cb, (aid -> aid_FramePool), (fn -> fn_BitMap) );
                    fn -> fn_BitMap = NULL;
                  }
                  else
                  {
                    if( (fn -> fn_PostedFree) == FALSE )
                    {
                      D( kprintf( "posted free %lu\n", (fn -> fn_TimeStamp) ) );

                      AddTail( (struct List *)(&(aid -> aid_PostedFreeList)), (struct Node *)(&(fn -> fn_PostedFreeNode)) );
                      fn -> fn_PostedFree = TRUE;
                    }
                  }
                }
              }
            }

            while( pfn = (struct MinNode *)RemHead( (struct List *)(&(aid -> aid_PostedFreeList)) ) )
            {
              fn = POSTEDFREENODE2FN( pfn );
              fn -> fn_PostedFree = FALSE;

              if( (fn -> fn_UseCount) == 0 )
              {
                if( FreeAbleFrame( aid, fn ) )
                {
                  D( kprintf( "free posted 2 %lu at %lu\n", (fn -> fn_TimeStamp), (((struct FrameNode *)(alf -> alf_UserData)) -> fn_TimeStamp) ) );

                  FreeVecPooled( cb, (aid -> aid_FramePool), (fn -> fn_BitMap) );
                  fn -> fn_BitMap = NULL;
                }
                else
                {
                  AddTail( (struct List *)(&(aid -> aid_PostedFreeList)), (struct Node *)(&(fn -> fn_PostedFreeNode)) );
                  fn -> fn_PostedFree = TRUE;
                }

                /* Don't process the list twice */
                if( fn == ((struct FrameNode *)(alf -> alf_UserData)) )
                {
                  i = MIN( 1, i );

                  break;
                }

                if( i-- == 0 )
                {
                  D( kprintf( "pl overflow at %lu\n", (((struct FrameNode *)(alf -> alf_UserData)) -> fn_TimeStamp) ) );

                  break;
                }
              }
            }

            ReleaseSemaphore( (&(aid -> aid_SigSem)) );
          }

          /* Indicate that the frame has been free'ed. */
          alf -> alf_UserData = NULL;
      }
          break;

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

    return( retval );
}


static
BOOL FreeAbleFrame( struct AnimInstData *aid, struct FrameNode *fn )
{
    struct FrameNode *currfn = aid -> aid_CurrFN;

    /* Don't free the current nor the previous nor the next bitmap (to avoid problems with delta frames) */
    if( (fn == currfn) ||
        (fn == (struct FrameNode *)(currfn -> fn_Node . mln_Succ)) ||
        (fn == (struct FrameNode *)(currfn -> fn_Node . mln_Pred)) )
    {
      return( FALSE );
    }

    if( ABS( ((LONG)(fn -> fn_TimeStamp)) - ((LONG)(currfn -> fn_TimeStamp)) ) < 5UL )
    {
      return( FALSE );
    }

    return( TRUE );
}


/****** anim.datatype/preferences ********************************************
*
*   NAME
*       preferences
*
*   DESCRIPTION
*       The "ENV:Classes/DataTypes/anim.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,MODEID/K/N,CMAPS/S,NOCMAPS/S,
*       DPAINTBRUSHPATCH/S,NODPAINTBRUSHPATCH/S,FPS/K/N,DYNAMICTIMING/S,
*       NODYNAMICTIMING/S,SAMPLE/K,SAMPLESPERFRAME=SPF/K/N,VOLUME/K/N,
*       LOADALL/S,NOLOADALL/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.
*
*       MODEID -- Select screen mode id of datatype (will be stored in
*           ADTA_ModeID). Note that the DOS ReadArgs function used for parsing
*           fetches a SIGNED long. The bit 31 will be represented by minus
*           '-'. (example: "MODEID=266240" sets the mode to the A2024 screen
*           mode id)
*           Defaults to 0, which means: Use the screen mode from CAMG chunk,
*           if missing use the best screenmode available for the given width,
*           height and depth.
*
*       CMAPS -- Use colormaps per frame. This switch is set per default,
*           and can be turned off by the NOCMAPS option, later it can be
*           turned on again by this option.
*
*       NOCMAPS -- Don't load/use colormaps per frame. Only the initial
*           colormap will be used.
*           The current version of animation.datatype (V40.7 (28.09.93)) does
*           not implement per frame colormaps, it's output may look trashed.
*           Custom players like "DBufDTAnim" does support
*           "per frame colormaps",
*           animation.datatype V41 will implement "per frame colormaps".
*
*       DPAINTBRUSHPATCH -- If frames of ANIM-5 with an interleave of 1
*           occurs, the XOR mode is forced, even if the XOR bit is not set.
*           This fixes problems with some DPaint brush animations.
*           This option is ON per default and can be turned off by the 
*           NODPAINTBRUSHPATCH option.
*
*       NODPAINTBRUSHPATCH -- Turns off the DPaint brush patch. See 
*           DPAINTBRUSHPATCH option for details.
*
*       FPS -- Frames Per Second
*           Defaults to FPS set by DPAN chunk. If the DPAN chunk is missing
*           a fixed 5 fps rate is used.
*           A value of 0 here means: Use default FPS.
*
*       DYNAMICTIMING -- Turns dynamic timing on. Default if superclass
*           is animation.datatype V41, otherwise this option must
*           be explicitly set.
*
*       NODYNAMICTIMING -- Turn dynamic timing off. Default if superclass
*           is animation.datatype < V41 (e.g. V40.6).
*
*       SAMPLE -- Attach the given sample to the animation. The sample will
*           be loaded using datatypes (GID_SOUND).
*           Only one sample can be attached to one animationstream, any
*           following attempt to attach a sample will be ignored.
*
*       SAMPLESPERFRAME -- Set samples per frame rate for sound. This
*           overrides the own internal calculations to get rid of rounding
*           errors.
*
*       VOLUME -- Volume of the sound when playing.
*           Defaults to 64, which is the maximum. A value greater than 64 will
*           be set to 64.
*
*       LOADALL -- Load all frames into memory. If the source input is a
*           clipboard, this option is always set.
*
*       NOLOADALL -- Turns off the LOADALL flag, which may be set in a prefs-
*           line before. This switch is set per default, and can be turned off
*           by the LOADALL option, later it can be turned on again by this
*           option.
*
*       REGISTERED -- Turns off the shareware notice requester.
*
*   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 AnimInstData *aid )
{
    struct RDArgs envvarrda =
    {
      NULL,
      256L,
      0L,
      0L,
      NULL,
      0L,
      NULL,
      RDAF_NOPROMPT
    };

    struct
    {
      STRPTR  matchproject;
      long   *verbose;
      long   *modeid;
      long   *cmaps;
      long   *nocmaps;
      long   *dpaintbrushpatch;
      long   *nodpaintbrushpatch;
      long   *fps;
      long   *dynamictiming;
      long   *nodynamictiming;
      STRPTR  sample;
      long   *samplesperframe;
      long   *volume;
      long   *loadall;
      long   *noloadall;
      long   *registered;
    } animargs;

    TEXT   varbuff[ 258 ];
    STRPTR var;

    if( var = GetPrefsVar( cb, "Classes/DataTypes/anim.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 *)(&animargs), 0, sizeof( animargs ) );          /* Clear result array          */

          if( ReadArgs( "MATCHPROJECT/K,"
                        "VERBOSE/S,"
                        "MODEID/K/N,"
                        "CMAPS/S,"
                        "NOCMAPS/S,"
                        "DPAINTBRUSHPATCH/S,"
                        "NODPAINTBRUSHPATCH/S,"
                        "FPS/K/N,"
                        "DYNAMICTIMING/S,"
                        "NODYNAMICTIMING/S,"
                        "SAMPLE/K,"
                        "SAMPLESPERFRAME=SPF/K/N,"
                        "VOLUME/K/N,"
                        "LOADALL/S,"
                        "NOLOADALL/S,"
                        "REGISTERED/S", (LONG *)(&animargs), (&envvarrda) ) )
          {
            BOOL noignore = TRUE;

            if( (animargs . matchproject) && (aid -> aid_ProjectName) )
            {
              noignore = matchstr( cb, (animargs . matchproject), (aid -> aid_ProjectName) );
            }

            if( noignore )
            {
              if( animargs . verbose )
              {
                OpenLogfile( cb, aid );
              }

              if( animargs . modeid )
              {
                aid -> aid_ModeID = *(animargs . modeid);
              }

              if( animargs . cmaps )
              {
                aid -> aid_NoCMAPs = FALSE;
              }

              if( animargs . nocmaps )
              {
                aid -> aid_NoCMAPs = TRUE;
              }

              if( animargs . dpaintbrushpatch )
              {
                aid -> aid_NoDPaintBrushPatch = FALSE;
              }

              if( animargs . nodpaintbrushpatch )
              {
                aid -> aid_NoDPaintBrushPatch = TRUE;
              }

              if( animargs . fps )
              {
                aid -> aid_FPS = *(animargs . fps);
              }

              if( animargs . dynamictiming )
              {
                aid -> aid_NoDynamicTiming = FALSE;
              }

              if( animargs . nodynamictiming )
              {
                aid -> aid_NoDynamicTiming = TRUE;
              }

              if( (animargs . sample) && ((aid -> aid_Sample) == NULL) )
              {
                Object *so;
                LONG    ioerr = 0L;

                verbose_printf( cb, aid, "loading sample \"%s\"...\n", (animargs . sample) );

                if( so = NewDTObject( (animargs . sample), DTA_GroupID, GID_SOUND, TAG_DONE ) )
                {
                  BYTE  *sample;
                  ULONG  length;
                  ULONG  period;

                  /* Get sample data from object */
                  if( GetDTAttrs( so, SDTA_Sample,       (&sample),
                                      SDTA_SampleLength, (&length),
                                      SDTA_Period,       (&period),
                                      TAG_DONE ) == 3UL )
                  {
                    if( aid -> aid_Sample = (STRPTR)AllocPooled( (aid -> aid_Pool), (length + 1UL) ) )
                    {
                      /* Copy sample and context */
                      XCopyMem( cb, (APTR)sample, (APTR)(aid -> aid_Sample), length );
                      aid -> aid_SampleLength = length;
                      aid -> aid_Period       = period;
                    }
                    else
                    {
                      /* Can't alloc sample */
                      ioerr = ERROR_NO_FREE_STORE;
                    }
                  }
                  else
                  {
                    /* Object does not support the requested attributes */
                    ioerr = ERROR_OBJECT_WRONG_TYPE;
                  }

                  DisposeDTObject( so );
                }
                else
                {
                  /* NewDTObjectA failed, cannot load sample... */
                  ioerr = IoErr();
                }

                if( (aid -> aid_Sample) == NULL )
                {
                  TEXT errbuff[ 256 ];

                  if( ioerr >= DTERROR_UNKNOWN_DATATYPE )
                  {
                    mysprintf( cb, errbuff, GetDTString( ioerr ), (animargs . sample) );
                  }
                  else
                  {
                    Fault( ioerr, (animargs . sample), errbuff, sizeof( errbuff ) );
                  }

                  error_printf( cb, aid, "can't load sample: \"%s\" line %lu\n", errbuff, linecount );
                }
              }

              if( animargs . samplesperframe )
              {
                aid -> aid_SamplesPerFrame = (ULONG)(*(animargs . samplesperframe));
              }

              if( animargs . volume )
              {
                aid -> aid_Volume = *(animargs . volume);

                if( (aid -> aid_Volume) > 64UL )
                {
                  aid -> aid_Volume = 64UL;
                }
              }

              if( animargs . loadall )
              {
                aid -> aid_LoadAll = TRUE;
              }

              if( animargs . noloadall )
              {
                aid -> aid_LoadAll = FALSE;
              }

              if( animargs . registered )
              {
                aid -> aid_Registered = TRUE;
              }
            }
            else
            {
              verbose_printf( cb, aid, "prefs line %lu ignored\n", linecount );
            }

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

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

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

        prefsline = ++nextprefsline;
        linecount++;
      }

      FreeVec( var );
    }

    /* Notify the user that she/he is using shareware... */
    if( !(aid -> aid_Registered) )
    {
      YouShouldRegister( cb, aid );
    }
}


/* The shareware notify requester */
static
void YouShouldRegister( struct ClassBase *cb, struct AnimInstData *aid )
{
    struct EasyStruct SharewareES;
    ULONG             xa,
                      xb,
                      xc,
                      xd;
    ULONG             result;
    LONG              reqresult;

    TEXT              buffer[ 256 ],
                      cbuffer[ 256 ];
#define NUMCHOICES (6)
    ULONG             choices[ NUMCHOICES ];

    /* Create random values */
    CurrentTime( (&xb), (&xa) );
    xc = FastRand( xa );
    xd = FastRand( xb );

    /* Strip down to human-friendly values */
    xa %=  5;
    xb %=  7;
    xc %= 11;
    xd %= 13;

    /* calc */
    result = xa + xb * xc + xd;

    /* Build term */
    mysprintf( cb, buffer, "%lu + %lu * %lu + %lu = ???", xa, xb, xc, xd );

    /* Our choices */
    choices[ 0 ] = result;
    choices[ 1 ] = result + xb;
    choices[ 2 ] = result + xb + 1;
    choices[ 3 ] = result + xa / 2;
    choices[ 4 ] = result + 2 + xc;
    choices[ 5 ] = xa + xb + result / 2;

    mysprintf( cb, cbuffer, "%lu|%lu|%lu|%lu|%lu|%lu",
               choices[ (0 + xc) % NUMCHOICES ],
               choices[ (1 + xc) % NUMCHOICES ],
               choices[ (2 + xc) % NUMCHOICES ],
               choices[ (3 + xc) % NUMCHOICES ],
               choices[ (4 + xc) % NUMCHOICES ],
               choices[ (5 + xc) % NUMCHOICES ] );

    /* Prepare requester */
    SharewareES . es_StructSize   = sizeof( struct EasyStruct );
    SharewareES . es_Flags        = 0UL;
    SharewareES . es_Title        = "IFF ANIM DataType Shareware notice";
    SharewareES . es_TextFormat   = "Please register this DataType if you're using it more than 30 days\n"
                                    "See docs how to pay the shareware fee.\n"
                                    "To get rid of this requester forever, you must set the REGISTERED switch\n"
                                    "in the prefs-file \"ENVARC:Classes/DataTypes/anim.prefs\"\n"
                                    "then reboot the computer.\n"
                                    "To close the requester successfully, you must answer the following term:\n"
                                    "%s";
    SharewareES . es_GadgetFormat = cbuffer;

    /* The trial and error loop... */
    do
    {
      reqresult = EasyRequest( NULL, (&SharewareES), NULL, buffer );

      if( reqresult == 0L )
      {
        reqresult = NUMCHOICES;
      }

      reqresult--;

    } while( choices[ (reqresult + xc) % NUMCHOICES ] != result );
}


static
LONG LoadFrames( struct ClassBase *cb, Object *o )
{
    struct AnimInstData *aid   = (struct AnimInstData *)INST_DATA( (cb -> cb_Lib . cl_Class), o );
    LONG                 error = 0L;

    /* Init */
    InitSemaphore( (&(aid -> aid_SigSem)) );
    NewList( (struct List *)(&(aid -> aid_FrameList)) );
    NewList( (struct List *)(&(aid -> aid_PostedFreeList)) );

    /* Create a memory pool for frame nodes and delta buffers */
    if( aid -> aid_Pool = CreatePool( MEMF_PUBLIC, 16384UL, 16384UL ) )
    {
      APTR                 fh;                              /* handle (IFF stream handle)      */
      ULONG                sourcetype;                      /* type of stream (either DTST_FILE or DTST_CLIPBOARD */
      ULONG                pos        = 0UL;                /* current file pos in IFF stream  */
      struct BitMapHeader *bmh;                             /* obj's bitmapheader              */
      ULONG                modeid     = (ULONG)INVALID_ID;  /* anim view mode                  */
      ULONG                animwidth  = 0UL,                /* anim width                      */
                           animheight = 0UL,                /* anim height                     */
                           animdepth  = 0UL;                /* anim depth                      */
      ULONG                timestamp  = 0UL;                /* timestamp                       */
      ULONG                minreltime = 1UL,                /* Maximum ah_RelTime value        */
                           maxreltime = 0UL;                /* Minimum ah_RelTime              */
      struct tPoint       *grabpoint  = NULL;               /* Grabbing point of animation     */

      /* Prefs defaults */
      aid -> aid_Volume          = 64UL;
      aid -> aid_NoDynamicTiming = MAKEBOOL( ((cb -> cb_SuperClassBase -> lib_Version) < 41U) );
      aid -> aid_ModeID          = (ULONG)INVALID_ID;

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

      /* Get file handle, handle type and BitMapHeader */
      if( GetDTAttrs( o, DTA_SourceType,    (&sourcetype),
                         DTA_Handle,        (&fh),
                         DTA_Name,          (&(aid -> aid_ProjectName)),
                         ADTA_BitMapHeader, (&bmh),
                         ADTA_Grab,         (&grabpoint), /* animation.datatype V41 */
                         TAG_DONE ) >= 4UL ) /* ADTA_Grab not supported in V40, e.g. 4 == V40, 5 == V41 */
      {
        struct IFFHandle *iff = NULL;

        aid -> aid_BMH = bmh; /* Store BitMapHeader */

        switch( sourcetype )
        {
          case DTST_CLIPBOARD:
          {
              aid -> aid_LoadAll = TRUE;

              iff = (struct IFFHandle *)fh;
          }
              break;

          case DTST_FILE:
          {
              BPTR iff_file_fh;
              BPTR cloned_fh    = NULL;

              iff = (struct IFFHandle *)fh;

              /* Attempt to open file from given stream (allows usage of virtual fs when using datatypes.library V45) */
              iff_file_fh = (BPTR)(iff -> iff_Stream); /* see iffparse.library/InitIFFasDOS autodoc */

              if( iff_file_fh )
              {
                BPTR lock;

                if( lock = DupLockFromFH( iff_file_fh ) )
                {
                  /* Set up a filehandle for disk-based loading (random loading) */
                  if( !(cloned_fh = (LONG)OpenFromLock( lock )) )
                  {
                    /* failure */
                    UnLock( lock );
                  }
                }
              }

              /* OpenFromLock failed ? - Then open by name :-( */
              if( cloned_fh == NULL )
              {
                /* Set up a filehandle for disk-based loading (random loading) */
                if( !(cloned_fh = (LONG)Open( (aid -> aid_ProjectName), MODE_OLDFILE )) )
                {
                  /* Can't open file */
                  error = IoErr();
                }
              }

              if( cloned_fh )
              {
#ifdef DOASYNCIO
                if( !(aid -> aid_FH = OpenAsync( cb, cloned_fh, 8192L )) )
                {
                  /* Can't get async access */
                  error = IoErr();
                }
#else
                aid -> aid_FH = cloned_fh;
#endif /* DOASYNCIO */
              }
          }
              break;

          case DTST_RAM:
          {
              /* do nothing */
          }
              break;

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

        /* Any error ? */
        if( error == 0L )
        {
          if( iff )
          {
            struct StoredProperty *bmhdprop       = NULL, /* ILBM BMHD (struct BitMapHeader)        */
                                  *camgprop       = NULL, /* ILBM CAMG (amiga view mode id)         */
                                  *grabprop       = NULL, /* ILBM GRAB (grabbing point)             */
                                  *dpanprop       = NULL, /* DPaint DPAN chunk                      */
                                  *annoprop       = NULL, /* Generic IFF ANNO (annotation) chunk    */
                                  *authprop       = NULL, /* Generic IFF AUTH (author) chunk        */
                                  *copyrightprop  = NULL, /* Generic IFF (C)  (copyright) chunk     */
                                  *fverprop       = NULL, /* Generic IFF FVER (version) chunk       */
                                  *nameprop       = NULL; /* Generic IFF NAME (name) chunk          */

#define NUM_PROPCHUNKS (9L)
            const
            LONG propchunks[ (NUM_PROPCHUNKS * 2) ] =
            {
              ID_ILBM, ID_BMHD,
              ID_ILBM, ID_CAMG,
              ID_ILBM, ID_GRAB,
              ID_ILBM, ID_DPAN,
              ID_ILBM, ID_ANNO,
              ID_ILBM, ID_AUTH,
              ID_ILBM, ID_Copyright,
              ID_ILBM, ID_FVER,
              ID_ILBM, ID_NAME
            };

            if( !(error = PropChunks( iff, (LONG *)propchunks, NUM_PROPCHUNKS )) )
            {
#define NUM_STOPCHUNKS (5L)
              const
              LONG stopchunks[ (NUM_STOPCHUNKS * 2) ] =
              {
                ID_ILBM, ID_FORM,
                ID_ILBM, ID_ANHD,
                ID_ILBM, ID_CMAP,
                ID_ILBM, ID_BODY,
                ID_ILBM, ID_DLTA
              };

              if( !(error = StopChunks( iff, (LONG *)stopchunks, NUM_STOPCHUNKS )) )
              {
                struct FrameNode *fn         = NULL;
                ULONG             numcmaps   = 0UL; /* number of created cmaps  */

                /* Scan IFF stream until an error or an EOF occurs */
                for( ;; )
                {
                  struct ContextNode *cn;

                  if( error = ParseIFF( iff, IFFPARSE_SCAN ) )
                  {
                    /* EOF (End Of File) is no error here... */
                    if( error == IFFERR_EOF )
                    {
                      error = 0L;
                    }

                    break;
                  }

                  /* Get file position */
                  if( cn = CurrentChunk( iff ) )
                  {
                    pos = 0UL;

                    while( cn = ParentChunk( cn ) )
                    {
                      pos += cn -> cn_Scan;
                    }
                  }

                  /* bmhd header loaded ? */
                  if( bmhdprop == NULL )
                  {
                    if( bmhdprop = FindProp( iff, ID_ILBM, ID_BMHD ) )
                    {
                      ULONG poolsize,
                            availmem;

                      *bmh = *((struct BitMapHeader *)(bmhdprop -> sp_Data));

                      animwidth  = bmh -> bmh_Width;
                      animheight = bmh -> bmh_Height;
                      animdepth  = bmh -> bmh_Depth;

                      availmem = AvailMem( MEMF_PUBLIC );

                      /* Create a seperate pool for frames:
                       * (((width + 7) / 8) * height * depth + struct BitMapHeader + Padding) * 4 frames
                       */
                      poolsize = (((animwidth * animheight * animdepth) / 8UL) + 256UL) * 4UL;

                      /* Shrink pool to a fitting size */
                      while( (poolsize * 4) > availmem )
                      {
                        poolsize /= 2UL;
                      }

                      /* Create a memory pool for frame bitmaps */
                      if( !(aid -> aid_FramePool = CreatePool( MEMF_PUBLIC, poolsize, poolsize )) )
                      {
                        error = ERROR_NO_FREE_STORE;
                      }
                    }
                  }

                  /* camg loaded ? */
                  if( camgprop == NULL )
                  {
                    if( camgprop = FindProp( iff, ID_ILBM, ID_CAMG ) )
                    {
                      modeid = *(ULONG *)(camgprop -> sp_Data);

                      /* Check for invalid flags */
                      if( (!(modeid & MONITOR_ID_MASK)) ||
                          ((modeid & EXTENDED_MODE) &&
                          (!(modeid & 0xFFFF0000UL))) )
                      {
                        /* Remove invalid flags (see include31:graphics/view.h) */
                        modeid &= ~(GENLOCK_VIDEO | PFBA | GENLOCK_AUDIO | DUALPF | EXTENDED_MODE | VP_HIDE | SPRITES );
                      }

                      /* Be safe ! */
                      if( (modeid & 0xFFFF0000UL) && (!(modeid & 0x00001000UL)) )
                      {
                        modeid = (ULONG)INVALID_ID;
                      }
                    }
                  }

                  /* grab loaded ? */
                  if( grabprop == NULL )
                  {
                    if( grabprop = FindProp( iff, ID_ILBM, ID_GRAB ) )
                    {
                      /* Grab point only available in animation.datatype V41 */
                      if( grabpoint )
                      {
                        *grabpoint = *((struct tPoint *)(grabprop -> sp_Data));
                      }

                      verbose_printf( cb, aid, "animation GRAB point x=%ld, y=%ld\n",
                                      (long)(((struct tPoint *)(grabprop -> sp_Data)) -> x),
                                      (long)(((struct tPoint *)(grabprop -> sp_Data)) -> y) );
                    }
                  }

                  /* dpan loaded ? */
                  if( dpanprop == NULL )
                  {
                    if( dpanprop = FindProp( iff, ID_ILBM, ID_DPAN ) )
                    {
                      if( (aid -> aid_FPS) == 0UL )
                      {
                        struct DPAnimChunk *dpan = (struct DPAnimChunk *)(dpanprop -> sp_Data);
                        
                        if( (dpan -> dpan_FPS) <= 60UL )
                        {
                          aid -> aid_FPS = dpan -> dpan_FPS;

                          verbose_printf( cb, aid, "DPAN found, FPS set to %lu\n", (aid -> aid_FPS) );
                        }
                        else
                        {
                          verbose_printf( cb, aid, "DPAN found, ignoring invalid FPS value %lu\n", (ULONG)(dpan -> dpan_FPS) );
                        }
                      }
                    }
                  }

                  if( annoprop == NULL )
                  {
                    /* IFF ANNO found ? */
                    if( annoprop = FindProp( iff, ID_ILBM, ID_ANNO ) )
                    {
                      STRPTR buff;

                      /* Allocate a temp buffer so that stccpy can add a '\0'-terminator */
                      if( buff = (STRPTR)AllocVec( ((annoprop -> sp_Size) + 2UL), MEMF_ANY ) )
                      {
                        stccpy( buff, (annoprop -> sp_Data), (int)(annoprop -> sp_Size) );

                        verbose_printf( cb, aid, "ANNO annotation: \"%s\"\n", buff );

                        SetDTAttrs( o, NULL, NULL, DTA_ObjAnnotation, buff, TAG_DONE );

                        FreeVec( buff );
                      }
                      else
                      {
                        /* no temp. buffer */
                        error = ERROR_NO_FREE_STORE;
                      }
                    }
                  }

                  if( authprop == NULL )
                  {
                    /* IFF AUTH found ? */
                    if( authprop = FindProp( iff, ID_ILBM, ID_AUTH ) )
                    {
                      STRPTR buff;

                      /* Allocate a temp buffer so that stccpy can add a '\0'-terminator */
                      if( buff = (STRPTR)AllocVec( ((authprop -> sp_Size) + 2UL), MEMF_ANY ) )
                      {
                        stccpy( buff, (authprop -> sp_Data), (int)(authprop -> sp_Size) );

                        verbose_printf( cb, aid, "AUTH author: \"%s\"\n", buff );

                        SetDTAttrs( o, NULL, NULL, DTA_ObjAuthor, buff, TAG_DONE );

                        FreeVec( buff );
                      }
                      else
                      {
                        /* no temp. buffer */
                        error = ERROR_NO_FREE_STORE;
                      }
                    }
                  }

                  if( copyrightprop == NULL )
                  {
                    /* IFF (C) found ? */
                    if( copyrightprop = FindProp( iff, ID_ILBM, ID_Copyright ) )
                    {
                      STRPTR buff;

                      /* Allocate a temp buffer so that stccpy can add a '\0'-terminator */
                      if( buff = (STRPTR)AllocVec( ((copyrightprop -> sp_Size) + 2UL), MEMF_ANY ) )
                      {
                        stccpy( buff, (copyrightprop -> sp_Data), (int)(copyrightprop -> sp_Size) );

                        verbose_printf( cb, aid, "(C) copyright: \"%s\"\n", buff );

                        SetDTAttrs( o, NULL, NULL, DTA_ObjCopyright, buff, TAG_DONE );

                        FreeVec( buff );
                      }
                      else
                      {
                        /* no temp. buffer */
                        error = ERROR_NO_FREE_STORE;
                      }
                    }
                  }

                  if( fverprop == NULL )
                  {
                    /* IFF FVER found ? */
                    if( fverprop = FindProp( iff, ID_ILBM, ID_FVER ) )
                    {
                      STRPTR buff;

                      /* Allocate a temp buffer so that stccpy can add a '\0'-terminator */
                      if( buff = (STRPTR)AllocVec( ((fverprop -> sp_Size) + 2UL), MEMF_ANY ) )
                      {
                        stccpy( buff, (fverprop -> sp_Data), (int)(fverprop -> sp_Size) );

                        verbose_printf( cb, aid, "FVER version: \"%s\"\n", buff );

                        SetDTAttrs( o, NULL, NULL, DTA_ObjVersion, buff, TAG_DONE );

                        FreeVec( buff );
                      }
                      else
                      {
                        /* no temp. buffer */
                        error = ERROR_NO_FREE_STORE;
                      }
                    }
                  }

                  if( nameprop == NULL )
                  {
                    /* IFF NAME found ? */
                    if( nameprop = FindProp( iff, ID_ILBM, ID_NAME ) )
                    {
                      STRPTR buff;

                      /* Allocate a temp buffer so that stccpy can add a '\0'-terminator */
                      if( buff = (STRPTR)AllocVec( ((nameprop -> sp_Size) + 2UL), MEMF_ANY ) )
                      {
                        stccpy( buff, (nameprop -> sp_Data), (int)(nameprop -> sp_Size) );

                        verbose_printf( cb, aid, "NAME name: \"%s\"\n", buff );

                        SetDTAttrs( o, NULL, NULL, DTA_ObjName, buff, TAG_DONE );

                        FreeVec( buff );
                      }
                      else
                      {
                        /* no temp. buffer */
                        error = ERROR_NO_FREE_STORE;
                      }
                    }
                  }

                  if( cn = CurrentChunk( iff ) )
                  {
                    switch( (cn -> cn_Type) )
                    {
                      case ID_ILBM:
                      {
                          switch( (cn -> cn_ID) )
                          {
                            case ID_FORM:
                            {
                                /* Create an prepare a new frame node */
                                if( fn = AllocFrameNode( cb, (aid -> aid_Pool) ) )
                                {
                                  AddTail( (struct List *)(&(aid -> aid_FrameList)), (struct Node *)(&(fn -> fn_Node)) );

                                  fn -> fn_TimeStamp = timestamp++;
                                  fn -> fn_Frame     = fn -> fn_TimeStamp;
                                  
                                  fn -> fn_PrevFrame = fn;
                                }
                                else
                                {
                                  /* can't alloc frame node */
                                  error = ERROR_NO_FREE_STORE;
                                }
                            }
                                break;

                            case ID_ANHD:
                            {
                                if( fn )
                                {
                                  ULONG interleave;

                                  /* Read struct AnimHeader */
                                  error = ReadChunkBytes( iff, (&(fn -> fn_AH)), (LONG)sizeof( struct AnimHeader ) );
                                  if( error == (LONG)sizeof( struct AnimHeader ) ) error = 0L;

                                  /* Info */
                                  DumpAnimHeader( cb, aid, (fn -> fn_TimeStamp), (&(fn -> fn_AH)) );

                                  /* Check if we have dynamic timing */
                                  maxreltime = MAX( maxreltime, (fn -> fn_AH . ah_RelTime) );
                                  minreltime = MIN( minreltime, (fn -> fn_AH . ah_RelTime) );

                                  interleave = (ULONG)(fn -> fn_AH . ah_Interleave);

                                  /* An interleave of 0 means two frames back */
                                  if( interleave == 0 )
                                  {
                                    interleave = 2;
                                  }

                                  /* Get previous frame */
                                  fn -> fn_PrevFrame = GetPrevFrameNode( fn, interleave );
                                }
                            }
                                break;

                            case ID_CMAP:
                            {
                                if( fn )
                                {
                                  UBYTE *buff;

                                  /* Allocate buffer */
                                  if( buff = (UBYTE *)AllocVecPooled( cb, (aid -> aid_Pool), ((cn -> cn_Size) + 16UL) ) )
                                  {
                                    /* Load CMAP data */
                                    error = ReadChunkBytes( iff, buff, (cn -> cn_Size) );

                                    /* All read ? */
                                    if( error == (cn -> cn_Size) )
                                    {
                                      error = 0L; /* Success ! */

                                      if( timestamp == 1UL )
                                      {
                                        if( !CMAP2Object( cb, o, buff, (cn -> cn_Size) ) )
                                        {
                                          /* can't alloc object's color table */
                                          error = ERROR_NO_FREE_STORE;
                                        }
                                      }

                                      /* Any failure ? */
                                      if( error == 0L )
                                      {
                                        if( aid -> aid_NoCMAPs )
                                        {
                                          error = 0L; /* Success ! */
                                        }
                                        else
                                        {
                                          if( fn -> fn_CMap = CMAP2ColorMap( cb, aid, buff, (cn -> cn_Size) ) )
                                          {
                                            error = 0L; /* Success ! */
                                            numcmaps++;
                                          }
                                          else
                                          {
                                            /* no colormap */
                                            error = ERROR_NO_FREE_STORE;
                                          }
                                        }
                                      }
                                    }

                                    FreeVecPooled( cb, (aid -> aid_Pool), buff );
                                  }
                                  else
                                  {
                                    /* no load buff */
                                    error = ERROR_NO_FREE_STORE;
                                  }
                                }
                            }
                                break;

                            case ID_BODY:
                            case ID_DLTA:
                            {
                                if( fn )
                                {
                                  /* Store position of DLTA (pos points to the DLTA ID) */
                                  fn -> fn_BMOffset = pos;
                                  fn -> fn_BMSize   = cn -> cn_Size;

                                  if( (fn -> fn_BitMap) == NULL )
                                  {
                                    /* Preload frames only if requested or if this is the key frame (first frame of anim) */
                                    if( (aid -> aid_LoadAll) || ((fn -> fn_TimeStamp) == 0UL) )
                                    {
                                      if( animwidth && animheight && animdepth )
                                      {
                                        if( fn -> fn_BitMap = AllocBitMapPooled( cb, animwidth, animheight, animdepth, (aid -> aid_FramePool) ) )
                                        {
                                          UBYTE *buff;

                                          /* Allocate buffer */
                                          if( buff = (UBYTE *)AllocVecPooled( cb, (aid -> aid_Pool), ((cn -> cn_Size) + 32UL) ) )
                                          {
                                            struct FrameNode *prevfn;

                                            /* Clear buffer to get rid of some problems with corrupted DLTAs */
                                            memset( (void *)buff, 0, (size_t)((cn -> cn_Size) + 31UL) );

                                            /* Get previous frame */
                                            prevfn = fn -> fn_PrevFrame;

                                            /* Load delta data */
                                            error = ReadChunkBytes( iff, buff, (cn -> cn_Size) );

                                            /* All bytes read ? */
                                            if( error == (cn -> cn_Size) )
                                            {
                                              error = DrawDLTA( cb, aid, (prevfn -> fn_BitMap), (fn -> fn_BitMap), (&(fn -> fn_AH)), buff, (cn -> cn_Size) );

                                              if( error )
                                              {
                                                error_printf( cb, aid, "scan/load: dlta unpacking error %lu\n", error );
                                              }
                                            }

                                            FreeVecPooled( cb, (aid -> aid_Pool), buff );
                                          }
                                          else
                                          {
                                            /* no load buff */
                                            error = ERROR_NO_FREE_STORE;
                                          }
                                        }
                                        else
                                        {
                                          /* no bitmap */
                                          error = ERROR_NO_FREE_STORE;
                                        }
                                      }
                                      else
                                      {
                                        /* no dimensions for bitmap (possibly a missing bmhd) */
                                        error = DTERROR_NOT_ENOUGH_DATA;
                                      }
                                    }
                                  }
                                  else
                                  {
                                    error_printf( cb, aid, "scan/load: bitmap already loaded\n" );
                                  }
                                }
                            }
                                break;
                          }
                      }
                          break;
                    }
                  }

                  /* on error: leave for-loop */
                  if( error )
                  {
                    break;
                  }
                }

                /* Any frames ? */
                if( timestamp && (error == 0L) && numcmaps )
                {
                  if( numcmaps == 1UL )
                  {
                    /* We only have a global colormap and no colormap changes,
                     * delete first colormap (a colormap in the first frames indicates following colormap
                     * changes)
                     */
                    struct FrameNode *firstnode = (struct FrameNode *)(aid -> aid_FrameList . mlh_Head);

                    if( firstnode -> fn_CMap )
                    {
                      FreeColorMap( (firstnode -> fn_CMap) );
                      firstnode -> fn_CMap = NULL;
                    }
                  }
                  else
                  {
                    /* All frames must have a colormap, therefore we replicate the colormap
                     * from the previous colormap if one is missing
                     */
                    struct FrameNode *worknode,
                                     *nextnode;
                    struct ColorMap  *currcm = NULL;

                    verbose_printf( cb, aid, "Animation has palette changes per frame\n" );

                    worknode = (struct FrameNode *)(aid -> aid_FrameList . mlh_Head);

                    while( nextnode = (struct FrameNode *)(worknode -> fn_Node . mln_Succ) )
                    {
                      if( worknode -> fn_CMap )
                      {
                        /* Current node contains colormap, this are the colors for the following frames... */
                        currcm = worknode -> fn_CMap;
                      }
                      else
                      {
                        if( currcm )
                        {
                          /* Copy colormap from previous one... */
                          if( !(worknode -> fn_CMap = CopyColorMap( cb, currcm )) )
                          {
                            /* Can't copy/alloc colormap */
                            error = ERROR_NO_FREE_STORE;
                          }
                        }
                        else
                        {
                          verbose_printf( cb, aid, "scan/load: no colormap, can't copy it\n" );
                        }
                      }

                      worknode = nextnode;
                    }
                  }
                }
              }
            }

            /* Check for required information */
            if( error == 0L )
            {
              /* bmh information available  ? */
              if( bmhdprop == NULL )
              {
                verbose_printf( cb, aid, "scan: no bmhd found\n" );

                /* BMHD missing */
                error = DTERROR_INVALID_DATA;
              }
              else
              {
                /* Any frames loaded ? */
                if( timestamp == 0UL )
                {
                  /* not enougth frames (at least one required) */
                  error = DTERROR_NOT_ENOUGH_DATA;
                }
              }
            }

            /* Dynamic timing ? */
            if( (minreltime != maxreltime) && ((aid -> aid_NoDynamicTiming) == FALSE) )
            {
              struct FrameNode *worknode,
                               *nextnode;
              ULONG             shift = 0UL;    
              
              if( minreltime == 0UL )
              {
                shift = 1UL;
              }

              verbose_printf( cb, aid, "using dynamic timing\n" );

              /* Renumber timestamps */
              timestamp = 0UL; /* again we count from 0 */

              worknode = (struct FrameNode *)(aid -> aid_FrameList . mlh_Head);

              while( nextnode = (struct FrameNode *)(worknode -> fn_Node . mln_Succ) )
              {
                ULONG duration = (worknode -> fn_AH . ah_RelTime) + shift - 1UL;

                worknode -> fn_TimeStamp = timestamp;
                worknode -> fn_Frame     = timestamp;
                worknode -> fn_Duration  = duration;

                timestamp += (duration + 1UL);

                worknode = nextnode;
              }
            }

            /* Any error ? */
            if( error == 0L )
            {
              /* Alloc bitmap as key bitmap  */
              if( aid -> aid_KeyBitMap = AllocBitMap( animwidth, animheight, animdepth, (BMF_CLEAR | BMF_MINPLANES), NULL ) )
              {
                struct FrameNode *firstfn = (struct FrameNode *)(aid -> aid_FrameList . mlh_Head); /* short cut to the first FrameNode */
                
                aid -> aid_CurrFN = firstfn;

                if( (firstfn -> fn_BitMap) == NULL )
                {
                  if( !(firstfn -> fn_BitMap = AllocBitMapPooled( cb, (ULONG)(aid -> aid_BMH -> bmh_Width), (ULONG)(aid -> aid_BMH -> bmh_Height), (ULONG)(aid -> aid_BMH -> bmh_Depth), (aid -> aid_FramePool) )) )
                  {
                    /* can't alloc first bitmap */
                    error = ERROR_NO_FREE_STORE;
                  }
                }

                if( error == 0L )
                {
                  /* Copy first frame into key bitmap */
                  CopyBitMap( cb, (firstfn -> fn_BitMap), (aid -> aid_KeyBitMap) );

                  /* No name chunk ? */
                  if( nameprop == NULL )
                  {
                    STRPTR name;

                    GetDTAttrs( o, DTA_Name, (&name), TAG_DONE );
                    SetDTAttrs( o, NULL, NULL, DTA_ObjName, name, TAG_DONE );
                  }

                  /* ModeID... */
                  if( (aid -> aid_ModeID) != INVALID_ID )
                  {
                    modeid = aid -> aid_ModeID;
                  }
                  else
                  {
                    /* No mode id ? */
                    if( modeid == INVALID_ID )
                    {
                      /* BUG: Does currently not support SUPERHIRES modes */
                      if( animwidth >= 640UL )
                      {
                        if( animheight >= 400 )
                        {
                          modeid = HIRESLACE_KEY;
                        }
                        else
                        {
                          modeid = HIRES_KEY;
                        }
                      }
                      else
                      {
                        if( animheight >= 400 )
                        {
                          modeid = LORESLACE_KEY;
                        }
                        else
                        {
                          modeid = LORES_KEY;
                        }
                      }
                    }
                  }

                  /* No FPS rate found ? */
                  if( (aid -> aid_FPS) == 0UL )
                  {
                    ULONG adaptive;

                    aid -> aid_FPS = 10UL; /* should be 60 (e.g. 1/60 sec per frame) */

                    if( GetDTAttrs( o, ADTA_AdaptiveFPS, (&adaptive), TAG_DONE ) == 1UL )
                    {
                      if( adaptive )
                      {
                        verbose_printf( cb, aid, "animation.datatype uses adaptive playback rate selection\n" );

                        aid -> aid_FPS = 60UL; /* 1/60 sec per frame */
                      }
                    }
                  }

                  /* Attach external sound */
                  AttachSample( cb, aid );

                  /* Infos */
                  verbose_printf( cb, aid, "width %lu height %lu depth %lu frames %lu fps %lu\n",
                                animwidth,
                                animheight,
                                animdepth,
                                timestamp,
                                (aid -> aid_FPS) );

                  /* Set misc attributes */
                  SetDTAttrs( o, NULL, NULL,
                              DTA_TotalHoriz,       animwidth,
                              DTA_TotalVert,        animheight,
                              ADTA_Width,           animwidth,
                              ADTA_Height,          animheight,
                              ADTA_Depth,           animdepth,
                              ADTA_ModeID,          modeid,
                              ADTA_Frames,          timestamp,
                              ADTA_FramesPerSecond, (aid -> aid_FPS),
                              ADTA_KeyFrame,        (aid -> aid_KeyBitMap),
                              ADTA_Sample,          (firstfn -> fn_Sample),
                              ADTA_SampleLength,    (firstfn -> fn_SampleLength),
                              ADTA_Period,          (firstfn -> fn_Period),
                              ADTA_Volume,          (aid -> aid_Volume),
                              ADTA_Cycles,          1UL,
                              TAG_DONE );
                }
              }
              else
              {
                /* can't alloc key bitmap */
                error = ERROR_NO_FREE_STORE;
              }
            }
          }
          else
          {
            /* No iff 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;
    }

    /* Error codes below 0 are related to the IFFParse.library functions */
    if( error < 0L )
    {
      verbose_printf( cb, aid, "iff error %ld\n", (long)error );

      /* convert IFFParse error to DOS error */
      error = ifferr2doserr[ (-error - 1) ];
    }

    return( error );
}


static
struct FrameNode *AllocFrameNode( struct ClassBase *cb, APTR pool )
{
    struct FrameNode *fn;

    if( fn = (struct FrameNode *)AllocPooled( pool, (ULONG)sizeof( struct FrameNode ) ) )
    {
      memset( fn, 0, sizeof( struct FrameNode ) );
    }

    return( fn );
}


static
struct FrameNode *FindFrameNode( struct MinList *fnl, ULONG timestamp )
{
    if( fnl )
    {
      struct FrameNode *worknode,
                       *nextnode,
                       *prevnode;

      prevnode = worknode = (struct FrameNode *)(fnl -> mlh_Head);

      while( nextnode = (struct FrameNode *)(worknode -> fn_Node . mln_Succ) )
      {
        if( (worknode -> fn_TimeStamp) > timestamp )
        {
          return( prevnode );
        }

        prevnode = worknode;
        worknode = nextnode;
      }

      if( !IsListEmpty( ((struct List *)fnl) ) )
      {
        return( prevnode );
      }  
    }

    return( NULL );
}


static
void FreeFrameNodeResources( struct ClassBase *cb, struct MinList *fnl )
{
    if( fnl )
    {
      struct FrameNode *worknode,
                       *nextnode;

      worknode = (struct FrameNode *)(fnl -> mlh_Head);

      while( nextnode = (struct FrameNode *)(worknode -> fn_Node . mln_Succ) )
      {
        if( worknode -> fn_CMap )
        {
          FreeColorMap( (worknode -> fn_CMap) );
          worknode -> fn_CMap = NULL;
        }

        worknode = nextnode;
      }
    }
}


/* Copy bm1 to bm2 */
static
void CopyBitMap( struct ClassBase *cb, struct BitMap *bm1, struct BitMap *bm2 )
{
    ULONG  bpr1 = bm1 -> BytesPerRow;
    ULONG  bpr2 = bm2 -> BytesPerRow;

    /* Same bitmap layout ? */
    if( bpr1 == bpr2 )
    {
      /* Interleaved BitMap ? */
      if( ((bm1 -> Planes[ 1 ]) - (bm1 -> Planes[ 0 ])) == (bpr1 / (ULONG)(bm1 -> Depth)) )
      {
        ULONG planesize = bpr2 * (ULONG)(bm2 -> Rows);

        XCopyMem( cb, (bm1 -> Planes[ 0 ]), (bm2 -> Planes[ 0 ]), planesize );
      }
      else
      {
        ULONG planesize = bpr2 * (ULONG)(bm2 -> Rows);
        UWORD i;

        for( i = 0U ; i < (bm2 -> Depth) ; i++ )
        {
          XCopyMem( cb, (bm1 -> Planes[ i ]), (bm2 -> Planes[ i ]), planesize );
        }
      }
    }
    else
    {
      register UBYTE *src;
      register UBYTE *dst;
      register LONG   r;
      register LONG   p;
               ULONG  width = bm1 -> BytesPerRow;

      /* Interleaved BitMap ? */
      if( ((bm1 -> Planes[ 1 ]) - (bm1 -> Planes[ 0 ])) == (bpr1 / (ULONG)(bm1 -> Depth)) )
      {
        width /= (bm1 -> Depth);
      }

      for( p = bm1 -> Depth - 1 ; p >= 0 ; p-- )
      {
        src = (BYTE *)bm1 -> Planes[ p ];
        dst = (BYTE *)bm2 -> Planes[ p ];

        for( r = bm1 -> Rows - 1 ; r >= 0 ; r-- )
        {
          CopyMem( src, dst, width );
          src += bpr1;
          dst += bpr2;
        }
      }
    }
}


static
void XCopyMem( struct ClassBase *cb, APTR src, APTR dest, ULONG size )
{
    /* Check if we can use the optimized CopyMemQuick */
    if( (ALIGN_LONG( src ) == src) && (ALIGN_LONG( dest ) == dest) )
    {
      register ULONG lsize = size & ~3UL,
                     cut   = size - lsize; /* remaining bytes (0-3) */

      CopyMemQuick( src, dest, lsize );

      if( cut )
      {
        src  = ((UBYTE *)src)  + lsize;
        dest = ((UBYTE *)dest) + lsize;

        CopyMem( src, dest, cut );
      }
    }
    else
    {
      CopyMem( src, dest, size );
    }
}


static
void ClearBitMap( struct BitMap *bm )
{
    if( bm )
    {
      ULONG planesize = (ULONG)(bm -> BytesPerRow) * (ULONG)(bm -> Rows);
      UWORD i;

      for( i = 0U ; i < (bm -> Depth) ; i++ )
      {
        memset( (bm -> Planes[ i ]), 0, (size_t)planesize );
      }
    }
}


/* XOR Bitmaps op1 ^= op2 */
static
void XORBitMaps( struct BitMap *op1, struct BitMap *op2 )
{
    if( op1 && op2 )
    {
               ULONG  planesize = (ULONG)(op1 -> BytesPerRow) * (ULONG)(op1 -> Rows);
               ULONG  missing;
               ULONG  i;
      register ULONG  j;
      register ULONG *op1p, /* op1 planes */
                     *op2p; /* op2 planes */

      planesize = planesize / sizeof( ULONG ); /* op1p and op2p are ULONGs, not BYTES... */
      missing   = planesize % sizeof( ULONG ); /* missing bytes */

      for( i = 0U ; i < (op1 -> Depth) ; i++ )
      {
        j = planesize;

        op1p = (ULONG *)(op1 -> Planes[ i ]);
        op2p = (ULONG *)(op2 -> Planes[ i ]);

        while( j-- )
        {
          *op1p++ ^= *op2p++;
        }

        if( missing )
        {
          register UBYTE *op1px = (UBYTE *)op1p;
          register UBYTE *op2px = (UBYTE *)op2p;

          j = missing;

          while( j-- )
          {
            *op1px++ ^= *op2px++;
          }
        }
      }
    }
}


static
struct BitMap *AllocBitMapPooled( struct ClassBase *cb, ULONG width, ULONG height, ULONG depth, APTR pool )
{
    struct BitMap *bm;
    ULONG          planesize,
                   moredepthsize,
                   size;

    planesize       = (ULONG)RASSIZE( ((width + 63UL) & ~63UL), height );
    moredepthsize   = (depth > 8UL)?((depth - 8UL) * sizeof( PLANEPTR )):(0UL);
    size            = ((ULONG)sizeof( struct BitMap )) + moredepthsize + (planesize * depth) + 31UL;

    if( bm = (struct BitMap *)AllocVecPooled( cb, pool, size ) )
    {
      UWORD    pl;
      PLANEPTR plane;

      InitBitMap( bm, depth, width, height );

      plane = ALIGN_QUADLONG( (PLANEPTR)(bm + 1) ); /* First plane follows struct BitMap */

      /* Set up plane data */
      pl = 0U;

      /* Set up plane ptrs */
      while( pl < depth )
      {
        bm -> Planes[ pl ] = plane;

        plane = ALIGN_QUADLONG( (PLANEPTR)(((UBYTE *)plane) + planesize) );
        pl++;
      }

      /* Clear the remaining plane ptrs (up to 8 planes) */
      while( pl < 8U )
      {
        bm -> Planes[ pl ] = NULL;

        pl++;
      }
    }

    return( bm );
}


static
BOOL CMAP2Object( struct ClassBase *cb, Object *o, UBYTE *rgb, ULONG rgbsize )
{
    struct ColorRegister *acm;
    ULONG                *acregs;
    ULONG                 nc;

    /* file has this many colors (e.g. each color has one byte per R,B,G-gun) */
    nc = rgbsize / 3UL;

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

    /* Get color context */
    if( GetDTAttrs( o,
                    ADTA_ColorRegisters, (&acm),
                    ADTA_CRegs,          (&acregs),
                    ADTA_NumColors,      (&nc),
                    TAG_DONE ) == 3UL )
    {
      /* All valid ? */
      if( acm && acregs && nc )
      {
        ULONG i;

        for( i = 0UL ; i < nc ; i++, acm++ )
        {
          acm -> red   =  *rgb++;
          acm -> green =  *rgb++;
          acm -> blue  =  *rgb++;

          /* Replicate the color information.
           * This surrounds an OS bug which uses the low-oreder bytes of the 32-bit colors
           * instead of the high order ones
           */
          acregs[ ((i * 3) + 0) ] = ((ULONG)(acm -> red))   * 0x01010101UL;
          acregs[ ((i * 3) + 1) ] = ((ULONG)(acm -> green)) * 0x01010101UL;
          acregs[ ((i * 3) + 2) ] = ((ULONG)(acm -> blue))  * 0x01010101UL;
        }

        return( TRUE );
      }
    }

    return( FALSE );
}


static
struct ColorMap *CMAP2ColorMap( struct ClassBase *cb, struct AnimInstData *aid, UBYTE *rgb, ULONG rgbsize )
{
    struct ColorMap *cm;
    ULONG            a_nc   = (1UL << (ULONG)(aid -> aid_BMH -> bmh_Depth)); /* Number of colors in animation */
    ULONG            rgb_nc = rgbsize / 3UL;                                 /* Number of colors in CMAP      */

    /* Get a colormap which hold all colors */
    if( cm = GetColorMap( (long)MAX( a_nc, rgb_nc ) ) )
    {
      ULONG i,
            r, g, b;

      for( i = 0UL ; i < rgb_nc ; i++ )
      {
        r = *rgb++;
        g = *rgb++;
        b = *rgb++;

        /* Replicate color information (see CMAP2Object for details) and store them into colormap */
        SetRGB32CM( cm, i, (r * 0x01010101UL), (g * 0x01010101UL), (b * 0x01010101UL) );
      }

      /* BUG: the remaining entries should be filled with colors from the last colormap */
      for( ; i < a_nc ; i++ )
      {
        SetRGB32CM( cm, i, 0UL, 0UL, 0UL ); /* fill remaining entries with black */
      }
    }

    return( cm );
}


static
struct ColorMap *CopyColorMap( struct ClassBase *cb, struct ColorMap *src )
{
    struct ColorMap *dest = NULL;

    if( src )
    {
      ULONG *ctable;

      if( ctable = (ULONG *)AllocVec( ((ULONG)(src -> Count) * sizeof( ULONG ) * 3UL), MEMF_PUBLIC ) )
      {
        if( dest = GetColorMap( (long)(src -> Count) ) )
        {
          ULONG i;

          GetRGB32( src, 0UL, (ULONG)(src -> Count), ctable );

          for( i = 0UL ; i < (src -> Count) ; i++ )
          {
            SetRGB32CM( dest, i, ctable[ ((i * 3) + 0) ], ctable[ ((i * 3) + 1) ], ctable[ ((i * 3) + 2) ] );
          }
        }

        FreeVec( ctable );
      }
    }

    return( dest );
}


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

#define DEBUG_POOLS 1

#ifndef DEBUG_POOLS
static
APTR AllocVecPooled( struct ClassBase *cb, APTR pool, ULONG memsize )
{
    ULONG *memory = NULL;

    if( pool && memsize )
    {
      memsize += (ULONG)sizeof( ULONG );

      if( memory = (ULONG *)AllocPooled( pool, memsize ) )
      {
        (*memory) = memsize;

        memory++;
      }
    }

    return( (APTR)memory );
}


static
void FreeVecPooled( struct ClassBase *cb, APTR pool, APTR mem )
{
    if( pool && mem )
    {
      ULONG *memory;

      memory = (ULONG *)mem;

      memory--;

      FreePooled( pool, memory, (*memory) );
    }
}
#else
#define MY_MAGIC_MEMID (0xD0ADEAD0UL)

static
APTR AllocVecPooled( struct ClassBase *cb, APTR pool, ULONG memsize )
{
    ULONG *memory = NULL;

    if( pool && memsize )
    {
      memsize += (ULONG)sizeof( ULONG );
      memsize += (ULONG)sizeof( APTR );

      if( memory = (ULONG *)AllocPooled( pool, memsize ) )
      {
        (*memory) = memsize;
        memory++;

        (*memory) = (ULONG)pool;
        memory++;

        (*memory) = MY_MAGIC_MEMID;
        memory++;
      }
    }

    return( (APTR)memory );
}


static
void FreeVecPooled( struct ClassBase *cb, APTR pool, APTR mem )
{
    if( pool && mem )
    {
      ULONG *memory;

      memory = (ULONG *)mem;

      memory--;

      if( *memory != MY_MAGIC_MEMID )
      {
        D( kprintf( "wrong mem id\n" ) );
        return;
      }

      memory--;

      if( *memory != (ULONG)pool )
      {
        D( kprintf( "wrong pool %lx %lx\n", pool, *memory ) );
        return;
      }

      memory--;

      FreePooled( pool, memory, (*memory) );
    }
}
#endif /* DEBUG_POOLS */


static
LONG DrawDLTA( struct ClassBase *cb, struct AnimInstData *aid, struct BitMap *prevbm, struct BitMap *bm, struct AnimHeader *ah, UBYTE *dlta, ULONG dltasize )
{
    LONG error = 0L;

    if( bm && ah && dlta && dltasize )
    {
      struct BitMap       *unpackbm = bm,
                          *tempbm   = NULL;
      struct BitMapHeader *bmh      = aid -> aid_BMH;
      BOOL                 DoXOR;

      /* Handle acmpILBM, acmpXORILBM and acmpAnimJ explicitly */
      switch( ah -> ah_Operation )
      {
        case acmpILBM:    /*  0  */
        {
            /* unpack ILBM BODY */
            return( LoadILBMBody( cb, unpackbm, bmh, dlta, dltasize ) );
        }

        case acmpXORILBM: /*  1  */
        {
            error_printf( cb, aid, "\adlta: acmpXORILBM disabled, call author immediately\n" );
            return( ERROR_NOT_IMPLEMENTED );
        }

        case acmpAnimJ:   /* 'J' */
        {
            /* unpack ANIM-J  */
            return( unpackanimjdelta( cb, dlta, dltasize, prevbm, bm ) );
        }
      }

      /* XOR ? */
      DoXOR = ((ah -> ah_Flags) & ahfXOR);

      if( !DoXOR )
      {
        if( (aid -> aid_NoDPaintBrushPatch) == FALSE )
        {
          /* DPaint anim brush (compatibility hack) */
          if( ((ah -> ah_Operation) == acmpByteDelta) && ((ah -> ah_Interleave) == 1U) )
          {
            DoXOR = TRUE;
          }
        }
      }

      /* Prepare XOR (see below) */
      if( DoXOR && prevbm )
      {
        if( prevbm == bm )
        {
          if( !(tempbm = AllocBitMapPooled( cb, (ULONG)(aid -> aid_BMH -> bmh_Width), (ULONG)(aid -> aid_BMH -> bmh_Height), (ULONG)(aid -> aid_BMH -> bmh_Depth), (aid -> aid_FramePool) )) )
          {
            return( ERROR_NO_FREE_STORE );
          }

          unpackbm = prevbm = tempbm;
        }

        ClearBitMap( unpackbm );
      }
      else
      {
        if( prevbm )
        {
          if( prevbm != bm )
          {
            CopyBitMap( cb, prevbm, bm );
          }
        }
        else
        {
          ClearBitMap( bm );
        }
      }

      /* dispatch compression type, second attempt */
      switch( ah -> ah_Operation )
      {
        /* acmpILBM, acmpXORILBM and acmpAnimJ have been processed above */

        case acmpLongDelta:         /* 2 */
        {
            error = unpacklongdelta( unpackbm, dlta, dltasize );
        }
            break;

        case acmpShortDelta:        /* 3 */
        {
            error = unpackshortdelta( unpackbm, dlta, dltasize );
        }
            break;

        case acmpDelta:             /*  4 */
        {
#ifdef COMMENTED_OUT
            if( (ah -> ah_Flags) & ahfLongData )
            {
              error = unpackanim4longdelta( unpackbm, dlta, dltasize, (ah -> ah_Flags) );
            }
            else
            {
              error = unpackanim4worddelta( unpackbm, dlta, dltasize, (ah -> ah_Flags) );
            }
#else
            error_printf( cb, aid, "\adlta: acmpDelta disabled, call author (gisburn@w-specht.rhein-ruhr.de)\n"
                                   "immediately. If you are this FIRST user who send me a VALID IFF ANIM-4 compressed animation\n"
                                   "you'll get $10 US-Dollar !!\n THIS IS NO JOKE !!\n" );
            error = ERROR_NOT_IMPLEMENTED;
#endif /* COMMENTED_OUT */
        }
            break;

        case acmpByteDelta:         /* 5 */
        case acmpStereoByteDelta:   /* 6 */
        {
            error = unpackbytedelta( unpackbm, dlta, dltasize );
        }
            break;

        case acmpAnim7:             /* 7 */
        {
            if( (ah -> ah_Flags) & ahfLongData )
            {
              error = unpackanim7longdelta( unpackbm, dlta, dltasize );
            }
            else
            {
              error = unpackanim7worddelta( unpackbm, dlta, dltasize );
            }
        }
            break;

        case acmpAnim8:             /* 8 */
        {
            if( (ah -> ah_Flags) & ahfLongData )
            {
              error = unpackanim8longdelta( unpackbm, dlta, dltasize );
            }
            else
            {
              error = unpackanim8worddelta( unpackbm, dlta, dltasize );
            }
        }
            break;

        default:                    /* 'l' */
        {
            error_printf( cb, aid, "\adlta: anim compression %ld not implemented yet\n", (long)(ah -> ah_Operation) );
            error = ERROR_NOT_IMPLEMENTED;
        }
            break;
      }

      /* Handle XOR (see above) */
      if( DoXOR && prevbm )
      {
        XORBitMaps( bm, prevbm );
      }

      if( tempbm )
      {
        FreeVecPooled( cb, (aid -> aid_FramePool), tempbm );
      }
    }

    return( error );
}


static
void DumpAnimHeader( struct ClassBase *cb, struct AnimInstData *aid, ULONG ti, struct AnimHeader *anhd )
{
    if( anhd )
    {
      verbose_printf( cb, aid, "%4lu: ", ti );

      switch( anhd -> ah_Operation )
      {
        case acmpILBM:              verbose_printf( cb, aid, "Operation ILBM" );                  break;
        case acmpXORILBM:           verbose_printf( cb, aid, "Operation XORILBM" );               break;
        case acmpLongDelta:         verbose_printf( cb, aid, "Operation LongDelta" );             break;
        case acmpShortDelta:        verbose_printf( cb, aid, "Operation ShortDelta" );            break;
        case acmpDelta:             verbose_printf( cb, aid, "Operation Delta" );                 break;
        case acmpByteDelta:         verbose_printf( cb, aid, "Operation ByteDelta" );             break;
        case acmpStereoByteDelta:   verbose_printf( cb, aid, "Operation StereoByteDelta" );       break;
        case acmpAnim7:             verbose_printf( cb, aid, "Operation Anim7" );                 break;
        case acmpAnim8:             verbose_printf( cb, aid, "Operation Anim8" );                 break;
        case acmpAnimJ:             verbose_printf( cb, aid, "Operation AnimJ" );                 break;
        default:                    verbose_printf( cb, aid, "Operation <unknown compression>" ); break;
      }

      verbose_printf( cb, aid, " AbsTime %3lu RelTime %3lu Interleave %3lu", (anhd -> ah_AbsTime), (anhd -> ah_RelTime), (ULONG)(anhd -> ah_Interleave) );

      if( (anhd -> ah_Flags) & ahfLongData          ) verbose_printf( cb, aid, " LongData"          );
      if( (anhd -> ah_Flags) & ahfXOR               ) verbose_printf( cb, aid, " XOR"               );
      if( (anhd -> ah_Flags) & ahfOneInfoList       ) verbose_printf( cb, aid, " OneInfoList"       );
      if( (anhd -> ah_Flags) & ahfRLC               ) verbose_printf( cb, aid, " RLC"               );
      if( (anhd -> ah_Flags) & ahfVertical          ) verbose_printf( cb, aid, " Vertical"          );
      if( (anhd -> ah_Flags) & ahfLongInfoOffsets   ) verbose_printf( cb, aid, " LongInfoOffsets"   );

      verbose_printf( cb, aid, "\n" );

      if( (aid -> aid_NoDPaintBrushPatch) == FALSE )
      {
        /* DPaint anim brush (compatibility hack) */
        if( (anhd -> ah_Operation == acmpByteDelta) && ((anhd -> ah_Interleave) == 1U) && (!((anhd -> ah_Flags) & ahfXOR)) )
        {
          verbose_printf( cb, aid, "Assuming XOR mode for this frame (DPaint brush patch, see docs)\n" );
        }
      }
    }
}


static
struct FrameNode *GetPrevFrameNode( struct FrameNode *currfn, ULONG interleave )
{
    struct FrameNode *worknode,
                     *prevnode;

    /* Get previous frame */
    worknode = currfn;

    while( prevnode = (struct FrameNode *)(worknode -> fn_Node . mln_Pred) )
    {
      if( (interleave-- == 0U) || ((prevnode -> fn_Node . mln_Pred) == NULL) )
      {
        break;
      }

      worknode = prevnode;
    }

    return( worknode );
}


static
void OpenLogfile( struct ClassBase *cb, struct AnimInstData *aid )
{
    if( (aid -> aid_VerboseOutput) == NULL )
    {
      STRPTR confile;

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

        aid -> aid_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 AnimInstData *aid, STRPTR format, ... )
{
    OpenLogfile( cb, aid );

    if( aid -> aid_VerboseOutput )
    {
      VFPrintf( (aid -> aid_VerboseOutput), format, (APTR)((&format) + 1) );
    }
}


static
void verbose_printf( struct ClassBase *cb, struct AnimInstData *aid, STRPTR format, ... )
{
    if( aid -> aid_VerboseOutput )
    {
      VFPrintf( (aid -> aid_VerboseOutput), format, (APTR)((&format) + 1) );
    }
}


static
void AttachSample( struct ClassBase *cb, struct AnimInstData *aid )
{
    if( aid -> aid_Sample )
    {
      struct FrameNode *worknode,
                       *nextnode;

      ULONG             period          = aid -> aid_Period;
      ULONG             samplesperframe;
      BYTE             *sample          = aid -> aid_Sample;

      samplesperframe = (((SysBase -> ex_EClockFrequency) * 10UL) / (period * (aid -> aid_FPS) * 2UL));

      if( aid -> aid_SamplesPerFrame )
      {
        period = (period * samplesperframe) / (aid -> aid_SamplesPerFrame);

        samplesperframe = aid -> aid_SamplesPerFrame;

        verbose_printf( cb, aid, "period corrected from %lu to %lu to match spf=%lu with fps=%lu\n",
                        (aid -> aid_Period), period, samplesperframe, (aid -> aid_FPS) );
      }

      verbose_printf( cb, aid, "Attching samples (sysclock %lu period %lu fps %lu length %lu samplesperframe %lu)...\n",
                      (SysBase -> ex_EClockFrequency), period, (aid -> aid_FPS), (aid -> aid_SampleLength), samplesperframe );

      worknode = (struct FrameNode *)(aid -> aid_FrameList . mlh_Head);

      while( nextnode = (struct FrameNode *)(worknode -> fn_Node . mln_Succ) )
      {
        worknode -> fn_Sample       = sample;
        worknode -> fn_SampleLength = samplesperframe * ((worknode -> fn_Duration) + 1UL);
        worknode -> fn_Period       = period;

        sample += (samplesperframe * ((worknode -> fn_Duration) + 1UL));

        /* End of sample reached ? */
        if( (ULONG)(sample - (aid -> aid_Sample)) > (aid -> aid_SampleLength) )
        {
          /* Cut last size of sample to fit */
          worknode -> fn_SampleLength -= (ULONG)(sample - (aid -> aid_Sample));

          break;
        }

        worknode = nextnode;
      }
    }
}


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

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

      struct BitMapHeader *bmh;
      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_BitMapHeader,     (&bmh),
                         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 ) == 11UL )
      {
        struct TagItem     *tstate,
                           *ti;
        struct AnimContext *ac;
        struct IFFHandle   *iff;
        struct BitMapHeader xbmh   = *bmh;
        BOOL                planar       = MAKEBOOL( GetBitMapAttr( keyframe, BMA_FLAGS ) & BMF_STANDARD );
        BOOL                interleaved  = MAKEBOOL( GetBitMapAttr( keyframe, BMA_FLAGS ) & BMF_INTERLEAVED );

        if( planar && (!interleaved) )
        {
          xbmh . bmh_Compression = cmpNone; /* Currently we only write an uncompressed key frame */

          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, aid, "saving iff anim %lu %lu %lu\n", startframe, numframes, framestep );

          if( numframes )
          {
            if( ac = CreateAnimContext( cb, animwidth, animheight, animdepth ) )
            {
              if( iff = CreateDOSIFFHandle( cb, (dtw -> dtw_FileHandle) ) )
              {
                if( !(error = StartIFFAnim3( cb, aid, iff, ac, (&xbmh), modeid, cregs, numcolors, numframes, fps, keyframe )) )
                {
                  struct adtFrame  alf;
                  ULONG            timestamp,
                                  *cmap_cregs = NULL;

                  /* Start scan through animation */
                  for( timestamp = startframe ; numframes > 0UL ; timestamp += framestep, numframes-- )
                  {
                    /* On error break */
                    if( error )
                    {
                      break;
                    }

                    /* Check for CTRL_D signal... */
                    if( SetSignal( 0UL, 0UL ) & SIGBREAKF_CTRL_D )
                    {
                      error = ERROR_BREAK;

                      break;
                    }

                    /* reset method msg */
                    memset( (void *)(&alf), 0, sizeof( struct adtFrame ) );

                    /* load frame */
                    alf . MethodID = ADTM_LOADFRAME;
                    alf . alf_TimeStamp = timestamp;
                    alf . alf_Frame     = timestamp;

                    if( DoMethodA( o, (Msg)(&alf) ) == 0UL )
                    {
                      error = IoErr();

                      if( error != ERROR_OBJECT_NOT_FOUND )
                      {
                        break;
                      }
                    }
                    else
                    {
                      /* print frame contents */
                      verbose_printf( cb, aid, "frame: timestamp %lu frame %lu duration %lu bitmap %lx cmap %lx sample %lx len %lu period %lu\n",
                              timestamp,
                              (alf . alf_Frame),
                              (alf . alf_Duration),
                              (alf . alf_BitMap),
                              (alf . alf_CMap),
                              (alf . alf_Sample),
                              (alf . alf_SampleLength),
                              (alf . alf_Period) );

                      if( alf . alf_CMap )
                      {
                        if( cmap_cregs = (ULONG *)AllocVec( (((sizeof( ULONG ) * 3UL) + 1UL) * numcolors), MEMF_PUBLIC ) )
                        {
                          GetRGB32( (alf . alf_CMap), 0UL, numcolors, cmap_cregs );
                        }
                        else
                        {
                          error_printf( cb, aid, "can't alloc dynamic palette buffer\n" );
                          error = ERROR_NO_FREE_STORE;
                        }
                      }

                      if( alf . alf_BitMap )
                      {
                        if( error = WriteIFFAnim3( cb, iff, ac, ((timestamp * 60UL) / fps), (60UL / fps), (&xbmh), cmap_cregs, numcolors, (alf . alf_BitMap) ) )
                        {
                          error_printf( cb, aid, "error while writing IFF ANIM-3, aborted\n" );
                        }
                      }

                      if( cmap_cregs )
                      {
                        FreeVec( cmap_cregs );
                        cmap_cregs = NULL;
                      }
                    }

                    alf . MethodID = ADTM_UNLOADFRAME;
                    DoMethodA( o, (Msg)(&alf) );
                  }

                  EndIFFAnim3( cb, aid, iff );

                  if( error == 0L )
                  {
                    retval = 1UL; /* success ! */
                  }
                }

                FreeIFF( iff );
              }
              else
              {
                error = IoErr();
              }

              DeleteAnimContext( cb, ac );
            }
            else
            {
              /* Can't alloc animcontext */
              error = ERROR_NO_FREE_STORE;
            }
          }
        }  
        else
        {
          /* Does not support non-planar or planar interleaved bitmaps */
          error = ERROR_NOT_IMPLEMENTED;
        }
      }
      else
      {
        error_printf( cb, aid, "not enougth attributes\n" );
      }
    }

    /* Error codes below 0 are related to the IFFParse.library functions */
    if( error < 0L )
    {
      /* convert IFFParse error to DOS error */
      error = ifferr2doserr[ (-error - 1) ];
    }

    SetIoErr( error );

    return( retval );
}


static
struct IFFHandle *CreateDOSIFFHandle( struct ClassBase *cb, BPTR fh )
{
    struct IFFHandle *iff;

    if( iff = AllocIFF() )
    {
      iff -> iff_Stream = (ULONG)fh;

      InitIFFasDOS( iff );
    }

    return( iff );
}


static
LONG StartIFFAnim3( struct ClassBase *cb, struct AnimInstData *aid, struct IFFHandle *iff, struct AnimContext *ac, struct BitMapHeader *bmh, ULONG modeid, ULONG *cregs, ULONG numcolors, ULONG numframes, ULONG fps, struct BitMap *bm )
{
    LONG error;

    if( !(error = OpenIFF( iff, IFFF_WRITE )) )
    {
      for( ;; ) /* not a loop, used as a jump table */
      {
        if( error = PushChunk( iff, ID_ANIM, ID_FORM, IFFSIZE_UNKNOWN ) )
          break;

        /* write initial FORM ILBM */
        {
          if( error = PushChunk( iff, ID_ILBM, ID_FORM, IFFSIZE_UNKNOWN ) )
            break;

          /* write ILBM BMHD (BitMapHeader) */
          {
            if( error = PushChunk( iff, 0UL, ID_BMHD, IFFSIZE_UNKNOWN ) )
              break;

            if( WriteChunkBytes( iff, (APTR)bmh, sizeof( struct BitMapHeader ) ) != sizeof( struct BitMapHeader ) )
            {
              error = IFFERR_WRITE;
              break;
            }

            if( error = PopChunk( iff ) )
              break;
          }

          /* write ILBM CMAP (global color map) */
          if( error = PutILBMCMAP( cb, iff, cregs, numcolors ) )
            break;

          /* write ILBM DPAN chunk */
          {
            struct DPAnimChunk dpan;

            dpan . dpan_Version = 0U; /* ??? */
            dpan . dpan_nframes = numframes;
            dpan . dpan_FPS     = fps;

            if( error = PushChunk( iff, 0UL, ID_DPAN, IFFSIZE_UNKNOWN ) )
              break;

            if( WriteChunkBytes( iff, (APTR)(&dpan), sizeof( struct DPAnimChunk ) ) != sizeof( struct DPAnimChunk ) )
            {
              error = IFFERR_WRITE;
              break;
            }

            if( error = PopChunk( iff ) )
              break;
          }

          /* write ILBM CAMG (amiga view mode) */
          {
            if( error = PushChunk( iff, 0UL, ID_CAMG, IFFSIZE_UNKNOWN ) )
              break;

            if( WriteChunkBytes( iff, (APTR)(&modeid), sizeof( ULONG ) ) != sizeof( ULONG ) )
            {
              error = IFFERR_WRITE;
              break;
            }

            if( error = PopChunk( iff ) )
              break;
          }

          /* Write ILBM BODY (if there is one...) */
          if( bm )
          {
            if( error = PutILBMBody( cb, iff, bm, bmh ) )
              break;

            /* Copy current bitmap into both buffers */
            CopyBitMap( cb, bm, CurrFrame( cb, ac ) );
            SwapFrames( cb, ac );
            CopyBitMap( cb, bm, CurrFrame( cb, ac ) );
          }

          if( error = PopChunk( iff ) )
            break;
        }

        break; /* end of jump table */
      }

      /* All headers written successfully ? */
      if( error == 0L )
      {
        /* Success ! */
        return( 0L );
      }

      CloseIFF( iff );
    }

    return( error );
}


static
void EndIFFAnim3( struct ClassBase *cb, struct AnimInstData *aid, struct IFFHandle *iff )
{
    if( iff )
    {
      LONG error;

      if( error = PopChunk( iff ) )
      {
        error_printf( cb, aid, "error while popping IFF ANIM-3 FORM %ld\n", error );
      }

      CloseIFF( iff );

      verbose_printf( cb, aid, "IFF ANIM-3 sucessfully created\n" );
    }
}


static
LONG WriteIFFAnim3( struct ClassBase *cb, struct IFFHandle *iff, struct AnimContext *ac, ULONG abstime, ULONG reltime, struct BitMapHeader *bmh, ULONG *cregs, ULONG numcolors, struct BitMap *bm )
{
    LONG error = 0L;

    for( ;; ) /* not a loop, used as a jump-table */
    {
      /* write FORM ILBM */
      {
        if( error = PushChunk( iff, ID_ILBM, ID_FORM, IFFSIZE_UNKNOWN ) )
          break;

        /* write ILBM ANHD (AnimHeader) */
        {
          struct AnimHeader animheader = { 0 };

          animheader . ah_Operation  = acmpShortDelta;
          animheader . ah_AbsTime    = abstime;
          animheader . ah_RelTime    = reltime;
          animheader . ah_Interleave = 2U;

          if( error = PushChunk( iff, 0UL, ID_ANHD, IFFSIZE_UNKNOWN ) )
            break;

          if( WriteChunkBytes( iff, (APTR)(&animheader), sizeof( struct AnimHeader ) ) != sizeof( struct AnimHeader ) )
          {
            error = IFFERR_WRITE;
            break;
          }

          if( error = PopChunk( iff ) )
            break;
        }

        /* Palette change ? */
        if( cregs && numcolors )
        {
          if( error = PutILBMCMAP( cb, iff, cregs, numcolors ) )
            break;
        }

        /* Write ANIM-3 delta */
        if( error = PutAnim3Delta( cb, iff, ac, CurrFrame( cb, ac ), bm ) )
          break;

        /* Copy current bitmap into buffer, then swap to next frame */
        CopyBitMap( cb, bm, CurrFrame( cb, ac ) );
        SwapFrames( cb, ac );

        if( error = PopChunk( iff ) )
          break;
      }

      break; /* end of jump-table */
    }

    return( error );
}


static
LONG PutAnim3Delta( struct ClassBase *cb, struct IFFHandle *iff, struct AnimContext *ac, struct BitMap *prevbm, struct BitMap *bm )
{
    LONG    error;
    ULONG   len;

    ULONG  *ptrs = (ULONG *)(ac -> ac_WorkBuffer);
    WORD   *data = (WORD *)(ptrs + 8UL);  /* data space starts after the 8 byte pointers... */
    UBYTE   plane;
    ULONG   planesize = ((bm -> BytesPerRow) * (bm -> Rows)) / sizeof( WORD ); /* size of plane,
                                                                                * in compression units (WORD).
                                                                                */

    memset( (ac -> ac_WorkBuffer), 0, (size_t)(ac -> ac_WorkBufferSize) );

    for( plane = 0U ; plane < (bm -> Depth) ; plane++ )
    {
      WORD   distance = 0,
             i;
      WORD  *planedata     = (WORD *)(bm -> Planes[ plane ]),
            *prevplanedata = (WORD *)(prevbm -> Planes[ plane ]);

      ptrs[ plane ] = (ULONG)((UBYTE *)data - (UBYTE *)(ac -> ac_WorkBuffer));

      for( i = 0 ; i < planesize ; i++ )
      {
        if( (planedata[ i ] != prevplanedata[ i ]) || (distance > 30000) )
        {
          if( FALSE /*planedata[ (i + 1) ] != prevplanedata[ (i + 1) ]*/ )
          {
            WORD *len;

            *data++ = -distance - 1;
            len = data++;

            *len = 0;

            while( (planedata[ i ] != prevplanedata[ i ]) && (i < planesize) && (*len < 30000))
            {
              *data++ = planedata[ i ];
              i++;
              (*len)++;
            }

            i--;
          }
          else
          {
            *data++ = distance;
            *data++ = planedata[ i ];
          }

          distance = 1;
        }
        else
        {
          distance++;
        }
      }

      /* No changes ? */
      if( ptrs[ plane ] == (ULONG)((UBYTE *)data - (UBYTE *)(ac -> ac_WorkBuffer)) )
      {
        ptrs[ plane ] = 0UL;
      }
      else
      {
        *data++ = (WORD)0xFFFF; /* terminator */
      }
    }

    if( error = PushChunk( iff, 0UL, ID_DLTA, IFFSIZE_UNKNOWN ) )
      return( error );

    len = (ULONG)((UBYTE *)data - (UBYTE *)(ac -> ac_WorkBuffer));

    if( WriteChunkBytes( iff, (APTR)(ac -> ac_WorkBuffer), len ) != len )
    {
      return( IFFERR_WRITE );
    }

    error = PopChunk( iff );

    return( error );
}


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

/* write ILBM CMAP  */
static
LONG PutILBMCMAP( struct ClassBase *cb, struct IFFHandle *iff, ULONG *cregs, ULONG numcolors )
{
    long                 error;
    ULONG                i;
    struct ColorRegister cm;

    if( error = PushChunk( iff, 0UL, ID_CMAP, IFFSIZE_UNKNOWN ) )
      return( error );

    for( i = 0UL ; i < numcolors ; i++ )
    {
      /* reduce colors from 32 bits to cmap's 8 bit-per-gun */
      cm . red   = (UBYTE)(cregs[ ((i * 3) + 0) ] >> 24UL);
      cm . green = (UBYTE)(cregs[ ((i * 3) + 1) ] >> 24UL);
      cm . blue  = (UBYTE)(cregs[ ((i * 3) + 2) ] >> 24UL);

      /* Write R, B, G bytes */
      if( WriteChunkBytes( iff, (APTR)(&cm), 3UL ) != 3UL )
      {
        return( IFFERR_WRITE );
      }
    }

    return( PopChunk( iff ) );
}


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

/* from IFF example code ("iffp/ilbm.h") */
#define RowBytes( w )     ((((w) + 15) >> 4) << 1)
#define RowBits( w )      ((((w) + 15) >> 4) << 4)


static
LONG PutILBMBody( struct ClassBase *cb, struct IFFHandle *iff, struct BitMap *bitmap, struct BitMapHeader *bmh )
{
    LONG     error;
    LONG     rowBytes        = bitmap -> BytesPerRow;           /* for source modulo only  */
    LONG     FileRowBytes    = RowBytes( (bmh -> bmh_Width) );  /* width to write in bytes */
    ULONG    planeCnt        = bmh -> bmh_Depth;                /* number of bit planes including mask */
    ULONG    iPlane,
             iRow;
    BYTE    *planes[ 24 ]; /* array of ptrs to planes & mask */

    /* Copy the ptrs to bit & mask planes into local array "planes" */
    for( iPlane = 0 ; iPlane < planeCnt; iPlane++ )
       planes[ iPlane ] = (BYTE *)(bitmap -> Planes[ iPlane ]);

    /* Write out a BODY chunk header */
    if( error = PushChunk( iff, 0L, ID_BODY, IFFSIZE_UNKNOWN ) )
      return( error );

    /* Write out the BODY contents */
    for( iRow = bmh -> bmh_Height ; iRow > 0 ; iRow-- )
    {
      for( iPlane = 0 ; iPlane < planeCnt ; iPlane++ )
      {
        /* Write next row.*/
        if( WriteChunkBytes( iff, planes[ iPlane ], FileRowBytes ) != FileRowBytes )
          return( IFFERR_WRITE );

        planes[ iPlane ] += rowBytes; /* Possibly skipping unused bytes */
      }
    }

    /* Finish the chunk */
    error = PopChunk( iff );

    return( error );
}


static
struct AnimContext *CreateAnimContext( struct ClassBase *cb, ULONG width, ULONG height, ULONG depth )
{
    APTR pool;

    if( pool = CreatePool( (MEMF_PUBLIC | MEMF_CLEAR), 1024UL, 1024UL ) )
    {
      struct AnimContext *ac;

      if( ac = (struct AnimContext *)AllocVecPooled( cb, pool, sizeof( struct AnimContext ) ) )
      {
        /* Alloc two bitmaps, used for interleaving */
        ac -> ac_BitMap[ 0 ] = AllocBitMapPooled( cb, width, height, depth, pool );
        ac -> ac_BitMap[ 1 ] = AllocBitMapPooled( cb, width, height, depth, pool );

        /* Alloc work buffer large enougth to hold fancy things... */

        ac -> ac_WorkBufferSize = (width * height * depth * 2UL) + 1024UL;
        ac -> ac_WorkBuffer     = (UBYTE *)AllocVecPooled( cb, pool, (ac -> ac_WorkBufferSize) );

        if( (ac -> ac_BitMap[ 0 ]) && (ac -> ac_BitMap[ 1 ]) && (ac -> ac_WorkBuffer) )
        {
          return( ac );
        }
      }

      /* Something goes wrong here, get rid of the resources allocated */
      DeletePool( pool );
    }

    return( NULL );
}


#if 0
static
struct BitMap *PrevFrame( struct ClassBase *cb, struct AnimContext *ac )
{
    if( ac )
    {
      return( (ac -> ac_BitMap[ ((ac -> ac_WhichBitMap) ^ 1) ]) );
    }

    return( NULL );
}
#endif


static
void SwapFrames( struct ClassBase *cb, struct AnimContext *ac )
{
    if( ac )
    {
      ac -> ac_WhichBitMap ^= 1; /* Toggle buffer */
    }
}


static
struct BitMap *CurrFrame( struct ClassBase *cb, struct AnimContext *ac )
{
    if( ac )
    {
      return( (ac -> ac_BitMap[ (ac -> ac_WhichBitMap) ]) );
    }

    return( NULL );
}


static
void DeleteAnimContext( struct ClassBase *cb, struct AnimContext *ac )
{
    if( ac )
    {
      DeletePool( (ac -> ac_Pool) );
    }
}



