

/*
**
**  $VER: dumpdtanim.c 1.3 (5.1.97)
**  dumpdtanim 1.3
**
**  main file
**
**  (C) Copyright 1996/97 by Roland 'Gizzy' Mainz
**          All Rights Reserved
**
*/


/* amiga includes */
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <exec/libraries.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <datatypes/datatypes.h>
#include <datatypes/datatypesclass.h>
#include <datatypes/soundclass.h>
#include <datatypes/pictureclass.h>
#include <datatypes/animationclass.h>
#include <workbench/workbench.h>

/* amiga prototypes */
#include <clib/macros.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/iffparse_protos.h>
#include <clib/icon_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <clib/datatypes_protos.h>
#include <clib/dtclass_protos.h>
#include <clib/alib_protos.h>

/* amiga pragmas */
#include <pragmas/exec_pragmas.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/iffparse_pragmas.h>
#include <pragmas/icon_pragmas.h>
#include <pragmas/graphics_pragmas.h>
#include <pragmas/intuition_pragmas.h>
#include <pragmas/datatypes_pragmas.h>
#include <pragmas/dtclass_pragmas.h>

/* ansi includes */
#include <string.h>

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

/* IFF FILM ID */
#ifndef ID_FILM
#define ID_FILM MAKE_ID( 'F', 'I', 'L', 'M' )
#endif /* !ID_FILM */

/* CAT CELL, a single IFF FILM frame (bitmap and sound) */
#ifndef ID_CELL
#define ID_CELL MAKE_ID( 'C', 'E', 'L', 'L' )
#endif /* !ID_CELL */

/* body, used by CELLs ILBM and 8SVX */
#ifndef ID_BODY
#define ID_BODY MAKE_ID( 'B', 'O', 'D', 'Y' )
#endif /* !ID_BODY */

/* IFF ILBM */
#ifndef ID_ILBM
#define ID_ILBM MAKE_ID( 'I', 'L', 'B', 'M' )
#endif /* !ID_ILBM */

/* ILBM BMHD (bitmap header) */
#ifndef ID_BMHD
#define ID_BMHD MAKE_ID( 'B', 'M', 'H', 'D' )
#endif /* !ID_BMHD */

/* ILBM CMAP (color map) */
#ifndef ID_CMAP
#define ID_CMAP MAKE_ID( 'C', 'M', 'A', 'P' )
#endif /* !ID_CMAP */

/* ILBM CAMG (view mode id) */
#ifndef ID_CAMG
#define ID_CAMG MAKE_ID( 'C', 'A', 'M', 'G' )
#endif /* !ID_CMAP */

/* IFF 8SVX */
#ifndef ID_8SVX
#define ID_8SVX MAKE_ID( '8', 'S', 'V', 'X' )
#endif /* !ID_8SVX */

/* 8SVX VHDR (VoiceHeader) */
#ifndef ID_VHDR
#define ID_VHDR MAKE_ID( 'V', 'H', 'D', 'R' )
#endif /* !ID_VHDR */

/* 8SVX CHAN (left/right/stereo channel) */
#ifndef ID_CHAN
#define CHAN_RIGHT  (4L)
#define CHAN_LEFT   (2L)
#define CHAN_STEREO (6L)  /* CHAN_RIGHT + CHAN_LEFT */

#define ID_CHAN MAKE_ID( 'C', 'H', 'A', 'N' )
#endif /* !ID_CHAN */

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

/* from <iffp/ilbm.h>*/
/* BMHD flags */

/* Advisory that 8 significant bits-per-gun have been stored in CMAP
 * i.e. that the CMAP is definitely not 4-bit values shifted left.
 * This bit will disable nibble examination by color loading routine.
 */
#define BMHDB_CMAPOK (7U)
#define BMHDF_CMAPOK (1U << BMHDB_CMAPOK)

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

extern struct ExecBase  *SysBase;
extern struct Library   *DOSBase;
       struct Library   *IFFParseBase;
       struct Library   *IconBase;
       struct Library   *GfxBase;
       struct Library   *IntuitionBase;
       struct Library   *DataTypesBase;

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


#define TEMPLATE "NAME/A,"                           \
                 "MOVIE/S,"                          \
                 "STARTFRAME/K/N,"                   \
                 "STOPFRAME/K/N,"                    \
                 "SKIPFRAMES/K/N,"                   \
                 "IFFFILM=FILM/K,"                   \
                 "CLIPIFFFILM=CLIPFILM/S,"           \
                 "CLIPIFFFILMUNIT=CLIPFILMUNIT/K/N," \
                 "PICTUREPERFRAME/S,"                \
                 "SAMPLEPERFRAME=SPF/S,"             \
                 "GLOBALSAMPLE/S"

