#include <exec/types.h>
#include <intuition/intuitionbase.h>
#include <functions.h>
#include <exec/memory.h>
#include <libraries/dos.h>

	/* defines in eine Zeile schreiben */
#define POS ((( zeile * scr_data->pic_width )
                    / 8 ) + ( xp / 8 ))
#define BMHD (STRPTR)"BMHD"
#define CMAP (STRPTR)"CMAP"
#define BODY (STRPTR)"BODY"
#define CAMG (STRPTR)"CAMG"
#define PLANELOOP for(i=0;i<firstpic->pic_depth
                                           ;i++)
#define DISP_PLANE scr->BitMap.Planes[i]
#define BUFF_PLANE planebuf[i]
#define GRAF_BASE (struct GfxBase *)
        OpenLibrary("graphics.library",0L);
#define INTU_BASE (struct IntuitionBase *)
        OpenLibrary("intuition.library",0l);

struct iffpic
 {
   ULONG form_len;
   ULONG bmhd_len;
   ULONG cmap_len;
   ULONG body_len;
   UBYTE *form;
   UBYTE *bmhd;
   UBYTE *cmap;
   UBYTE *body;
   UWORD pic_width;
   UWORD pic_height;
   WORD pic_xoff;
   WORD pic_yoff;
   UBYTE pic_depth;
   UBYTE pic_mask;
   UBYTE pic_comp;
   UBYTE unused;
   UWORD pic_transp_col;
   UBYTE xAsp;
   UBYTE yAsp;
   UWORD screenX;
   UWORD screenY;
 };   
 
ULONG *vmp,viewmodes,test,anim_len,planesize, 
      displaysize,anlen;

UWORD colortable[32],*anpos,*animdata,dbuf = 0,
      colnum;
UBYTE header[12],bmh[28],file[70],firstfile[70],
      namebuf[70],*suche_chunk(),tempfile[70],
      getkey();
PLANEPTR planebuf[6];
struct FileHandle *fh,*iff_file,*open_file();

struct iffpic *firstpic,*nextpic;
struct BitMap customBitMap;
struct Screen *scr,*bscr,*open_scr();
struct NewScreen nscr;
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct Window *dw;

main()
 {
  UBYTE c;

  startup();

  FOREVER
   {
    printf("\n\nBENUTZUNGSHINWEIS :\n");
    printf(" (m)ache eine Animation\n");
    printf(" (a)nimiere\n");
    printf(" (f)ile erstellen\n");
    printf(" (l)aden einer Anim.\n");
    printf(" (s)peichern einer Anim\n");
    printf(" (e)nde des Programms\n");
    printf(" (d) DBUF ein/aus momentan :");
    if(dbuf) printf(" EIN\n");
    else     printf(" AUS\n");
    printf("\n TMP : %s\n",tempfile);

    switch(getkey(dw))
     {
      case 'd':
               dbuf = dbmode();
               break;
      case 'm':
               make_anim();
               break;
      case 'a':
               show_anim();
               break;
      case 'f':
               fastfile();
               break;
      case 'l':
               if(load_anim())
                printf("laden mißlungen\n");
               else
                printf("laden gelungen\n");
               break;
      case 's':
               if(save_anim())
                printf("speichern mißlungen\n");
               else
                printf("speichern gelungen\n");
               break;
      case 'e':
               ende ("Programm Ende");
               break;
      default:
              break;
     }
   }
 }

/************************************************
 *
 *     CHUNK in den IFF Daten suchen
 *
 ***********************************************/ 

UBYTE *suche_chunk(chunk_name,lpic)

  STRPTR chunk_name;
  struct iffpic *lpic; 
  {
   UBYTE *such_ptr;

   for(such_ptr = lpic->form
       ;such_ptr < (lpic->form + lpic->form_len);
        such_ptr++)
    {
     if( *such_ptr     == *chunk_name &&
         *(such_ptr+1) == *(chunk_name+1) && 
         *(such_ptr+2) == *(chunk_name+2) && 
         *(such_ptr+3) == *(chunk_name+3)   )
       return(such_ptr);
    }
   return(NULL);  
  }


/************************************************
 *
 *   Globaler Ausstieg
 *
 ***********************************************/
