

/*
**
**  $VER: dumpdtanim.c 1.1 (4.11.96)
**  dumpdtanim 1.0
**
**  main include file
**
**  (C) Copyright 1996 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/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/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>

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

/* 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   *IconBase;
       struct Library   *GfxBase;
       struct Library   *IntuitionBase;
       struct Library   *DataTypesBase;

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


#define TEMPLATE "NAME/A,MOVIE/S,PICTUREPERFRAME/S,SAMPLEPERFRAME=SPF/S,GLOBALSAMPLE/S"
#define OPT_NAME             0
#define OPT_MOVIE            1
#define OPT_PICTUREPERFRAME  2
#define OPT_SAMPLEPERFRAME   3
#define OPT_GLOBALSAMPLE     4
#define NUM_OPTS             5

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


/* prototypes */
ULONG SaveDTObject( Object *, struct Window *, struct Requester *, STRPTR, ULONG, struct TagItem * );
void  SaveSample( STRPTR, UBYTE *, ULONG, ULONG, ULONG );
void  FillColorRegisters( struct ColorRegister *, ULONG *, ULONG );
void  CopyBitMap( struct BitMap *, struct BitMap * );
void  mysprintf( STRPTR, STRPTR, ... );

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


void main( void )
{
    /* Argument parsing variables */
    LONG                    options[ NUM_OPTS ];
    struct RDArgs          *rdargs;

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

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

    if( rdargs = ReadArgs( TEMPLATE, options, NULL ) )
    {
      STRPTR filepart = FilePart( (STRPTR)options[ OPT_NAME ] );

      /* Open DataTypes */
      if( DataTypesBase = OpenLibrary( "datatypes.library", 39UL ) )
      {
          Object *o; /* anim/movie object */

          /* 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 */
            UBYTE                  *asample;
            ULONG                   asamplelength = 0UL;

            ULONG                   aperiod;
            ULONG                   acycles;

            TEXT                    samplefilename[ 512 ];

            /* Sample build from all samples */
#define GLOBALSAMPLESIZE (100000UL)
            UBYTE                  *globalsample        = NULL;
            ULONG                   globalsampleoffset  = 0UL;


            if( options[ OPT_GLOBALSAMPLE ] )
            {
              globalsample = (UBYTE *)AllocVec( GLOBALSAMPLESIZE, MEMF_PUBLIC );
            }

            /* 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" );
            }

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

            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 = 1UL ; timestamp < anumframes ; timestamp++ )
            {
              /* Check for CTRL_C signal... */
              if( SetSignal( 0UL, 0UL ) & SIGBREAKF_CTRL_C )
              {
                break;
              }

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

                /* 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" );
                          }
                        }

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

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

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

              /* 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" );
                  }
                }
              }

              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 );
            }
          }
          else
          {
            LONG errnum = IoErr();
            TEXT errbuff[ 100 ];

            if( errnum >= DTERROR_UNKNOWN_DATATYPE )
            {
              mysprintf( errbuff, GetDTString( errnum ), options[ OPT_NAME ] );
            }
            else
            {
              Fault( errnum, NULL, errbuff, sizeof( errbuff ) );
            }

            Printf( "%s\nerror #%ld\n", errbuff, errnum );
          }

          CloseLibrary( DataTypesBase );
      }
      else
      {
        Printf( "couldn't open datatypes.library V39\n" );
      }

      /* Free the allocated memory after ReadArgs */
      FreeArgs( rdargs );
    }
    else
    {
      /* Show the failure message */
      PrintFault( IoErr(), NULL );
    }

    CloseLibrary( IntuitionBase );
    CloseLibrary( GfxBase );
    CloseLibrary( IconBase );
}


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

    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
    {
      SetIoErr( ERROR_REQUIRED_ARG_MISSING );
    }

    return( retval );
}


/* save sound sample using a sound.datatype object */
void SaveSample( STRPTR filename, UBYTE *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 )
        {
#define PALCLOCK (3546895UL)
          vh -> vh_OneShotHiSamples     = length;
          vh -> vh_RepeatHiSamples      = 0UL;
          vh -> vh_SamplesPerHiCycle    = 0UL;
          vh -> vh_SamplesPerSec        = PALCLOCK / 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 bitmap (from fast-mem into chip-mem etc.)
 * WARNING: This version does __NOT__ work with interleaved bitmaps
 */
void CopyBitMap( struct BitMap *dest, struct BitMap *src )
{
    if( dest && src )
    {
      ULONG planesize = (ULONG)(dest -> BytesPerRow) * (ULONG)(dest -> Rows);
      UWORD i;

      for( i = 0U ; i < (dest -> Depth) ; i++ )
      {
        CopyMem( (src -> Planes[ i ]), (dest -> Planes[ i ]), planesize );
      }
    }
}


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

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

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


