/**************************************************************************/
/* pro.c                                                       27.01.88   */
/* Version 2.0                                                            */
/*                                                                        */
/*                                                                        */
/* Projektor zum Abspielen mehrerer IFF-Bilder hintereinander             */
/*                                                                        */
/* fuer Markt+Technik geschrieben von Heiko Knappe                        */
/*                                                                        */
/* Bilder  im IFF-Format koennen geladen werden und dann schnell hinter-  */
/* einander abgespielt werden. Dadurch entsteht die Wirkung einer kurzen  */
/* Filmsequenz. Wieviel Bilder geladen werden koennen und damit die Lae-  */
/* nge der Sequenz haengt vom verfuegbaren Speicher ab.                   */
/* Der Aufruf des Programms erfolgt:                                      */
/*   pro.run Bild1 Bild2 ...                                              */
/* Jetzt  werden die einzelnen Bilder geladen. Tritt ein Fehler auf,  so  */
/* wird dies gemeldet. (Fehler 1: File konnte nicht richtig geladen wer-  */
/* den  oder kein  Speicherplatz fuer Puffer verfuegbar / Fehler 2: kein  */
/* IFF-Format / Fehler 100: nicht genug Speicher) Nachdem nun die Bilder  */
/* geladen sind, wird ein Consolfenster zur Steuerung und Eingabe geoeff- */
/* net. Folgende Eingaben sind moeglich:                                  */
/* e flagsingle        Einzelbild an (1) oder aus (0)                     */
/* p flagvor,flagzu    Vorwaerts an/aus, Rueckwaerts an/aus               */
/* n anz               Anzahl der Durchlaeufe                             */
/* z zeit              Abspielrate                                        */
/* b bildstart,bildend Anfangs- Endbild beim Abspielen                    */
/* a                   Ausgabe der gesetzten Parameter                    */
/* d                   default Werte setzen                               */
/* s                   Film starten                                       */
/* x                   Programm verlassen                                 */
/**************************************************************************/


#include <libraries/dos.h>
#include <exec/types.h>
#include <exec/exec.h>
#include <exec/memory.h>
#include <graphics/gfx.h>
#include <graphics/gfxmacros.h>
#include <graphics/copper.h>
#include <graphics/gels.h>
#include <graphics/regions.h>
#include <graphics/clip.h>
#include <graphics/text.h>
#include <graphics/gfxbase.h>
#include <graphics/view.h>
#include <hardware/dmabits.h>
#include <hardware/custom.h>
#include <hardware/blit.h>


#define BMHDChunk 1
#define CMAPChunk 2
#define BODYChunk 3
#define CAMGChunk 4
#define Unknown   5

#define N 100         /* moegliche Anzahl der Bilder              */
#define OUT_MEM 100


struct CMAPC          /* Definition des CMAP-Chunks               */
{
 int anz;             /* Anzahl der Farben                        */
 struct ColorRegister /* Definition einer Farbe                   */
 {
  UBYTE red;
  UBYTE green;
  UBYTE blue;
 } Color[64];         /* lieber zuviel als zuwenig                */
};

struct FORMC          /* Definition des Anfang-Chunks             */
{
 char  FORMName[4];   /* handelt es sich um ein IFF-Format?       */
 long  len;           /* Gesamte Laenge des Files                 */
 char  ILBM[4];       /* handelt es sich um ILBM-Format?          */
};

struct BMHDC          /* Definition des BMHD                      */
{
 char  BMHDName[4];
 long  lenBMHD;       /* Laenge des BMHD-Chunks                   */
 short Width;         /* Breite des Bildes                        */
 short Height;        /* Hoehe des Bildes                         */
 short xpos;          /* x-Position des Bildes                    */
 short ypos;          /* y-Position des Bildes                    */
 char  Depth;         /* Anzahl der BitPlanes                     */
 char  mask;          /* Mask-Bit                                 */
 char  compression;   /* Bild 'compressed'?                       */
 char  null;
 short color;
 char  xaspect;
 char  yaspect;
 short PWidth;
 short PHeight;
};