ende(why)
 char *why;

 {
   SHORT i;
   printf("%s",why);

   if(fh)      Close(fh);
   if(iff_file)Close(iff_file);
   if(firstpic)FreeMem(firstpic
                   ,(LONG)sizeof(struct iffpic));
   if(nextpic)FreeMem(nextpic
                   ,(LONG)sizeof(struct iffpic));
   if(animdata)FreeMem(animdata,anim_len);
   if(scr)CloseScreen(scr);
   if(bscr)CloseScreen(bscr);
   if(GfxBase)       CloseLibrary(GfxBase);
   if(IntuitionBase) CloseLibrary(IntuitionBase);
   printf("\n\n ENDE \n\n");
   Exit(1L);
 }


/************************************************
 *
 *   Screen öffnen
 *
 ************************************************/

struct Screen *open_scr()
 {
  struct Screen *oscr;
 
  planesize = RASSIZE((LONG)firstpic->pic_width,
                      (LONG)firstpic->pic_height);

  nscr.LeftEdge      = firstpic->pic_xoff;
  nscr.TopEdge       = firstpic->pic_yoff;
  nscr.Width         = firstpic->pic_width;
  nscr.Height        = firstpic->pic_height;
  nscr.Depth         = firstpic->pic_depth;
  nscr.DetailPen     = NULL;
  nscr.BlockPen      = NULL;

  if(firstpic->pic_width > 320)
   {
    if(firstpic->pic_height > 256)
     nscr.ViewModes = HIRES|LACE;
    else
     nscr.ViewModes = HIRES;
   }
  else
   {
    if(firstpic->pic_depth == 6)
     {
      if(firstpic->pic_height > 256)
       nscr.ViewModes = HAM|LACE;
      else
       nscr.ViewModes = HAM;
     }
    if(firstpic->pic_height > 256)
     nscr.ViewModes = LACE;
    else
     nscr.ViewModes     = NULL;
   }
  if(viewmodes)nscr.ViewModes = viewmodes;

  nscr.Type          = CUSTOMSCREEN;
  nscr.Font          = NULL;
  nscr.DefaultTitle  = NULL;
  nscr.Gadgets       = NULL;
  nscr.CustomBitMap  = NULL;

  if(!(oscr = (struct Screen *)OpenScreen(&nscr)))
      ende("kein Screen");


  return(oscr);
 }


/************************************************
 *
 *   Dekomprimieren und in Bildschirmspeicher
 *
 ************************************************/

read_body(scr_data,disp)
 struct iffpic *scr_data;
 struct Screen *disp;
 {
  LONG n,zeile;
  struct BitMap *bm;
  BYTE *bp,m,plane;
  WORD spalte,xp;

  bm = (struct BitMap *)&(disp->BitMap);
  bp = (BYTE *)scr_data->body ;
  bp += 8;

  for(zeile = 0;zeile < scr_data->pic_height
                                         ;zeile++)
   {
    for(plane = 0;plane < scr_data->pic_depth
                                         ;plane++)
     {
      spalte = NULL;
      while(spalte <  (scr_data->pic_width - 8) )
       {
        m = *bp;
        if(m != -128)
         {       
          if(m < 0)
           {
            m *= -1;
            bp++;
            for(xp = spalte
                 ;xp <=  (spalte + (m*8));xp += 8)
             {
              *((bm->Planes[plane]) + POS) = *bp;
             }
            spalte = xp;
             bp++;
           }
          else
            {
            for(xp = spalte
                 ;xp <=  (spalte + (m*8));xp += 8)
             {
              *((bm->Planes[plane]) + POS) 
                                        = *(++bp);
             }
            spalte = xp;
            bp++;
           }
  
         }  /* END of if(-128      */
        else bp++;
       }    /* END of while(spalte */

     }      /* END of for(zeile    */

   }        /* END of for(plane    */

   Delay (50L);
  FreeMem(scr_data->form,scr_data->form_len);
  scr_data = NULL; 
 }          /* END of function     */
 
/*************************************************
 *
 *   FARBEN AUF SCREEN SETZEN
 *
 ************************************************/
  
