/* SaveSprite.c */

/* Saving an anim as a RiscOS sprite file
 * (K) All Rites Reversed - Copy What You Like (see file Copying)
 *
 * Authors:
 *      Peter Hartley       <peter@ant.co.uk>
 *
 * History:
 *      23-Oct-96 pdh Started
 *      27-Oct-96 pdh Frob to cope with 3/5/6/7bpp anim's
 *      27-Oct-96 *** Release 4beta1
 *      29-Oct-96 pdh Fix mask code so it expands to whole pixels properly
 *      29-Oct-96 *** Release 4beta2
 *      07-Nov-96 *** Release 4
 *      15-Dec-96 *** Release 5beta1
 *      01-Jan-97 pdh Fix bug in saving 2bpp sprites
 *      27-Jan-97 *** Release 5beta2
 *      29-Jan-97 *** Release 5beta3
 *      03-Feb-97 *** Release 5
 *      07-Feb-97 *** Release 5.01
 *
 */

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include "DeskLib:Sprite.h"

#include "bitmap.h"
#include "count.h"
#include "utils.h"

#if 0
#define debugf printf
#define DEBUG 1
#else
#define debugf 1?0:printf
#define DEBUG 0
#endif


void CompressSpriteLine( char *dest, char *src, int n, int bpp )
{
    int i;

    switch ( bpp )
    {
    case 8:
        memcpy( dest, src, n );
        break;
    case 4:
        for ( i=0; i< (n+1)/2; i++ )
            dest[i] = src[i<<1] + ( src[(i<<1)+1] << 4 );
        break;
    case 2:
        memset( dest, 0, (n+3)/4 );
        for ( i=0; i<n; i++ )
            dest[i>>2] |= (src[i]) << ((i&3)*2);
        break;
    case 1:
        memset( dest, 0, (n+7)/8 );
        for ( i=0; i<n; i++ )
            dest[i>>3] |= (src[i]) << (i&7);
        break;
    }
}

void CompressMaskLine( char *dest, char *src, int n, int bpp )
{
    int i;

    switch ( bpp )
    {
    case 8:
        for ( i=0; i<n; i++ )
            dest[i] = ( src[i] ) ? 0xFF : 0;
        break;
    case 4:
        for ( i=0; i< (n+1)/2; i++ )
            dest[i] =     ( src[i<<1]     ? 0xF : 0 )
                      + ( ( src[(i<<1)+1] ? 0xF : 0 ) << 4 );
        break;
    case 2:
        memset( dest, 0, (n+3)/4 );
        for ( i=0; i<n; i++ )
            dest[i>>2] |= ( src[i] ? 3 : 0 ) << ((i&3)*2);
        break;
    case 1:
        memset( dest, 0, (n+7)/8 );
        for ( i=0; i<n; i++ )
            dest[i>>3] |= (src[i]) << (i&7);
        break;
    }
}

/* Sprites are only available in 1, 2, 4, 8 bpp */
const char bppmap[8] = { 1, 2, 4, 4, 8, 8, 8, 8 };

BOOL Anim_MakeSprite( anim a, FILE *output )
{
    sprite_areainfo sai;
    sprite_header *psh;
    int nSpriteSize;
    int i,y;
    int newbpp;
    int abw;
    void *data;
/*    unsigned int palette[256];*/
    int newncol;
    frameptr f2 = Anim_OpenFrame( a, 0 );
    int nFrames = a->nFrames;

    if ( !f2 )
    {
        /* There was an error */
        return FALSE;
    }

    newbpp = bppmap[ MinBpp( f2->nColours ) - 1 ];
    newncol = 1 << newbpp;
    abw = ( ( (a->nWidth*newbpp)+31 ) >> 3 ) & ~3;  /* aligned byte width */

    nSpriteSize = abw;
    nSpriteSize *= a->nHeight;
    nSpriteSize *= 2;
    nSpriteSize += 8*newncol;
    nSpriteSize += sizeof( sprite_header );

    Anim_CloseFrame( a, &f2 );

    /* Write the header */
    sai.numsprites = nFrames;
    sai.firstoffset = 16;
    sai.freeoffset = 16 + nFrames*nSpriteSize;
    fwrite( &sai.numsprites, 1, 12, output );

    data = Anim_Allocate( nSpriteSize );
    if ( !data )
    {
        Anim_NoMemory( "savesprite" );
        return FALSE;
    }

    psh = (sprite_header*) data;

    /* Write the frames */
    psh->offset_next = nSpriteSize;
    psh->width = (abw>>2)-1;
    psh->height = a->nHeight-1;
    psh->leftbit = 0;
    psh->rightbit = (a->nWidth*newbpp - 1 ) & 31;
    psh->imageoffset = sizeof(sprite_header) + 8*newncol;
    psh->maskoffset = psh->imageoffset + abw*a->nHeight;
    switch ( newbpp )
    {
    case 1: psh->screenmode = 18; break;
    case 2: psh->screenmode = 19; break;
    case 4: psh->screenmode = 20; break;
    case 8: psh->screenmode = 21; break;
    }

    for ( i=0; i < a->nFrames; i++ )
    {
        frameptr f = Anim_OpenFrame( a, i );
        char *src;
        char *dest;
        unsigned int *pPal = (unsigned int*)(psh+1);
        int delay;

        if ( !f )
        {
            Anim_Free( &data );
            return FALSE;
        }

        src = f->pImage;
        memset( psh->name, 0, 12 );
        delay = Anim_GetDelay( a, i, -1 );
        if ( delay != -1 )
            sprintf( psh->name, "%03ddelay%d", i, delay );
        else
            sprintf( psh->name, "%03d", i );

        for ( y=0; y < f->nColours; y++ )       /* NOT newncol */
        {
            *pPal++ = f->pPalette[y];
            *pPal++ = f->pPalette[y];
        }

        src = f->pImage;
        dest = ((char*)(psh+1)) + newncol*8;
        for ( y=0; y < a->nHeight; y++ )
        {
            CompressSpriteLine( dest, src, a->nWidth, newbpp );
            dest += abw;
            src += a->nWidth;
        }

        src = f->pMask;
        for ( y=0; y < a->nHeight; y++ )
        {
            CompressMaskLine( dest, src, a->nWidth, newbpp );
            dest += abw;
            src += a->nWidth;
        }

        Anim_CloseFrame( a, &f );
        fwrite( data, 1, nSpriteSize, output );
    }
    Anim_Free( &data );
    return TRUE;
}
