
// gfxanim.cpp
//
// Copyright (c) 1995 by Toshiaki Tsuji, all rights reserved.

#include "stdgfx.h"
#include "gfxanim.h"
#include "gfxfiles.h"

ANIMIMAGE::ANIMIMAGE () : IMAGE ( IMAGE_TOPDOWN )
  {
    NumFrames = 0;
    Frames = NULL;
    BaseImage = NULL;
  } // End of Constructor for ANIMIMAGE

ANIMIMAGE::~ANIMIMAGE ()
  {
    Destroy ();
  } // End of Destructor for ANIMIMAGE

BOOLEAN ANIMIMAGE::CreateFrames ( LONG Num )
  {
    DestroyFrames ();
    Frames = new ANIMFRAME [Num];
    if (Frames==NULL)
      return FAILURE;
      
    NumFrames = Num;
    
    LONG i;
    for (i=0;i<NumFrames;i++)  
      {
        Frames[i].NumChunks = 0;
        Frames[i].Size = 0;
        Frames[i].Chunks = NULL;
      } // End for
    return SUCCESS;
  } // End of CreateFrames for ANIMIMAGE

VOID ANIMIMAGE::DestroyFrame ( ANIMFRAME *Frame )
  {
    if (Frame->Chunks!=NULL)
      {
        delete Frame->Chunks;  
      } // End if  
    Frame->Chunks = NULL;
  } // End of DestroyFrame for ANIMIMAGE
  
VOID ANIMIMAGE::DestroyFrames ()
  {
    if (Frames!=NULL)
      {
        LONG i;
        for (i=0;i<NumFrames;i++)
          {
            DestroyFrame ( &(Frames[i]) );
          } // End for
        delete Frames;  
      } // End if
         Frames = NULL;
  } // End of DestroyFrames for ANIMIMAGE

VOID ANIMIMAGE::Destroy ()
  {
    IMAGE::Destroy ();
    DestroyBaseImage ();    
    DestroyFrames ();
  } // End of Destroy for ANIMIMAGE

BOOLEAN ANIMIMAGE::CreateBaseImage ()
  {
    LONG Size;
    Size = BytesPerRow*Height;
    if (Size<=0)
      return FAILURE;
      
    BaseImage = new BYTE [(LONG)Size];
    if (BaseImage==NULL)
      return FAILURE;
    memset ( BaseImage, 0, Size );
    return SUCCESS;  
  } // End of CreateBaseImage for ANIMIMAGE

VOID ANIMIMAGE::DestroyBaseImage ()
  {
    if (BaseImage!=NULL)
      delete BaseImage;
    BaseImage = NULL;    
  } // End of DestroyBaseImage for ANIMMAGE

VOID ANIMIMAGE::SetBaseImage ( IMAGE *Image, LONG Sx, LONG Sy )
  {
    if (BaseImage==NULL)
      return;

    LONG i;
    BYTE *SrcBuffer;

    for (i=0;i<Height;i++)
      {
        SetOffset ( 0, i );
        SrcBuffer = Image->SetOffset ( Sx, Sy+i );
        memcpy ( BaseImage+Offset, SrcBuffer, BytesPerRow );
      } // End for    
  } // End of SetBaseImage for ANIMIMAGE
  
