/*
======================================================================
util.c

Ernie Wright  05 Feb 2012

Utility routines for the ANIM-J movie loader.
====================================================================== */

#include "iff.h"
#include <stdlib.h>


/*
=====================================================================
FreeMovie()

Free a MovieInfo.

All MovieInfos should be sent here at the end of their lives.  This
routine is written so that the resources tied to an incompletely
constructed MovieInfo can be freed.  NULL is always echoed to the
caller.
===================================================================== */

MovieInfo *FreeMovie( MovieInfo *movie )
{
   int i;

   if ( movie ) {
      if ( movie->fp ) fclose( movie->fp );
      if ( movie->filename ) free( movie->filename );
      if ( movie->cmap ) free( movie->cmap );
      if ( movie->buf ) free( movie->buf );
      if ( movie->body ) free( movie->body );
      if ( movie->front ) free( movie->front );
      if ( movie->back ) free( movie->back );
      if ( movie->delta ) {
         for ( i = 0; i < movie->ndeltas; i++ )
            if ( movie->delta[ i ].bytes ) free( movie->delta[ i ].bytes );
         free( movie->delta );
      }
      if ( movie->ansq ) free( movie->ansq );
      free( movie );
   }
   return NULL;
}


/*
=====================================================================
revbytes()

Reverses byte order in place.

   bp       bytes to reverse
   elsize   size of the underlying data type
   elcount  number of elements to swab

Amiga IFF uses a byte order variously called "big-endian", "Motorola"
or "Internet order".  So do most systems.  The biggest exception is
Windows, which is where the Movie code needs this function.  The
iff.h header substitutes an empty macro on other systems.
===================================================================== */

#ifdef _WIN32
void revbytes( void *bp, int elsize, int elcount )
{
   unsigned char *p, *q;

   p = ( unsigned char * ) bp;

   if ( elsize == 2 ) {
      q = p + 1;
      while ( elcount-- ) {
         *p ^= *q;
         *q ^= *p;
         *p ^= *q;
         p += 2;
         q += 2;
      }
      return;
   }

   while ( elcount-- ) {
      q = p + elsize - 1;
      while ( p < q ) {
         *p ^= *q;
         *q ^= *p;
         *p ^= *q;
         ++p;
         --q;
      }
      p += elsize >> 1;
   }
}
#endif


/*
=====================================================================
unpack()

Decompress a run-length encoded bitplane row.

   src      pointer to an array of encoded bytes
   dst      array of at least rowsize bytes to receive decoded data
   rowsize  number of bytes in an uncompressed row

If successful, the decoded row is in dst, the src pointer is moved to
the end of the encoded array, and the function returns TRUE.  If an
error occurs, FALSE is returned.

The pixel data for an IFF ILBM is a byte stream containing code bytes
followed by data bytes.  The meaning of the code bytes, when regarded
as unsigned, is:

   n < 128     copy the next n+1 bytes
   n > 128     repeat the next byte 257-n times
   n = 128     no-op

Some versions of Photoshop incorrectly use the n = 128 no-op as a
repeat code, which breaks strictly conforming readers.  We allow the
use of n = 128 as a repeat.  This is pretty safe, since no one to my
knowledge explicitly writes no-ops into their ILBMs.  The reason
n = 128 is a no-op is historical:  the Mac Packbits buffer was only
128 bytes, and a repeat code of 128 generates 129 bytes.
===================================================================== */

int unpack( unsigned char **psrc, unsigned char *dst, int rowsize )
{
   int c, n;
   unsigned char *src = *psrc;

   while ( rowsize > 0 ) {
      n = *src++;

      if ( n < 128 ) {
         ++n;
         rowsize -= n;
         if ( rowsize < 0 ) return FALSE;
         while ( n-- ) *dst++ = *src++;
      }
      else {
         n = 257 - n;
         rowsize -= n;
         if ( rowsize < 0 ) return FALSE;
         c = *src++;
         while ( n-- ) *dst++ = c;
      }
   }
   *psrc = src;
   return TRUE;
}


/*
=====================================================================
unpack_ro()

Pretend to decompress a run-length encoded bitplane row, so that you
can move the source pointer.  Like unpack(), but doesn't write.
===================================================================== */

int unpack_ro( unsigned char **psrc, int rowsize )
{
   int n;
   unsigned char *src = *psrc;

   while ( rowsize > 0 ) {
      n = *src++;

      if ( n < 128 ) {
         ++n;
         rowsize -= n;
         if ( rowsize < 0 ) return FALSE;
         src += n;
      }
      else {
         n = 257 - n;
         rowsize -= n;
         if ( rowsize < 0 ) return FALSE;
         ++src;
      }
   }

   *psrc = src;

   return TRUE;
}


/*
=====================================================================
bit rotation

Fast 90-degree bit rotation routines.

   src       starting address of 8 x 8 bit source tile
   srcstep   byte offset between adjacent rows in source
   dst       starting address of 8 x 8 bit destination tile
   dststep   byte offset between adjacent rows in destination

Bits from the source are rotated 90 degrees either clockwise or
counterclockwise and written to the destination.

Based on Sue-Ken Yap, "A Fast 90-Degree Bitmap Rotator," in GRAPHICS
GEMS II, James Arvo ed., Academic Press, 1991, ISBN 0-12-064480-0.
===================================================================== */

#define rtable( name, n ) \
   static unsigned long name[ 16 ] = { \
      0x00000000ul << n, 0x00000001ul << n, 0x00000100ul << n, 0x00000101ul << n, \
      0x00010000ul << n, 0x00010001ul << n, 0x00010100ul << n, 0x00010101ul << n, \
      0x01000000ul << n, 0x01000001ul << n, 0x01000100ul << n, 0x01000101ul << n, \
      0x01010000ul << n, 0x01010001ul << n, 0x01010100ul << n, 0x01010101ul << n };

rtable( rtab0, 0 )
rtable( rtab1, 1 )
rtable( rtab2, 2 )
rtable( rtab3, 3 )
rtable( rtab4, 4 )
rtable( rtab5, 5 )
rtable( rtab6, 6 )
rtable( rtab7, 7 )

#define extract( d, t ) \
   lonyb = *d & 0xF; hinyb = *d >> 4; \
   lo |= t[ lonyb ]; hi |= t[ hinyb ]; d += pstep;


/*
=====================================================================
bitrot_cw()

Rotate bits clockwise.  The ANIM-J loader uses this to convert pixel
bits from planar to chunky.
===================================================================== */

void bitrot_cw( unsigned char *src, int srcstep, unsigned char *dst, int dststep )
{
   unsigned char *p;
   int pstep, lonyb, hinyb;
   unsigned long lo, hi;

   lo = hi = 0;

   p = src; pstep = srcstep;
   extract( p, rtab0 )
   extract( p, rtab1 )
   extract( p, rtab2 )
   extract( p, rtab3 )
   extract( p, rtab4 )
   extract( p, rtab5 )
   extract( p, rtab6 )
   extract( p, rtab7 )

#define writebits_cw( d, w ) \
   *d = ( unsigned char )(( w >> 24 ) & 0xFF ); d += pstep; \
   *d = ( unsigned char )(( w >> 16 ) & 0xFF ); d += pstep; \
   *d = ( unsigned char )(( w >>  8 ) & 0xFF ); d += pstep; \
   *d = ( unsigned char )( w & 0xFF );

   p = dst; pstep = dststep;
   writebits_cw( p, hi )
   p += pstep;
   writebits_cw( p, lo )
}