struct CHUNKID        /* hier werden die zu analysierenden Bytes  */
{                     /* abgelegt                                 */
 char  Name[4];       /* Name des Chunks                          */
 int   len;           /* Laenge des Chunks                        */
};

struct HEADER         /* diese Struktur enthaelt die Zeichenfolge */
{                     /* um den Chunk zu erkennen.                */
 char bmhd[4];
 char cmap[4];
 char body[4];
 char camg[4];
};

struct CAMGC          /* Definition des CAMG                      */
{
 char CAMGName[4];
 long lenCAMG;
 long ViewModes;
};

struct CMAPC CMAPl;
struct FORMC FORMl;
struct BMHDC BMHDl;
struct CHUNKID CHUNKid;
struct HEADER Header=
{
 'B','M','H','D',    /* BMHD-Chunk                                */
 'C','M','A','P',    /* CMAP-Chunk                                */
 'B','O','D','Y',    /* BODY-Chunk                                */
 'C','A','M','G',    /* CAMG-Chunk                                */
};

char form[]="FORM";
char ilbm[]="ILBM";

struct View MyView[N];
struct View *OldView;
struct ViewPort MyViewPort[N];
struct RasInfo MyRasInfo[N];
struct BitMap MyBitMap[N];
struct RastPort MyRastPort[N];
struct GfxBase *GfxBase;
struct DosBase *DosBase;


long laenge,file,end,*waste;
BYTE *speicher,*zeiger,*zeiger0,err;
USHORT i,j,Width[N],Height[N],Depth[N],ViewModes[N],bild,bildanz,bildranz;
USHORT TopEdge[N],LeftEdge[N];
UBYTE *port;   /* fuer Einzelbild (s. auch frame.c) */
/* Anzahl der Durchlaeufe / Wartezeit / Vor-Zurueck / Einzelbild */
int an,anz=1,zeit=10,flagvor=1,flagzu=0,flagsingle=0,bildstart,bildend;



/******************/
/* Haupt-Programm */
/******************/

main(argc,argv)
short argc;
char *argv[];
{
 if(argc<2)
 {
    printf("%s: <Bild-File[1]> <Bild-File[2]> ...\n",argv[0]);
    exit(FALSE);
 }
 bildanz=argc-1;
 port=12574721;
 open_();
 for(bild=bildranz=0;bild<bildanz;++bild,++bildranz) /* Laden der Bilder */
    if((err=load(argv[bild+1],bildranz))!=0)
    {
       --bildranz;
       printf("Bild %s kann nicht geladen werden (Fehler:%d)!\n",
               argv[bild+1],err);
       if(err==OUT_MEM) bild=bildanz;
    }
 if(bildranz==0)
    close_();
 printf("\n%d Bilder geladen.\n",bildranz);
 bildend=bildranz-1; bildstart=0;
 for(;;)
 {
    eingabe();
    for(an=0;an<anz;++an)
    {
       if(flagvor==1)
       {
          for(bild=bildstart;bild<=bildend;++bild)
          {
             LoadView(&MyView[bild]);
             if(flagsingle==1)
             {
                while((*port&64)==64); /* Warten bis Taste gedrueckt */
                Delay(10);
             }
             else
               Delay(zeit); /* Warten */
          }
       }
       if(flagzu==1)
       {
          for(bild=bildend-1;bild>bildstart;--bild)
          {
             LoadView(&MyView[bild]);
             if(flagsingle==1)
             {
                while((*port&64)==64); /* Warten bis Taste gedrueckt */
                Delay(10);
             }
             else
               Delay(zeit); /* Warten */
          }
       }
    }
    LoadView(OldView); /* alten View wieder laden */
 }
 close_();
}