VOID ANIMIMAGE::RecordFrame ( LONG FrameNumber, IMAGE *Image, LONG Sx, LONG Sy )
  {
    if ((FrameNumber<0)||(FrameNumber>=NumFrames))
      return;
    if (BaseImage==NULL)
      return;  

    if (FrameNumber==0)
      {
        SetBaseImage ( Image, Sx, Sy );  
        return;
      } // End if  
        
    ANIMFRAME *Frame;
    Frame = &(Frames[FrameNumber]);  
    DestroyFrame ( Frame );
        
    BYTE *TempSrcBuffer;
    BYTE *ChunkBuffer;
    BYTE *DataBuffer;

    LONG Size;
    Size = BytesPerRow*Height;

    TempSrcBuffer = new BYTE [Size];
    DataBuffer = new BYTE [Size*2];
    ChunkBuffer = new BYTE [(LONG)0xFFFF];

    // Create TempSrcBuffer
    LONG i;
    LONG Offset;
    Offset = 0;
    for (i=0;i<Height;i++)
      {
        memcpy ( TempSrcBuffer+Offset, Image->SetOffset ( Sx, Sy+i ), BytesPerRow );
        Offset += BytesPerRow;
      } // End for
    
    LONG Count;
    LONG cCount;    // Compressed Count
    LONG SkipCount;
    LONG DumpCount;
    LONG NumChunks;
    BYTE CurPix,SrcPix;    

    Count=0;
    CurPix = BaseImage[Count];
    SrcPix = TempSrcBuffer[Count];
    Count++;

    cCount = 0;
    NumChunks = 0;
    
    while (Count<Size)
      {
        if (CurPix==SrcPix)
          {
            SkipCount=0;  
            while ((CurPix==SrcPix)&&(Count<Size)&(SkipCount<(LONG)0x7FFF))
              {
                SkipCount++;  
                CurPix = BaseImage[Count];
                SrcPix = TempSrcBuffer[Count];
                Count++;
              } // End while
            *(SHORT*)(ChunkBuffer+cCount) = (SHORT)(-SkipCount);
            cCount += sizeof(SHORT);
            NumChunks++;
          } // End if
        else
          {
            DumpCount=0;  
            while ((CurPix!=SrcPix)&&(Count<Size)&(DumpCount<(LONG)0x7FFF))
              {
                DataBuffer[DumpCount++] = SrcPix;
                CurPix = BaseImage[Count];
                SrcPix = TempSrcBuffer[Count];
                Count++;
              } // End while
            *(SHORT*)(ChunkBuffer+cCount) = (SHORT)DumpCount;
            cCount += sizeof(SHORT);

            for (i=0;i<DumpCount;i++)
              {
                *(ChunkBuffer+cCount) = DataBuffer[i];
                cCount++;
              } // End for  
              
            NumChunks++;
          } // End else    
      } // End while

    // Check if it is worth compressing
    if (cCount>Size)
      {
        Frame->NumChunks = -1;
        Frame->Size = Size;
        Frame->Chunks = new BYTE [Size];
        memcpy ( Frame->Chunks, TempSrcBuffer, Size );  
      } // End if
    else
      {
        // Copy All Chunks into Chunk Data
        Frame->NumChunks = NumChunks;
        Frame->Size = cCount;
        if (cCount==0)
          Frame->Chunks = NULL;
        else  
          Frame->Chunks = new BYTE [cCount];
        memcpy ( Frame->Chunks, ChunkBuffer, cCount );          
      } // End else  
          
    delete TempSrcBuffer;
    delete ChunkBuffer;    
    delete DataBuffer;    
  } // End of RecordFrame for ANIMIMAGE

VOID ANIMIMAGE::SetFrame ( LONG FrameNumber )
  {
    if (BaseImage==NULL)
      return;
    if (Buffer==NULL)
      return;
    if ((FrameNumber<0)||(FrameNumber>=NumFrames))
      return;
    
    LONG Size;
    Size = BytesPerRow*Height;      
    // First, copy base image into buffer
    memcpy ( Buffer, BaseImage, Size );

    if (FrameNumber==0)
      return;
       
    LONG i;
    LONG RepCount;
    BYTE* ChunkBuffer;
    BYTE* DestBuffer;
    ANIMFRAME *Frame;
    Frame = &(Frames[FrameNumber]);
    ChunkBuffer = Frame->Chunks;

    if (ChunkBuffer==NULL)
      return;
      
    DestBuffer = Buffer;
    
    // Copy Delta Compressed Data
    for (i=0;i<Frame->NumChunks;i++)
      {
        RepCount = *((SHORT*)ChunkBuffer);
        ChunkBuffer += sizeof(SHORT);
        if (RepCount<0)
          DestBuffer += -RepCount;
        else
          {
            memcpy ( DestBuffer, ChunkBuffer, RepCount );
            ChunkBuffer += RepCount;
            DestBuffer += RepCount;
          } // End else  
      } // End for
  } // End of SetFranme for ANIMIMAGE