#define OPT_NAME             (0)
#define OPT_MOVIE            (1)
#define OPT_STARTFRAME       (2)
#define OPT_STOPFRAME        (3)
#define OPT_SKIPFRAMES       (4)
#define OPT_IFFFILM          (5)
#define OPT_CLIPIFFFILM      (6)
#define OPT_CLIPIFFFILMUNIT  (7)
#define OPT_PICTUREPERFRAME  (8)
#define OPT_SAMPLEPERFRAME   (9)
#define OPT_GLOBALSAMPLE    (10)
#define NUM_OPTS            (11)

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


/* prototypes */
long              main( void );

struct IFFHandle *OpenIFFFile( STRPTR, ULONG );
BOOL              CloseIFFFile( struct IFFHandle * );

struct IFFHandle *OpenIFFClipboard( LONG );
void              CloseIFFClipboard( struct IFFHandle * );

LONG              StartIFFFilm( struct IFFHandle *, struct BitMapHeader *, ULONG, struct ColorRegister *, ULONG, struct VoiceHeader * );
void              EndIFFFilm( struct IFFHandle * );
LONG              WriteIFFFilmCell( struct IFFHandle *, struct BitMapHeader *, struct BitMap *, BYTE *, ULONG );
LONG              Put8SVXBody( struct IFFHandle *, BYTE *, ULONG );
LONG              PutILBMBody( struct IFFHandle *, struct BitMap *, struct BitMapHeader * );

ULONG             SaveDTObject( Object *, struct Window *, struct Requester *, STRPTR, ULONG, struct TagItem * );
void              SaveSample( STRPTR, BYTE *, ULONG, ULONG, ULONG );

void              FillColorRegisters( struct ColorRegister *, ULONG *, ULONG );
void              CopyBitMap( struct BitMap *, struct BitMap * );
void              mysprintf( STRPTR, STRPTR , ... );

BOOL              IsInterleavedBitMap( struct BitMap * );

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

/* IFF errors to DOS errors */
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 */
};

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