/* Einlesen ueber Console */
eingabe()
{
 char line[80];

 if((file=Open("con:0/0/320/80/Eingabe",MODE_NEWFILE))==0)
 {
    printf("Console kann nicht geoeffnet werden!\n");
    close_();
 }
 while(inline(line,file,80)!=0)
 {
    switch(line[0])
    {
       case 10 : /* Return-Zeile */
                 break;
       case 'z': /* Zeit-Definition */
                 if(sscanf(line+2,"%d",&zeit)!=1)
                    printf("Eingabefehler!\n");
                 break;
       case 'b': /* Anfangs- Endbild */
                 if(sscanf(line+2,"%d,%d",&bildstart,&bildend)!=2)
                    printf("Eingabefehler!\n");
                 --bildstart,--bildend;
                 if(bildstart<0) bildstart=0;
                 if(bildstart>(bildranz-1)) bildstart=bildranz-1;
                 if(bildend<0) bildend=0;
                 if(bildend>(bildranz-1)) bildend=bildranz-1;
                 break;
       case 'n': /* Anzahl der Durchlaeufe */
                 if(sscanf(line+2,"%d",&anz)!=1)
                    printf("Eingabefehler!\n");
                 break;
       case 'p': /* Vor-Rueckwaerts abspielen */
                 if(sscanf(line+2,"%d,%d",&flagvor,&flagzu)!=2)
                    printf("Eingabefehler!\n");
                 if(flagvor!=0) flagvor=1;
                 if(flagzu!=0) flagzu=1;
                 break;
       case 'e': /* Einzelbild */
                 if(sscanf(line+2,"%d",&flagsingle)!=1)
                    printf("Eingabefehler!\n");
                 if(flagsingle!=0) flagsingle=1;
                 break;
       case 'd': /* default Werte setzen */
                 anz=1,zeit=10,flagvor=1,flagzu=0,flagsingle=0;
                 bildstart=0; bildend=bildranz-1;
                 ausgabe();
                 break;
       case 'a': /* Ausgabe der Parameter */
                 ausgabe();
                 break;
       case 's': /* Animation starten */
                 Close(file);
                 return(0);
       case 'x': /* Programm beenden */
                 Close(file);
                 close_();
       default : /* Fehler aufgetreten */
                 printf("Eingabefehler!\n");
                 break;
    }
 }
}

inline(c,handle,len) /* eine Zeile des Files einlesen */
char c[];
long *handle;
short len;
{
 short i=0,ende=0;

 while((i<len)&&(c[i-1]!=10)&&((ende=Read(handle,&c[i],1))!=0))
    ++i;
 c[i]=0;
 return(ende);
}

ausgabe()
{
 printf("\nParameter:\n");
 printf("  %d Bilder geladen\n",bildranz);
 printf("  Abspielen von %d nach %d\n",bildstart+1,bildend+1);
 printf("  Abspielrate:%d\n  Durchlaeufe:%d\n",zeit,anz);
 printf("  vorwaerts:");
 if(flagvor==1) printf(" an  ");
 else printf(" aus  ");
 printf("  rueckwaerts:");
 if(flagzu==1) printf(" an\n");
 else printf(" aus\n");
 printf("  Einzelbild:");
 if(flagsingle==1) printf(" an\n");
 else printf(" aus\n");
 printf("FastMem=%d ChipMem=%d\n",AvailMem(MEMF_FAST),AvailMem(MEMF_CHIP));
}


/*********************/
/* Initialisierungs- */
/* routinen          */
/*********************/

open_()
{
 if((GfxBase=OpenLibrary("graphics.library",0))==0)
    close_();
 if((DosBase=OpenLibrary("dos.library",0))==0)
    close_();
 OldView=GfxBase->ActiView; /* alten View retten */
}

close_()
{
 if(bildranz!=0)
 {
    LoadView(OldView); /* alte View-Struktur wieder initialisieren */
    for(bild=0;bild<bildranz;++bild)
       remove_bild(bild);
 }
 if(GfxBase!=0) CloseLibrary(GfxBase);
 if(DosBase!=0) CloseLibrary(DosBase);
 exit(0);
}