set_colors(screen)
 struct Screen *screen;
 {
  UBYTE *colval;
  UWORD color,i;
  LONG line = 0;

  colnum = 2;
  if(firstpic->pic_depth < 6)
   {
    PLANELOOP
       colnum*=2;
   }
  else colnum = 16;

  colval = ((UBYTE *)firstpic->cmap)+8;  

  for(i=0;i<colnum;i++)
   {
    color = 0;
    color += (*colval++)*16;
    color += (*colval++);
    color += (*colval++)/16;

    colortable[i] = color;
   }

  LoadRGB4(&scr->ViewPort,colortable,
                              (LONG)colnum);
  return();
 }



/************************************************
 *
 *   LESEN + INITIALISIERUNG DER IFF STRUCT
 *
 ************************************************/

read_IFF(rpic,pfn)
 struct iffpic *rpic;
 STRPTR pfn;
 {
  LONG act_size;

  iff_file = open_file(pfn);
  if(!iff_file)return();

  Read(iff_file,&header[0],12L);
 
  if( *((ULONG *)&header[0]) != 0x464f524dL)
      ende("FORM ERR"); 
    
  if( *((ULONG *)&header[8]) != 0x494c424d)
      ende("ILBM_ERR");

  rpic->form_len = *((ULONG *)&header[4]);

  rpic->form = (UBYTE *)AllocMem
                      (rpic->form_len,MEMF_CLEAR);
  if(!rpic->form)ende("GRAFIK_ERR"); 
  act_size = Read(iff_file ,rpic->form
                                ,rpic->form_len); 

  if((rpic->bmhd = suche_chunk(BMHD,rpic))==NULL)
      ende("BMHD ERR"); 
  if((rpic->cmap = suche_chunk(CMAP,rpic))==NULL)
      ende("CMAP ERR");
  if((rpic->body = suche_chunk(BODY,rpic))==NULL)
      ende("BODY ERR");
  if(vmp = (ULONG *)suche_chunk(CAMG,rpic ))
    viewmodes = *(vmp+2);
  else viewmodes = NULL;

  rpic->bmhd_len  =*((ULONG *)((rpic->bmhd)+4));
  rpic->cmap_len  =*((ULONG *)((rpic->cmap)+4));
  rpic->body_len  =*((ULONG *)((rpic->body)+4));
  rpic->pic_width =*((UWORD *)((rpic->bmhd)+8));
  rpic->pic_height=*((UWORD *)((rpic->bmhd)+10));
  rpic->pic_xoff  =*( (WORD *)((rpic->bmhd)+12));
  rpic->pic_yoff  =*( (WORD *)((rpic->bmhd)+14));
  rpic->pic_depth =*((UBYTE *)((rpic->bmhd)+16));
  rpic->pic_mask  =*((UBYTE *)((rpic->bmhd)+17));
  rpic->pic_comp  =*((UBYTE *)((rpic->bmhd)+18));
  rpic->xAsp      =*((UBYTE *)((rpic->bmhd)+22));
  rpic->yAsp      =*((UBYTE *)((rpic->bmhd)+23));
  rpic->screenX   =*((UWORD *)((rpic->bmhd)+24));
  rpic->screenY   =*((UWORD *)((rpic->bmhd)+26));

  return();
 }


/*************************************************
 *
 *   Oeffnen eines Buffers fuer den Screen
 *
 ************************************************/

get_planebuffer()
 {
  WORD i;

  PLANELOOP
   {
    planebuf[i] = (PLANEPTR)AllocMem
                                 (planesize,NULL);
    if(!planebuf[i])ende("no Buffer Planes" );
   }
 }


/************************************************
 *
 *   Kopieren des Screens in den Buffer
 *
 ************************************************/

copy_screen()
 {
  WORD i;

  PLANELOOP
    CopyMemQuick(DISP_PLANE,BUFF_PLANE,planesize);
  return();
 }

/************************************************
 *
 *   Vergleichen der Screen Dimensionen
 *
 ***********************************************/

cmp_iffdata(dat1,dat2)
 struct iffpic *dat1,*dat2;
 {
  if( dat1->pic_width  == dat2->pic_width  &&
      dat1->pic_height == dat2->pic_height &&
      dat1->pic_depth  == dat2->pic_depth  )
     
    return(TRUE);

  else return(FALSE);
 } 


/************************************************
 *
 *   Erstellung der Animationsdaten
 *
 ************************************************/