long main( void )
{
    /* Argument parsing variables */
    LONG                    options[ NUM_OPTS ];
    struct RDArgs          *rdargs;
    LONG                    error = 0L;

    memset( options, 0, sizeof( options ) );

    IFFParseBase     = OpenLibrary( "iffparse.library",  39UL );
    IconBase         = OpenLibrary( "icon.library",      39UL );
    GfxBase          = OpenLibrary( "graphics.library",  39UL );
    IntuitionBase    = OpenLibrary( "intuition.library", 39UL );
    DataTypesBase    = OpenLibrary( "datatypes.library", 39UL );

    if( IFFParseBase && IconBase && GfxBase && IntuitionBase && DataTypesBase )
    {
      if( rdargs = ReadArgs( TEMPLATE, options, NULL ) )
      {
        STRPTR              filepart    = FilePart( (STRPTR)options[ OPT_NAME ] );
        ULONG               startframe  = 0UL,  /* start at this frame                  */
                            stopframe   = ~0UL, /* stop at this frame                   */
                            skipframes  = 1UL;  /* skip n frames during scan            */

        struct IFFHandle   *ifffilm     = NULL, /* IFF FILM output stream (file)        */
                           *clipifffilm = NULL; /* IFF FILM output stream (clipboard)   */

        Object             *o;                  /* anim/movie object */

        if( options[ OPT_STARTFRAME ] )
        {
          startframe = *((LONG *)options[ OPT_STARTFRAME ]);
        }

        if( options[ OPT_STOPFRAME ] )
        {
          stopframe = *((LONG *)options[ OPT_STOPFRAME ]);

          if( stopframe < 1UL )
          {
            stopframe = 1UL;
          }
        }

        if( options[ OPT_SKIPFRAMES ] )
        {
          skipframes = *((LONG *)options[ OPT_SKIPFRAMES ]);

          if( skipframes < 1UL )
          {
            skipframes = 1UL;
          }
        }

        /* Open the animation/movie object */
        if( o = NewDTObject( (APTR)options[ OPT_NAME ],

                             /* We will only accept sound DataTypes */
                             DTA_GroupID,      ((options[ OPT_MOVIE ])?(GID_MOVIE):(GID_ANIMATION)),

                             /* No more attributes */
                             TAG_DONE ) )
        {
          struct gpLayout         gpl;

          /* Base information */
          STRPTR                  objname;

          /* Animation related */
          ULONG                   animwidth,              /* width of animation */
                                  animheight,             /* height of animation */
                                  animdepth;              /* depth of animation */
          ULONG                   anumframes = 0UL;       /* number of frames in animation */
          ULONG                   afps = 0UL;

          /* Anim frame loading */
          struct adtFrame         alf;                    /* method msg for ADTM_LOADFRAME */
          ULONG                   timestamp;              /* timestamp to load */

          /* Animation frame picture related */
          ULONG                   amodeid;                /* mode id of animation */
          struct BitMapHeader    *abmh;                   /* bitmapheader of animation */
          ULONG                   anumcolors;             /* number of colors in animation */
          ULONG                  *acregs;                 /* ADTA_CRegs */
          struct ColorRegister   *acm;                    /* ADTA_ColorRegisters struct ColorRegister array */

          TEXT                    picturefilename[ 512 ]; /* buffer for filename when writing */

          /* Anim frame sample related */
          BYTE                   *asample;
          ULONG                   asamplelength = 0UL;

          ULONG                   aperiod;
          ULONG                   acycles;

          TEXT                    samplefilename[ 512 ];

          /* Sample build from all samples */
          BYTE                   *globalsample        = NULL;
          ULONG                   globalsampleoffset  = 0UL;
          ULONG                   globalsamplesize    = 0UL;

          /* Get information about the object */
          if( GetDTAttrs( o, DTA_ObjName,          (&objname),
                             ADTA_Width,           (&animwidth),
                             ADTA_Height,          (&animheight),
                             ADTA_Depth,           (&animdepth),
                             ADTA_Frames,          (&anumframes),
                             ADTA_FramesPerSecond, (&afps),
                             ADTA_ModeID,          (&amodeid),
                             PDTA_BitMapHeader,    (&abmh),
                             ADTA_NumColors,       (&anumcolors),
                             ADTA_CRegs,           (&acregs),
                             ADTA_ColorRegisters,  (&acm),
                             ADTA_Cycles,          (&acycles),
                             ADTA_Period,          (&aperiod),
                             ADTA_Sample,          (&asample),
                             ADTA_SampleLength,    (&asamplelength),
                             TAG_DONE ) != 15UL )
          {
            Printf( "*** warning: not enough attributes to get\n" );
          }

          /* Sound available ? */
          if( asamplelength && aperiod )
          {
            struct VoiceHeader vh;

            vh . vh_OneShotHiSamples     = asamplelength;
            vh . vh_RepeatHiSamples      = 0UL;
            vh . vh_SamplesPerHiCycle    = 0UL;
            vh . vh_SamplesPerSec        = ((SysBase -> ex_EClockFrequency) * 5UL) / aperiod;
            vh . vh_Octaves              = 1UL;
            vh . vh_Compression          = CMP_NONE;
            vh . vh_Volume               = 0x10000UL; /* maximum volume */

            if( options[ OPT_IFFFILM ] )
            {
              if( ifffilm = OpenIFFFile( (STRPTR)options[ OPT_IFFFILM ], MODE_NEWFILE ) )
              {
                if( error = StartIFFFilm( ifffilm, abmh, amodeid, acm, anumcolors, (&vh) ) )
                {
                  Printf( "error while writing IFF FILM to file%ld\n", error );

                  CloseIFFFile( ifffilm );
                  ifffilm = NULL;
                }
                else
                {
                  Printf( "writing IFF FILM stream to file %s\n", (STRPTR)options[ OPT_IFFFILM ] );
                }
              }
              else
              {
                Printf( "can't open iff file\n" );
                error = IoErr();
              }
            }

            if( options[ OPT_CLIPIFFFILM ] )
            {
              LONG unit;

              if( options[ OPT_CLIPIFFFILMUNIT ] )
              {
                unit = *((LONG *)options[ OPT_CLIPIFFFILMUNIT ]);
              }
              else
              {
                unit = PRIMARY_CLIP;
              }

              if( clipifffilm = OpenIFFClipboard( unit ) )
              {
                if( error = StartIFFFilm( clipifffilm, abmh, amodeid, acm, anumcolors, (&vh) ) )
                {
                  Printf( "error while writing IFF FILM to clipboard %ld\n", error );

                  CloseIFFClipboard( clipifffilm );
                  clipifffilm = NULL;
                }
                else
                {
                  Printf( "writing IFF FILM stream to clipboard unit %ld\n", unit );
                }
              }
              else
              {
                Printf( "can't open iff clipboard\n" );
                error = IoErr();
              }
            }

            if( options[ OPT_GLOBALSAMPLE ] )
            {
              globalsamplesize = (((asamplelength + 10UL) * anumframes) + 1024UL);
              globalsample = (BYTE *)AllocVec( globalsamplesize, MEMF_PUBLIC );
            }
          }
          else
          {
            if( options[ OPT_IFFFILM ] || options[ OPT_CLIPIFFFILM ] )
            {
              Printf( "can't create IFF FILM stream because sound is missing\n" );
              error = ERROR_OBJECT_WRONG_TYPE;
            }

            if( options[ OPT_GLOBALSAMPLE ] )
            {
              Printf( "can't create global sample because sound is missing\n" );
              error = ERROR_OBJECT_WRONG_TYPE;
            }
          }

          /* Display any information we obtained */
          if( objname )
          {
            Printf( "Name: \"%s\"\n", objname );
          }

          /* Print info... */
          Printf( "width %lu height %lu depth %lu frames %lu\n"
                  "FramesPerSecond %lu modeid %lx colors %lu\n"
                  "sample %lx samplelength %lu cycles %lu period %lu\n",
                   animwidth, animheight, animdepth, anumframes,
                   afps, amodeid, anumcolors,
                   asample, asamplelength, acycles, aperiod );

          /* "Layout" animation (for those anim classes which need this,
           * __NOT__ recommened)
           */
          gpl . MethodID    = DTM_PROCLAYOUT;
          gpl . gpl_GInfo   = NULL;
          gpl . gpl_Initial = 1L;

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

          /* Start scan through animation */
          for( timestamp = startframe ; (timestamp < anumframes) && (timestamp <= stopframe) ; timestamp += skipframes )
          {
            /* On error break */
            if( error )
            {
              break;
            }

            /* Check for CTRL_C signal... */
            if( SetSignal( 0UL, 0UL ) & SIGBREAKF_CTRL_C )
            {
              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;
            DoMethodA( o, (Msg)(&alf) );

            /* print frame contents */
            Printf( "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) );

            /* handle picture data */
            if( abmh && (alf . alf_BitMap) && options[ OPT_PICTUREPERFRAME ] )
            {
              ULONG          framewidth,
                             frameheight,
                             framedepth;
              struct BitMap *bm;

              /* bitmap dimensions may not match ADTA_(Width|Height|Depth) dimentions */
              framewidth  = GetBitMapAttr( (alf . alf_BitMap), BMA_WIDTH  );
              frameheight = GetBitMapAttr( (alf . alf_BitMap), BMA_HEIGHT );
              framedepth  = GetBitMapAttr( (alf . alf_BitMap), BMA_DEPTH  );

              /* Correct bitmap width if the OS didn't noticed that this is an interleaved bm
               * GetBitMapAttr( bm, BMA_WIDTH ) will return (width * depth) instead of width.
               * The following code should be future-compatible...
               */
              if( (!(GetBitMapAttr( (alf . alf_BitMap), BMA_FLAGS ) & BMF_INTERLEAVED)) &&
                  IsInterleavedBitMap( (alf . alf_BitMap) ) )
              {
                framewidth /= framedepth;
              }

              /* Here we must allocate a temp chip mem bitmap because animation frames can be
               * outside CHIP-MEM and picture.datatype cannot deal with non-chip-mem bitmaps...
               */
              if( bm = AllocBitMap( framewidth, frameheight, framedepth, 0UL, (alf . alf_BitMap) ) )
              {
                Object *po;

                /* Copy bitmap */
                CopyBitMap( bm, (alf . alf_BitMap) );

                /* Create picture object and set it up manually (e.g. DTST_RAM) */
                if( po = NewDTObject( NULL,
                                      DTA_SourceType, DTST_RAM,
                                      DTA_GroupID,    GID_PICTURE,
                                      TAG_DONE ) )
                {
                  struct BitMapHeader *pbmh;

                  if( GetDTAttrs( po, PDTA_BitMapHeader, (&pbmh), TAG_DONE ) )
                  {
                    if( pbmh )
                    {
                      ULONG                *pcregs;
                      struct ColorRegister *pcm;
                      ULONG                 numcolors = (1UL << animdepth), /* note that numcolors
                                                                             * may not match ADTA_NumColors
                                                                             */
                                            allocated_numcolors;

                      /* copy bitmapheader (RAW) from animation bmh... */
                      *pbmh = *abmh;

                      /* ...and fill in attributes which may be different */
                      pbmh -> bmh_Width  = framewidth;
                      pbmh -> bmh_Height = frameheight;
                      pbmh -> bmh_Depth  = framedepth;

                      /* we're writing a full palette, set this flag (currently NOP, but...) */
                      pbmh -> bmh_Pad    = BMHDF_CMAPOK; /* also known as bmh_Flags */

                      /* Set screen mode id,
                       * our bitmap and
                       * the requested number of colors...
                       */
                      SetDTAttrs( po, NULL, NULL,
                                  PDTA_ModeID,      amodeid,
                                  PDTA_NumColors,   numcolors,
                                  PDTA_BitMap,      bm,
                                  TAG_DONE );

                      /* Get color tables (pcregs, pcm) and the allocated number of colors */
                      if( GetDTAttrs( po, PDTA_CRegs,          (&pcregs),
                                          PDTA_ColorRegisters, (&pcm),
                                          PDTA_NumColors,      (&allocated_numcolors),
                                          TAG_DONE ) == 3UL )
                      {
                        /* Success ? */
                        if( pcregs && pcm )
                        {
                          ULONG i;

                          if( allocated_numcolors != numcolors )
                          {
                            Printf( "allocated_numcolors %lu != numcolors %lu\n", allocated_numcolors, numcolors );
                          }

                          /* get colors (either from frame's colormap or from animation's palette) */
                          if( alf . alf_CMap )
                          {
                            GetRGB32( (alf . alf_CMap), 0UL, (allocated_numcolors - 1UL), pcregs );
                          }
                          else
                          {
                            for( i = 0UL ; i < allocated_numcolors ; i++ )
                            {
                              pcregs[ ((i * 3) + 0) ] = acregs[ ((i * 3) + 0) ]; /* copy r, */
                              pcregs[ ((i * 3) + 1) ] = acregs[ ((i * 3) + 1) ]; /*      g, */
                              pcregs[ ((i * 3) + 2) ] = acregs[ ((i * 3) + 2) ]; /*      b  */
                            }
                          }

                          /* copy cregs to colorregisters */
                          FillColorRegisters( pcm, acregs, allocated_numcolors );

                          mysprintf( picturefilename, "%s_picture_%05.5lu.ilbm", filepart, timestamp );
                          Printf( "saving picture: \"%s\"\n", picturefilename );
                          SaveDTObject( po, NULL, NULL, picturefilename, DTWM_IFF, NULL );
                        }
                        else
                        {
                          Printf( "picture object mem err (no color tables)\n" );
                          error = ERROR_NO_FREE_STORE;
                        }
                      }
                      else
                      {
                        /* can't get required args from object */
                        error = ERROR_OBJECT_WRONG_TYPE;
                      }

                      /* picture.datatype object will free the bitmap */
                      bm = NULL;
                    }
                    else
                    {
                      Printf( "can't get bitmapheader\n" );
                      error = ERROR_NO_FREE_STORE;
                    }
                  }
                  else
                  {
                    /* can't get required args from object */
                    Printf( "can't get PDTA_BitMapHeader\n" );
                    error = ERROR_OBJECT_WRONG_TYPE;
                  }

                  /* Get rid of the picture object */
                  DisposeDTObject( po );
                }
                else
                {
                  Printf( "can't create picture object\n" );
                  error = IoErr();
                }

                /* free temp bitmap */
                FreeBitMap( bm );
              }
              else
              {
                Printf( "can't alloc frame bitmap\n" );
                error = ERROR_NO_FREE_STORE;
              }
            }

            /* handle sound/sample data */
            if( alf . alf_Sample )
            {
              if( options[ OPT_SAMPLEPERFRAME ] )
              {
                mysprintf( samplefilename, "%s_sample_%05.5lu.8svx", filepart, timestamp );
                Printf( "saving sample : \"%s\"\n", samplefilename );
                SaveSample( samplefilename, (alf . alf_Sample), (alf . alf_SampleLength), (alf . alf_Period), acycles );
              }

              if( globalsample )
              {
                if( globalsampleoffset < (globalsamplesize - (1000UL + (alf . alf_SampleLength))) )
                {
                  CopyMem( (alf . alf_Sample), (globalsample + globalsampleoffset), (alf . alf_SampleLength) );
                  globalsampleoffset += alf . alf_SampleLength;
                }
                else
                {
                  Printf( "global sample buffer overflow\n" );
                  error = ERROR_BUFFER_OVERFLOW;
                }
              }
            }

            if( ifffilm )
            {
              if( error = WriteIFFFilmCell( ifffilm, abmh, (alf . alf_BitMap), (alf . alf_Sample), (alf . alf_SampleLength) ) )
              {
                Printf( "error while writing IFF FILM, aborted\n" );
                EndIFFFilm( ifffilm );
                CloseIFFFile( ifffilm );
                ifffilm = NULL;
              }
            }

            if( clipifffilm )
            {
              if( error = WriteIFFFilmCell( clipifffilm, abmh, (alf . alf_BitMap), (alf . alf_Sample), (alf . alf_SampleLength) ) )
              {
                Printf( "error while writing IFF FILM to clipboard, aborted\n" );
                EndIFFFilm( clipifffilm );
                CloseIFFClipboard( clipifffilm );
                clipifffilm = NULL;
              }
            }

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

          /* Get rid of the anim/movie object */
          DisposeDTObject( o );

          if( globalsample )
          {
            mysprintf( samplefilename, "%s_sample_global.8svx", filepart );
            SaveSample( samplefilename, globalsample, globalsampleoffset, aperiod, acycles );

            FreeVec( globalsample );
          }

          if( ifffilm )
          {
            EndIFFFilm( ifffilm );
            CloseIFFFile( ifffilm );
          }

          if( clipifffilm )
          {
            EndIFFFilm( clipifffilm );
            CloseIFFClipboard( clipifffilm );
          }
        }
        else
        {
          /* NewDTObject failed */
          error = IoErr();
        }

        /* Free the allocated memory after ReadArgs */
        FreeArgs( rdargs );
      }
      else
      {
        /* ReadArgs failed */
        error = IoErr();
      }
    }
    else
    {
      /* can't open required libraries */
      error = ERROR_OBJECT_NOT_FOUND;
    }

    /* Error ? */
    if( error )
    {
      TEXT errbuff[ 256 ];

      /* IFFParse error code ? */
      if( error < 0L )
      {
        /* convert IFFParse error to DOS error */
        error = ifferr2doserr[ -error - 1 ];
      }

      if( error >= DTERROR_UNKNOWN_DATATYPE )
      {
        mysprintf( errbuff, GetDTString( error ), "file" );
      }
      else
      {
        Fault( error, NULL, errbuff, sizeof( errbuff ) );
      }

      Printf( "dumpdtanim: %s\n", errbuff );
    }

    CloseLibrary( DataTypesBase );
    CloseLibrary( IntuitionBase );
    CloseLibrary( GfxBase );
    CloseLibrary( IconBase );
    CloseLibrary( IFFParseBase );

    SetIoErr( error );

    return( ((error)?(RETURN_ERROR):(RETURN_OK)) );
}


struct IFFHandle *OpenIFFFile( STRPTR file, ULONG mode )
{
    struct IFFHandle *iff;
    LONG              ioerr;

    if( file && mode )
    {
      if( iff = AllocIFF() )
      {
        /* Set up IFF_File for file I/O. */
        if( iff -> iff_Stream = (ULONG)Open( file, mode ) )
        {
          InitIFFasDOS( iff );

          return( iff );
        }
        else
        {
          /* Open failed */
          ioerr = IoErr();
        }

        FreeIFF( iff );
      }
      else
      {
        /* AllocIFF failed */
        ioerr = ERROR_NO_FREE_STORE;
      }
    }
    else
    {
      /* wrong function args */
      ioerr = ERROR_REQUIRED_ARG_MISSING;
    }

    SetIoErr( ioerr );

    return( NULL );
}


BOOL CloseIFFFile( struct IFFHandle *iff )
{
    BOOL success = FALSE;

    if( iff )
    {
      success = Close( (BPTR)(iff -> iff_Stream) );

      FreeIFF( iff );
    }

    return( success );
}


struct IFFHandle *OpenIFFClipboard( LONG unit )
{
    struct IFFHandle *iff;
    LONG              ioerr;

    if( iff = AllocIFF() )
    {
      /* Set up IFF_File for clipboard I/O. */
      if( iff -> iff_Stream = (ULONG)OpenClipboard( unit ) )
      {
        InitIFFasClip( iff );

        return( iff );
      }
      else
      {
        /* couldn't open clipboard */
        ioerr = DTERROR_COULDNT_OPEN_CLIPBOARD;
      }

      FreeIFF( iff );
    }
    else
    {
      /* AllocIFF failed */
      ioerr = ERROR_NO_FREE_STORE;
    }

    SetIoErr( ioerr );

    return( NULL );
}


void CloseIFFClipboard( struct IFFHandle *iff )
{
    if( iff )
    {
      CloseClipboard( (struct ClipboardHandle *)(iff -> iff_Stream) );

      FreeIFF( iff );
    }
}


LONG StartIFFFilm( struct IFFHandle *iff, struct BitMapHeader *bmh, ULONG modeid, struct ColorRegister *cm, ULONG numcolors, struct VoiceHeader *vh )
{
    LONG error;

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

        /* write ILBM props */
        {
          if( error = PushChunk( iff, ID_ILBM, ID_PROP, 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 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 CMAP (global color map) */
          {
            ULONG i;

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

            for( i = 0UL ; i < numcolors ; i++, cm++ )
            {
              /* Write R, B, G bytes */
              if( WriteChunkBytes( iff, (APTR)cm, 3UL ) != 3UL )
              {
                error = IFFERR_WRITE;
                break;
              }
            }

            /* any error in loop above ? */
            if( error )
              break;

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

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

        /* write 8SVX props */
        {
          if( error = PushChunk( iff, ID_8SVX, ID_PROP, IFFSIZE_UNKNOWN ) )
            break;

          {
            /* write 8SVX VHDR (VoiceHeader) */
            {
              if( error = PushChunk( iff, 0UL, ID_VHDR, IFFSIZE_UNKNOWN ) )
                break;

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

              if( error = PopChunk( iff ) )
                break;
            }
          }
          
          /* a 8SVX CHAN chunk is missing here (animation.datatype does currently not support stereo sound) */

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

        break; /* end of jump table */
      }

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

      CloseIFF( iff );
    }

    return( error );
}


void EndIFFFilm( struct IFFHandle *iff )
{
    if( iff )
    {
      LONG error;

      if( error = PopChunk( iff ) )
      {
        Printf( "error while popping IFF FILM LIST %ld\n", error );
      }

      CloseIFF( iff );

      Printf( "IFF FILM sucessfully created\n" );
    }
    else
    {
      Printf( "no iff\n" );
    }
}


LONG WriteIFFFilmCell( struct IFFHandle *iff, struct BitMapHeader *bmh, struct BitMap *bm, BYTE *sample, ULONG samplelength )
{
    LONG error;

    for( ;; ) /* not a loop, used as a jump-table */
    {
      if( error = PushChunk( iff, ID_CELL, ID_CAT, IFFSIZE_UNKNOWN ) )
        break;

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

        /* Write ILBM BODY */
        if( error = PutILBMBody( iff, bm, bmh ) )
          break;

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

      /* write FORM 8SVX */
      {
        if( error = PushChunk( iff, ID_8SVX, ID_FORM, IFFSIZE_UNKNOWN ) )
          break;

        /* Write 8SVX BODY */
        if( error = Put8SVXBody( iff, sample, samplelength ) )
          break;

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

      if( error = PopChunk( iff ) )
        break;

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

    return( error );
}


LONG Put8SVXBody( struct IFFHandle *iff, BYTE *sample, ULONG samplelength )
{
    LONG error;

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

    if( WriteChunkBytes( iff, sample, samplelength ) != samplelength )
      return( IFFERR_WRITE );

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

    return( error );
}


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

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


LONG PutILBMBody( 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, NULL, 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 );
}


/* save datatypes object with matching icon */
ULONG SaveDTObject( Object *dto, struct Window *window, struct Requester *requester, STRPTR filename, ULONG mode, struct TagItem *dtw_AttrList )
{
    ULONG retval = 0UL;

    if( dto && filename )
    {
      struct DataType *dtn;

      /* Get the object type */
      if( GetDTAttrs( dto, DTA_DataType, (&dtn), TAG_DONE ) )
      {
        BPTR fh;

        /* Open new file... */
        if( fh = Open( filename, MODE_NEWFILE ) )
        {
          struct dtWrite dtw;
          LONG           ioerr;

          /* Write... */
          dtw . MethodID       = DTM_WRITE;
          dtw . dtw_GInfo      = NULL;
          dtw . dtw_FileHandle = fh;
          dtw . dtw_Mode       = mode;
          dtw . dtw_AttrList   = dtw_AttrList;

          if( retval = DoDTMethodA( dto, window, requester, (Msg)(&dtw) ) )
          {
            TEXT               buffer[ 256 ];
            struct DiskObject *dobj;

            /* Save IoErr code because following code will clear this */
            ioerr = IoErr();

            if( dtn )
            {
              /* Get the an icon for the file */
              mysprintf( buffer, "ENV:Sys/def_%.240s", (dtn -> dtn_Header -> dth_BaseName) );

              if( (dobj = GetDiskObject( buffer )) == NULL )
              {
                dobj = GetDefDiskObject( WBPROJECT );
              }
            }
            else
            {
              dobj = GetDefDiskObject( WBPROJECT );
            }

            /* If we have an icon, then write it out */
            if( dobj )
            {
              PutDiskObject( filename, dobj );

              FreeDiskObject( dobj );
            }
          }
          else
          {
            /* Save IoErr code because Close will clear this */
            ioerr = IoErr();
          }

          Close( fh );

          SetIoErr( ioerr );
        }
      }
      else
      {
        /* Can't get DTA_DataType */
        SetIoErr( ERROR_NO_FREE_STORE );
      }
    }
    else
    {
      /* wrong function args */
      SetIoErr( ERROR_REQUIRED_ARG_MISSING );
    }

    return( retval );
}


/* save sound sample using a sound.datatype object */
void SaveSample( STRPTR filename, BYTE *sample, ULONG length, ULONG period, ULONG cycles )
{
    Object *so;

    /* Create sound object and set it up manually (e.g. DTST_RAM) */
    if( so = NewDTObject( NULL,
                          DTA_SourceType,    DTST_RAM,
                          DTA_GroupID,       GID_SOUND,
                          SDTA_Sample,       sample,
                          SDTA_SampleLength, MAX( 1UL, length ),
                          SDTA_Period,       period,
                          SDTA_Cycles,       (ULONG)MAX( cycles, 1UL ),
                          TAG_DONE ) )
    {
      struct VoiceHeader *vh;

      /* Set up voiceheader, if possible... */
      if( GetDTAttrs( so, SDTA_VoiceHeader, (&vh), TAG_DONE ) )
      {
        if( vh )
        {
          vh -> vh_OneShotHiSamples     = length;
          vh -> vh_RepeatHiSamples      = 0UL;
          vh -> vh_SamplesPerHiCycle    = 0UL;
          vh -> vh_SamplesPerSec        = ((SysBase -> ex_EClockFrequency) * 5UL) / period;
          vh -> vh_Octaves              = 1UL;
          vh -> vh_Compression          = CMP_NONE;
          vh -> vh_Volume               = 0x10000UL;
        }
      }

      /* save the data */
      SaveDTObject( so, NULL, NULL, filename, DTWM_IFF, NULL );

      /* The given data (e.g. SDTA_Sample) will be freed by the application */
      SetDTAttrs( so, NULL, NULL, SDTA_Sample, NULL, TAG_DONE );

      /* Get rid of the sound object */
      DisposeDTObject( so );
    }
}


/* copy 32 bits-per-gun cregs table into struct ColorRegister array */
void FillColorRegisters( struct ColorRegister *cm, ULONG *cregs, ULONG numcolors )
{
    if( cm && cregs && numcolors )
    {
      ULONG i;

      for( i = 0UL ; i < numcolors ; i++, cm++ )
      {
        /* reduce color information from 32 bits down to 8 bits per r, g, b */
        cm -> red   = (UBYTE)(cregs[ ((i * 3) + 0) ] >> 24UL);
        cm -> green = (UBYTE)(cregs[ ((i * 3) + 1) ] >> 24UL);
        cm -> blue  = (UBYTE)(cregs[ ((i * 3) + 2) ] >> 24UL);
      }
    }
}


/* Copy bitmaps, (non-)interleaved source to (non-)interleaved destination */
void CopyBitMap( struct BitMap *dest, struct BitMap *src )
{
    if( dest && src )
    {
      PLANEPTR  splanes[ 8 ] = { 0 };
      PLANEPTR  dplanes[ 8 ] = { 0 };
      ULONG     iRow,
                iPlane;
      ULONG     srcBytes = (IsInterleavedBitMap( src )?
                           ((src -> BytesPerRow) / (src -> Depth)):
                           (src -> BytesPerRow));

      for( iRow = (src -> Rows) ; iRow > 0UL ; iRow-- )
      {
        for( iPlane = 0UL ; iPlane < (ULONG)(src -> Depth) ; iPlane++ )
        {
          if( splanes[ iPlane ] == NULL ) splanes[ iPlane ] = src  -> Planes[ iPlane ];
          if( dplanes[ iPlane ] == NULL ) dplanes[ iPlane ] = dest -> Planes[ iPlane ];

          /* Write next row. */
          memcpy( dplanes[ iPlane ], splanes[ iPlane ], (size_t)srcBytes );

          splanes[ iPlane ] += src  -> BytesPerRow;
          dplanes[ iPlane ] += dest -> BytesPerRow;
        }
      }
    }
}


void mysprintf( STRPTR buffer, STRPTR fmt, ... )
{
    APTR args;

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

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


/* Check if given bitmap is interleaved or not. This function works even on non-chip-mem-bitmaps.
 * The V39/V40 GetBitMapAttr function don't set the interleaved flag, see above for a future-compatible
 * workaround.
 */
BOOL IsInterleavedBitMap( struct BitMap *bm )
{
    if( bm )
    {
      if( (bm -> Depth) > 1U )
      {
        if( (ULONG)((bm -> Planes[ 1 ]) - (bm -> Planes[ 0 ])) == ((ULONG)(bm -> BytesPerRow) / (ULONG)(bm -> Depth)) )
        {
          return( TRUE );
        }
      }
    }

    return( FALSE );
}