init_bild(n)
int n;
{
 InitView(&MyView[n]); /* View-Struktur initialisieren */
 InitVPort(&MyViewPort[n]); /* ViewPort-Struktur initialisieren */
 MyView[n].Modes=MyViewPort[n].Modes=ViewModes[n];
 MyView[n].ViewPort=&MyViewPort[n];
 MyViewPort[n].DWidth=Width[n];
 MyViewPort[n].DHeight=Height[n];
 MyViewPort[n].RasInfo=&MyRasInfo[n];
 InitBitMap(&MyBitMap[n],Depth[n],Width[n],Height[n]); /* BitMap initialisieren */
 MyRasInfo[n].BitMap=&MyBitMap[n]; /* RasInfo-Struktur inittialisieren */
 MyRasInfo[n].RxOffset=LeftEdge[n];
 MyRasInfo[n].RyOffset=TopEdge[n];
 MyRasInfo[n].Next=0;
 /* BitMap-Speicher allocatieren */
 for(i=0;i<Depth[n];++i)
    if((MyBitMap[n].Planes[i]=AllocRaster(Width[n],Height[n]))==0)
    /* nicht genug Speicher */
    {
       printf("\n\n!!!Nicht genug Speicher!!!\n");
       for(j=0;j<i;++j)
          FreeRaster(MyBitMap[n].Planes[j],Width[n],Height[n]);
       return(FALSE);
    }
 MakeVPort(&MyView[n],&MyViewPort[n]);
 MrgCop(&MyView[n]);
 for(i=0;i<Depth[n];++i)
    BltClear(MyBitMap[n].Planes[i],RASSIZE(Width[n],Height[n]),0);
 /* Bild geladen -> Farben setzen */
 for(i=0;i<CMAPl.anz;++i)
    SetRGB4(&MyViewPort[n],i,(short)((float)CMAPl.Color[i].red/16),
            (short)((float)CMAPl.Color[i].green/16),
            (short)((float)CMAPl.Color[i].blue/16));
 return(TRUE);
}


remove_bild(n)
int n;
{
 for(i=0;i<Depth[n];++i)
    FreeRaster(MyBitMap[n].Planes[i],Width[n],Height[n]);
 FreeVPortCopLists(&MyViewPort[n]);
 FreeCprList(MyView[n].LOFCprList);
}


/*********************/
/* Laderoutinen      */
/*********************/

load(fname,n)
char *fname;
int n;
{
 short id;

 if(init_load_file(fname)==0)
 {
    close_load_file();
    return(1);
 }
 end=read_file(&FORMl,sizeof(FORMl));
 if((strncmp(&FORMl.FORMName,&form,4)!=0)||(strncmp(&FORMl.ILBM,&ilbm,4)!=0))
 {
   close_load_file();
   return(2);
 }
 while(end!=0)
 {
    end=read_file(&CHUNKid,sizeof(CHUNKid));
    if(end==8) id=test();
    else {end=0; break;};
    switch(id)        /* Welcher Chunk liegt vor? */
    {
    case BMHDChunk:
            BMHDl.lenBMHD=CHUNKid.len;
            end=read_file(&BMHDl.Width,CHUNKid.len);
            if((end<CHUNKid.len)) break;
            if(CHUNKid.len<(sizeof(BMHDl)-12))
            {
               close_load_file();
               return(2);
            }
            Width[n]=BMHDl.Width;
            Depth[n]=BMHDl.Depth;
            Height[n]=BMHDl.Height;
            LeftEdge[n]=BMHDl.xpos;
            TopEdge[n]=BMHDl.ypos;
            if(ViewModes[n]==0) /* ViewMode schon gesetzt? */
            {
               if(Width[n]>320) ViewModes[n]+=HIRES;
               if(Height[n]>250) ViewModes[n]+=LACE;
               if(Depth[n]==6) ViewModes[n]+=HAM;
            }
            break;
    case CMAPChunk:
            CMAPl.anz=CHUNKid.len/3;
            for(i=0;i<CMAPl.anz;++i)
               end=read_file(&(CMAPl.Color[i].red),3);
            break;
    case BODYChunk:
            if(init_bild(n)==FALSE)
            {
               close_load_file();
               return(OUT_MEM);
            }
            get_pic(n);
            break;
    case CAMGChunk:
            end=read_file(&ViewModes[n],2);
            end=read_file(&ViewModes[n],2); /* erst hier richtig */
            break;
    case Unknown:
            if((waste=AllocMem(CHUNKid.len,MEMF_CLEAR))==0)
              {end=0; break;}
            end=read_file(waste,CHUNKid.len);
            FreeMem(waste,CHUNKid.len);
            break;
    }
 }  
 close_load_file();
 return(0);
}