check_diff()
 {
  UWORD  i,pos,*countpos;

  PLANELOOP
   {
    countpos = anpos++;
    if(anpos > (animdata+anim_len-2))return(1);   
    *countpos = 0;

    for(pos = 0;pos < (planesize);pos+=2)
     {
      if(*((UWORD *)(DISP_PLANE+pos)) != 
          *((UWORD *)(BUFF_PLANE+pos)))
       {
        (*countpos)++;

        if(anpos > (animdata+anim_len-2))
                                      return(1);  
        *anpos++ = pos;
        *anpos++ = *((UWORD *)(DISP_PLANE+pos));
       }
     }  /* end of for (pos  */
   }    /* end of PLANELOOP */
  return(0); 
 }

 
/************************************************
 *
 *   Wiedergaberoutine der Animation
 *
 ************************************************/

playanim(pause,wieoft)
 LONG pause;
 LONG wieoft;
 {
  UWORD *anim,*pos,i,change,chnum;
  LONG sooft;

  if(dbuf)ScreenToFront(bscr); 

  for(sooft=0;sooft<wieoft;sooft++)
   { 
    anim = animdata;
    while(*anim != 0xFFFF)
     {
      PLANELOOP
       {
        chnum = *anim++;

        for(change = 0;change < chnum;change++)
         {
          pos = (UWORD *)(DISP_PLANE+(*(anim++)));
          *pos = *(anim++);
         }
       }
      Delay(pause);
      if(dbuf) copy_screen();
     }
   }
  return(NULL);
 }
 
/*************************************************
 *
 *   Kontolliertes oeffnen eines Files
 *
 ************************************************/

  
struct FileHandle * open_file(filename)
 STRPTR filename;
 {
  struct FileHandle *filehandle;
  struct Lock *lock;

  FOREVER
   {
    if(lock = Lock(filename,ACCESS_READ))
     {
      UnLock(lock);
      filehandle = Open(filename,MODE_OLDFILE);
      if(filehandle)return(filehandle);
     }
    printf("File: %s nicht vorhanden !!!\n"
           ,filename);
    printf("NOCHMAL ? ( #+ = Abbruch ) :");
    scanf("%s",filename);
    if(filename[0] == '#' && filename[1] == '+')
       return(NULL);
   }
  }

/************************************************
 *
 *   Lesen aus dem Textfile
 *
 ************************************************/

readname(filehandle,buffer)
 struct FileHandle *filehandle;
 STRPTR buffer;
 {
  FOREVER 
   {
    if( NULL == Read(filehandle,buffer,1L))
      break;
    if(*buffer == 10)
      break;
    buffer++;
   }
  *buffer = 0;
  return();
 }
  
/************************************************
 *
 *   Verwaltung der Animationserstellung
 *
 ************************************************/

 make_anim()
 {
  WORD i;
 
  if(dbuf)dbuf = FALSE;
  if(scr)CloseScreen(scr);
  if(bscr)CloseScreen(bscr);
  scr = bscr = NULL;

  printf("\n\nWelcher Textfile  ");
  printf("( #t = letzter Tempfile ?:\n");
  scanf("%s",namebuf);
  if( namebuf[0] == '#' && namebuf[1] == 't' )
   strcpy(namebuf,tempfile);
  
  fh = open_file(namebuf);
  if(!fh)return();

  readname(fh,namebuf);

  /* den Filenamen des 1.Bildes festhalten */
  strcpy(firstfile,namebuf);

  /* nun alle IFF Daten in den Speicher holen */
  read_IFF(firstpic,firstfile);

  /* entsprechend den Daten des 1.Bildes die Daten
   * nun einen Screen aufmachen, die Farben setzen
   * und einen Screen-Buffer bereitstellen */
  scr = open_scr();
  set_colors(scr);

  /* nun die IFF-Bilddaten lesen und das Bild auf
   * den Screen bringen */
  read_body(firstpic,scr);
  
  /* und das ganze im Screen-Buffer sichern */
  get_planebuffer();
  copy_screen();

  /* Das Start Bild ist nun geladen, nun folgt
   * der Rest */

  FOREVER
   {
    WBenchToFront();

    /* Name des folgenden Bildes aus dem Textfile 
       holen */
    readname(fh,namebuf);

    /* Testen ob Ende erreicht */
    if(namebuf[0] == '#')break;

    /* Das Laden der folgenden Bilder erfolgt so
       wie beim 1.Bild, nur wird kein Screen
       geoeffnet und die Farben bleiben immer
       die des ersten Bildes... */
    read_IFF(nextpic,namebuf);

    /* ... nur wird getestet ob das neue Bild die-
         selben Dimensionen hat */
    if(cmp_iffdata(firstpic,nextpic))
     {
      ScreenToFront(scr); /* damit man es sieht */
      read_body(nextpic,scr);
     
      /* Nun erfolgt die Differenzenerstellung 
         zum vorhergehenden Bild. Ist nicht
         genug Platz fuer diese Differenzenequenz 
         so wird dies erkannt und abgebrochen  */
      if(check_diff())

      /* Wenn zu wenig Speicher */
      ende("Out of Memory");

      copy_screen();
     }
    else
     {
      printf("Dieses Bild entsprach nicht dem");
      printf("Startbild und wird ignoriert\n");
     } 
   }
  WBenchToFront();
 
 
  /* An die letzte Stelle der Differenzdaten wird
    ein Abschluss WORD gesetzt */
  *anpos = 0xFFFF;

  anlen = anpos-animdata;anlen *= 2;anlen+=2;
  printf("Differenz-Daten = %ld Bytes\n",anlen);
  anpos = animdata;
   
  PLANELOOP
    if(BUFF_PLANE)FreeMem (BUFF_PLANE,planesize);

  if(fh) Close(fh);
  fh = NULL;

  return(); 
 }