BOOLEAN ANIMIMAGE::Save ( STRING FileName )
  {
    FILEHANDLE f;

    if (BaseImage==NULL)
      return FAILURE;
      
    f = File.Open ( FileName, OPEN_BINARY | OPEN_WRITE );
    if (f==NULL)
      return FAILURE;

    CHAR Str[32];
    sprintf ( Str, "ANIMATION" );
    File.Write ( f, Str, 32 );

    LONG Wd,Ht,BytesPerPix,NumFrm;
    Wd = Width;
    Ht = Height;
    BytesPerPix = BytesPerPixel;
    NumFrm = NumFrames;

    #if defined (__MSBFIRST__)
      SwapDWord ( (DWORD*)&Wd );
      SwapDWord ( (DWORD*)&Ht );
      SwapDWord ( (DWORD*)&BytesPerPix );
      SwapDWord ( (DWORD*)&NumFrm );
    #endif
    
    File.Write ( f, &Wd, sizeof(LONG) );     
    File.Write ( f, &Ht, sizeof(LONG) );     
    File.Write ( f, &BytesPerPix, sizeof(LONG) );     
    File.Write ( f, &NumFrm, sizeof(LONG) );

    LONG Size;
    Size = BytesPerRow*Height;

    File.Write ( f, BaseImage, Size );

    INT i;
    LONG NumChunks,ChunkSize;
    for (i=1;i<NumFrames;i++)
      {
        NumChunks = Frames[i].NumChunks;  
        ChunkSize = Frames[i].Size;
        #if defined (__MSBFIRST__)
          SwapDWord ( (DWORD*)&NumChunks );
          SwapDWord ( (DWORD*)&Size );
        #endif
        File.Write ( f, &NumChunks, sizeof(LONG) );
        File.Write ( f, &Size, sizeof(LONG) );
        File.Write ( f, Frames[i].Chunks, Frames[i].Size );
      } // End for
      
    File.Close ( f );
    return SUCCESS;    
  } // End of Save for ANIMIMAGE

BOOLEAN ANIMIMAGE::Load ( STRING FileName )
  {
    FILEHANDLE f;

    f = File.Open ( FileName, OPEN_BINARY | OPEN_READ );
    if (f==NULL)
      return FAILURE;

    Destroy ();
    DestroyFrames ();
    
    CHAR Str[32];
    File.Read ( f, Str, 32 );
    if(strcmp ( Str, "ANIMATION" ))
      {
        File.Close ( f );
        return FAILURE;  
      } // End if
      
    File.Read ( f, &Width, sizeof(LONG) );     
    File.Read ( f, &Height, sizeof(LONG) );     
    File.Read ( f, &BytesPerPixel, sizeof(LONG) );

    #if defined (__MSBFIRST__)
      SwapDWord ( (DWORD*)&Width );
      SwapDWord ( (DWORD*)&Height );
      SwapDWord ( (DWORD*)&BytesPerPixel );
    #endif
    
    LONG NewFormat=0;
    switch (BytesPerPixel)
      {
        case 1 :
          NewFormat = IMAGE_8BIT;
          break; 
        case 2 :
          NewFormat = IMAGE_16BIT;
          break; 
        case 3 :
          NewFormat = IMAGE_24BIT;
          break; 
      } // End switch

    if (Create ( NewFormat, Width, Height )==FAILURE)
      return FAILURE;

    if (CreateBaseImage()==FAILURE)
      return FAILURE;
      
    File.Read ( f, &NumFrames, sizeof(LONG) );
    
    #if defined (__MSBFIRST__)
      SwapDWord ( (DWORD*)&NumFrames );
    #endif

    if (CreateFrames ( NumFrames )==FAILURE)
      return FAILURE;
    
    LONG Size;
    Size = BytesPerRow*Height;

    File.Read ( f, BaseImage, Size );

    INT i;
    for (i=1;i<NumFrames;i++)
      {
        File.Read ( f, &(Frames[i].NumChunks), sizeof(LONG) );
        File.Read ( f, &(Frames[i].Size), sizeof(LONG) );
        #if defined (__MSBFIRST__)
          SwapDWord ( (DWORD*)&(Frames[i].NumChunks) );
          SwapDWord ( (DWORD*)&(Frames[i].Size) );
        #endif
        File.Read ( f, Frames[i].Chunks, Frames[i].Size );
      } // End for
      
    File.Close ( f );
    return SUCCESS;    
  } // End of Load for ANIMIMAGE