/* Um welchen Chunk handelt es sich? */
test()
{
 char *p;
 short i;

 for(i=0,p=&Header;
     (i<sizeof(Header)/4)&&(strncmp(p,&CHUNKid.Name,4)!=0);++i,p+=4);
 return(i+1);
}


/* Laden der BitPlanes */
get_pic(n)
int n;
{
 short count;
 char *next,*next0,*ne;

 printf("Bild %d -> %dx%d %d Planes\n",n,Width[n],Height[n],Depth[n]);
 printf("FastMem=%d ChipMem=%d\n",AvailMem(MEMF_FAST),AvailMem(MEMF_CHIP));
 if(BMHDl.compression!=0)         /* File compressed */
 {
    for(i=0;(i<BMHDl.Height)&&(end!=0);++i)
    {
       for(j=0;j<BMHDl.Depth;++j)
       {
          next0=next=MyBitMap[n].Planes[j]+BMHDl.Width/8*i;
          while((next-next0)<BMHDl.Width/8)
          {
             end=read_file(next,1);
             if(*next==-128) continue;  /* noop */
             if(*next>=0)
             {  
                count=1+(*next);
                end=read_file(next,count);
                next+=count;
                continue;
             }   
             if((*next<0)&&(*next!=-128))
             {
                count=1-(*next);
                end=read_file(next,1);
                for(ne=next;count>0;--count,*next=*ne,++next);
                continue;
             }
          }

       }
    }
 }
 else
 {
    for(i=0;i<BMHDl.Height;++i)
       for(j=0;j<BMHDl.Depth;++j)
          end=read_file(MyBitMap[n].Planes[j]+BMHDl.Width/8*i,BMHDl.Width/8);
 }
}


init_load_file(fname)
char *fname;
{
 if((laenge=filelaenge_(fname))==0)
    return(0);
 if((file=Open(fname,MODE_OLDFILE))==0)
    return(0);
 if((speicher=AllocMem(laenge,MEMF_CLEAR))==0)
    return(0);
 Read(file,speicher,laenge);
 zeiger=zeiger0=speicher;
 return(1);
}


filelaenge_(fname)
char *fname;
{
 struct FileInfoBlock *MyInfoBlock;
 long *MyLock,help,l;

 if((MyLock=Lock(fname,ACCESS_READ))==0)
    return(0);
 if((help=AllocMem(1000,MEMF_CLEAR))==0)
    return(0);
 MyInfoBlock=help+help%4;
 if((Examine(MyLock,MyInfoBlock))==0)
 {
    UnLock(MyLock);
    FreeMem(help,1000);
    return(0);
 }
 UnLock(MyLock);
 l=MyInfoBlock->fib_Size;
 FreeMem(help,1000);
 return(l);
}


close_load_file()
{
 if(speicher!=0) FreeMem(speicher,laenge);
 if(file!=0) Close(file);
}


read_file(p,anz)
BYTE *p;
int anz;
{
 while((zeiger<=speicher+laenge)&&(zeiger-zeiger0<anz))
 {
    *p=*zeiger;
    ++zeiger,++p;
 }
 zeiger0=zeiger;
}