/*************************************************
 *
 *   Verwaltung der Animationswiedergabe
 *
 ************************************************/

show_anim()
 {
  LONG delay,loops;
  printf("Laden des Startbildes ...\n\n");
  /* erstes Bild wieder laden */
  read_IFF(nextpic,firstfile);
  read_body(nextpic,scr);
  

  FOREVER
   {
    WBenchToFront();
    printf("Wieviel 1/50 sek. Pause zwischen ");
    printf("den Bildern ? (Negativ = Abbruch)");
    scanf("%ld",&delay);
    if( delay < NULL )break;

    printf("Anzahl der Wiederholungen ?\n");
    printf("!! Wiederholungen sind nur dann");
    printf(" sinnvoll wenn das erste Bild ");
    printf("gleich dem letzten ist\n");
    printf("(Negativ = Abbruch)");
    scanf("%ld",&loops);
    if( loops < NULL )break;

    ScreenToFront(scr);
    playanim(delay,loops);
   }
  return();     
  }

/*************************************************
 *
 *   Allgemeines Oeffnen von ...
 *
 ************************************************/

startup()
 {

  /* wichtigen Libraries */
  IntuitionBase = INTU_BASE
  if(!IntuitionBase)
     ende("keine IntuitionBase"); 

  GfxBase = GRAF_BASE
  if(!GfxBase)
      ende("keine GfxBase");
 
  dw = IntuitionBase->ActiveWindow;

  /* Speicherplatz fuer IFF Daten */
  firstpic = (struct iffpic *)AllocMem
         ((LONG)sizeof(struct iffpic),MEMF_CLEAR);
  nextpic = (struct iffpic *)AllocMem
         ((LONG)sizeof(struct iffpic),MEMF_CLEAR);
 
  /* Speicherplatz fuer die Differenzdaten 
      zuerst schauen wie gross der groesste freie
      Block ist */
  anim_len = (ULONG)
             AvailMem(MEMF_LARGEST);

  /* 60 KByte fuer den Rest uebrig lassen */ 
  anim_len -= 60000L;
  anpos = animdata = (UWORD *)AllocMem
                     (anim_len,MEMF_CLEAR);

  return();
 } 


/*************************************************
 *
 *   Textfile Erstellung
 *
 ************************************************/

fastfile()
 {
  struct FileHandle *fih;
  UBYTE co=0,LF=10,str[70];

  printf("\nName des Tempfiles ?:");
  scanf("%s",tempfile);  
  fih = Open(tempfile ,MODE_NEWFILE);
  if(!fih)
   {
    printf("Kein File möglich\n");
    return(0);
   }

   FOREVER
   {
    printf("Zeile eingeben :");
    scanf("%s",str);
    
    if(Write(fih,str,(LONG)strlen(str)) !=
            (LONG)strlen(str))
     { remove(fih,tempfile); return(); }

    if(Write(fih,&LF,1L) != 1L)
     { remove(fih,tempfile); return(); }

    if( str[0] == '#' )break;
   }

  if(fih)  Close(fih);
  fih = NULL; 
  return();
 }


/*************************************************
 *
 *   Speichern einer Animation
 *
 ************************************************/

save_anim()
 {
  struct FileHandle *fih;
  UBYTE LF=10, savename[70];

  printf("SPEICHERN ! Filename ?:");
  scanf("%s",savename);

  fih = Open(savename,MODE_NEWFILE);
  if(!fih)return(1);

  printf("schreibe name\n");
  if(Write(fih,firstfile,(LONG)strlen(firstfile))
      != (LONG)strlen(firstfile))
     { remove(fih,savename); return(1); }

  printf("schreibe linefeed\n");
  if(Write(fih,&LF,1L) != 1L)
     { remove(fih,savename); return(1); }

  printf("schreibe daten von %ld Bytes\n",anlen);
  if(Write(fih,(UBYTE *)animdata,anlen) != anlen)
     { remove(fih,savename); return(1); }

  if(fih) Close(fih);
  fih = NULL;
  return(0);
 } 

/*************************************************
 *
 *   Animationen Laden
 *
 ************************************************/

load_anim()
 {
  UBYTE loadname[70];
  struct FileHandle *fih;

  if(dbuf)dbuf = FALSE;
  if(scr)CloseScreen(scr);
  if(bscr)CloseScreen(bscr);
  scr = bscr = NULL;

  printf("LADEN ! Filename ?:");
  scanf("%s",loadname);

  fih = open_file(loadname);
  if(!fih)return(1);

  readname(fih,firstfile);
  printf("Name des Startbildes : %s\n",firstfile);
  read_IFF(firstpic,firstfile);
  scr = open_scr();
  ScreenToBack(scr);
  set_colors(scr); 

  anlen = Read(fih,(UBYTE *)animdata,anim_len);

  return(0);
 }
  
  
/*************************************************
 *
 *   Schreibgestoerte Files entfernen
 *
 ************************************************/

remove(filha,remfile)
 struct FileHandle *filha;
 STRPTR remfile;
 {
  printf("Fehler beim Schreiben in %s\n",remfile);

  Close(filha);
  filha = NULL;
  printf("gescl. fh = %ld\n",filha);

  if(DeleteFile(remfile))
    printf("File ist entfernt\n");
  else
    printf("File konnte nicht entfernt werden\n");

  return();
 }


/************************************************
 *
 *  ein gut funftionierendes getchar(); 
 *
 ************************************************/

UBYTE getkey(readwindow)
 struct Window *readwindow;
 {
  STRPTR cp;
  struct MsgPort *wpBuf,*upBuf;
  struct IntuiMessage *mess;

  wpBuf = readwindow->WindowPort;
  upBuf = readwindow->UserPort;
  ModifyIDCMP(readwindow,VANILLAKEY);

  FOREVER
   {
    Wait(1L<<readwindow->UserPort->mp_SigBit);

    while(mess = (struct IntuiMessage *)
                      GetMsg(readwindow->UserPort))
     {
      if(mess->Class == VANILLAKEY);
       {
        cp = (STRPTR)&mess->Code;
        ReplyMsg(mess);
        ModifyIDCMP(readwindow,NULL);
        readwindow->UserPort = upBuf;
        readwindow->WindowPort = wpBuf;
        return( *(cp+1) );
       }
      ReplyMsg(mess);
     }
   }
 }


dbmode()
 {
  WORD i;

  if(!scr)return(NULL);

  if(!dbuf)
   {
    bscr = open_scr();
    if(!bscr)
     {
      printf("\nNicht genug CHIP-RAM fuer ");
      printf(" zweiten Screen\n");
      return(0);
     }
     ScreenToBack(bscr);
    LoadRGB4(&bscr->ViewPort,colortable,
                                (LONG)colnum);
    PLANELOOP
     BUFF_PLANE = bscr->BitMap.Planes[i];
    return(1);
   }
  if(bscr)CloseScreen(bscr);
  bscr = NULL;
  return(0);
 } 
  
