/* Copyright © 1991 Lake Forest Logic Inc. */
/* SHAZAM: Macro Paint picture viewer version 1.1 */

/*

This program is designed to display dynamic hires images created with
Macro Paint, the 4096 color high-resolution paint program from Lake
Forest Logic. The source code is provided so that others can support
Macro Paint images in their viewer programs. There is quite a bit
of room for improvement in this program but we felt it was important
to release a usable viewer as soon as possible. 1/31/91

This program is version 1.1: bug reports and suggestions are welcome
and may be sent to:

	Lake Forest Logic Inc.
	28101 Ballard Road, Unit E
	Lake Forest, IL 60045
	(708)816-6666 FAX: (708)680-0832
	BBS: (708)680-0590 HST, 24 hours
	BIX: equack
	PLink: LFL*ERIK

The program was compiled using SAS/C Version 5.10a. The command
"LC -rr -L shazam" should suffice to compile and link it.

Permission is granted to distribute this file for non-commercial
purposes. Portions of the compiled code may be incorporated into your own
programs (commercial or otherwise), but you may not distribute a modified
version of this source file without permission.

*/

#include <exec/types.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <graphics/gfx.h>
#include <graphics/gfxbase.h>
#include <graphics/view.h>
#include <graphics/copper.h>
#include <hardware/custom.h>
#include <proto/all.h>
#include <stdio.h>

/* short hand */

#define NL (0)
#define CINIT(c,n)	{ UCopperListInit(c,n); }
#define CWAIT(c,a,b)	{ CWait(c,a,b);CBump(c); }
#define CEND(c)		{ CWAIT(c,10000,255); }
#define CMOVE(c,a,b)	{ CMove(c,(long)&a,b);CBump(c); }

#define MakeID(a,b,c,d)  ( (LONG)(a)<<24L | (LONG)(b)<<16L | (c)<<8 | (d) )

#define ID_FORM MakeID('F','O','R','M')
#define ID_MPCT MakeID('M','P','C','T')
#define ID_DYCP MakeID('D','Y','C','P')
#define ID_CTBL MakeID('C','T','B','L')
#define ID_BMHD MakeID('B','M','H','D')
#define ID_CMAP MakeID('C','M','A','P')
#define ID_BODY MakeID('B','O','D','Y')

#define BUFSIZE (8192)
#define UGetByte()	(*source++)
#define UPutByte(c)	(*dest++ = (c))

/* some global data */

struct View *vshift;
struct Screen *screen=0;
struct NewScreen plane;
struct GfxBase *GfxBase;
struct IntuitionBase *IntuitionBase=0;
struct ViewPort *vp;

int COPHEIGHT;
char DISPLAY=1,LACEME=0,OSCAN=16;
UWORD inmap[64],*Coppers;
UWORD colors[16]={0x000,0x111,0x222,0x333,0x444,0x555,0x666,0x777,
                  0x888,0x999,0xaaa,0xbbb,0xccc,0xddd,0xeee,0xfff};

struct Custom *custom;
struct UCopList *ucop=0;

struct BMHD
  {
    UWORD w, h;
    WORD  x, y;
    UBYTE nPlanes;
    UBYTE masking;
    UBYTE compression;
    UBYTE pad1;
    UWORD transparentColor;
    UBYTE xAspect, yAspect;
    WORD  pageWidth, pageHeight;
  };

/* function prototypes */

void Shazam(BPTR);
void PutCop(void);
void Chunker(BPTR,ULONG *,int);
void GetScreen(struct BMHD *header);
void UnScreen(void);
char UnpackRLL(BYTE **,BYTE **,int,int);


main(int argc,char **argv)
  {
   ULONG handle;

   printf("\n\233;1mSHAZAM\233;0m V1.1 © 1991 Lake Forest Logic Inc.\n\n");
   
   if(argc<2)
    {
     printf(" Syntax: SHAZAM <macro paint file>\n");
    }
   else
    {
     handle=Open(argv[1],MODE_OLDFILE);
     if(!handle)
      {
       printf(" Error: unable to open file \233;32m%s\233;0m\n",argv[1]);
       return(1);
      }
     Shazam(handle);
     Close(handle);
    }
   printf("\n");
   return(DISPLAY);
  }

/* get started */

void Shazam(BPTR handle)
  {
   ULONG *buffer;
   int result;

   buffer=AllocMem(BUFSIZE,0);
   if(!buffer)
    {
     printf(" Error: not enough memory.\n");
     DISPLAY=1;
     return;
    }
   result=Read(handle,buffer,12);
   if(result!=12)
    {
     printf(" Error: unable to read header.\n");
     DISPLAY=1;
     return;
    }
   if(buffer[0]!=ID_FORM)
    {
     printf(" Error: not an IFF file.\n");
     DISPLAY=1;
     return;
    }

   Chunker(handle,buffer,buffer[1]-4);

   UnScreen();

   FreeMem(buffer,BUFSIZE);
   return;
  }

/* chop up those IFF chunks */

void Chunker(BPTR handle,ULONG *buffer,int total)
  {
   int here,result,size,loop,planes,lines,bites,line,plane;
   struct BMHD *bmhd;
   UBYTE *collar, *source,*dest;
   here=0;

   custom=(struct Custom *) 0x00DFF000;

   while(here<total)
    {
     if(here==1) return;
     if(here&1) Read(handle,buffer,1);
     result=Read(handle,buffer,8);
     if(result!=8)
      {
       printf(" Error: unable to read chunk header.\n");
       DISPLAY=1;
       return;
      }
    size=buffer[1];
    if(buffer[0]==ID_MPCT)
     {
      DISPLAY=0;
     }
    if(buffer[0]==ID_BODY)
     {
      if(screen)
       {
        collar=AllocMem(size,0);
        if(!collar)
         {
          printf(" Error: unable to allocate BODY.\n");
          DISPLAY=1;
         }
        else
         {
          result=Read(handle,collar,size);
          if(result!=size) printf(" Error: unable to read BODY.\n");
          lines=screen->BitMap.Rows;
          planes=screen->BitMap.Depth;
          bites=screen->BitMap.BytesPerRow;
          source=collar;

          for(line=0;line<lines;line++)
           {
            for(plane=0;plane<planes;plane++)
             {
              dest=screen->BitMap.Planes[plane]+line*bites;
              result=UnpackRLL(&source,&dest,size,bites);

              if(result)
               {
                printf(" Error %d: unable to unpack BODY L:%d P:%d.\n",
                 result,line,plane);
                DISPLAY=1;
                FreeMem(collar,size);
                here+=size+8;
                return;
               }
             }
           }
          FreeMem(collar,size);
         }
       }
     }
    else if(buffer[0]==ID_CMAP)
     {
      result=Read(handle,buffer,size);
      if(screen)
       {
        collar=(BYTE *)buffer;
        for(loop=0;loop<size/3;loop++)
         {
          inmap[loop]=((collar[loop*3]>>4)<<8)+((collar[loop*3+1]>>4)<<4)+(collar[loop*3+2]>>4);
          SetRGB4(&screen->ViewPort,
           loop,collar[loop*3]>>4,collar[loop*3+1]>>4,collar[loop*3+2]>>4);
         }
       }
     }
    else if(buffer[0]==ID_CTBL)
     {
      Coppers=AllocMem(size,0);
      if(!Coppers) DISPLAY=1;
      else
       {
        COPHEIGHT=size/32;
        result=Read(handle,Coppers,size);
        if(!DISPLAY)
         {
          colors[0]=Coppers[0];
          colors[1]=Coppers[1];
          colors[2]=Coppers[2];
          colors[3]=Coppers[3];
          LoadRGB4(&screen->ViewPort,colors,20);
         }
       }
     }
    else if(buffer[0]==ID_BMHD)
     {
      result=Read(handle,buffer,size);
      if(!DISPLAY)
       {
        bmhd=(struct BMHD *)buffer;
        GetScreen(bmhd);
       }
      else
       {
        printf(" Not a Macro Paint picture file.\n");
        DISPLAY=1;
       }
     }
    else
     {
      if(size%BUFSIZE)
        {
         result=Read(handle,buffer,size%BUFSIZE);
         if(result!=size%BUFSIZE)
          {
           printf(" Error: unable to read chunk body.\n");
           DISPLAY=1;
           return;
          }
        }
       if(size/BUFSIZE) for(loop=0;loop<size/BUFSIZE;loop++)
        {
         result=Read(handle,buffer,BUFSIZE);
         if(result!=BUFSIZE)
          {
           printf(" Error: unable to read chunk body.\n");
           DISPLAY=1;
           return;
          }
        }
      }
     here+=size+8;
    }
  }

/* kick in the display and wait for the end */

void UnScreen()
 {
  int preserve;
  if(screen)
   {
    ScreenToFront(screen);

    vshift=ViewAddress();
    preserve=vshift->DyOffset;

    if(GfxBase->LibNode.lib_Version<36) /* dead reckoning */
     {
      if((COPHEIGHT>>1)>GfxBase->NormalDisplayRows) vshift->DyOffset=14;
     }
    else
     {
      if((COPHEIGHT>>1)>GfxBase->NormalDisplayRows) vshift->DyOffset=22;
     }

    PutCop();
    getchar();
    vshift->DyOffset=preserve;

    FreeVPortCopLists(&screen->ViewPort);
    RemakeDisplay();
    CloseScreen(screen);
    if(Coppers) FreeMem(Coppers,COPHEIGHT<<5);
   }

  if(IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
  if(GfxBase) CloseLibrary((struct Library *)GfxBase);
 }

/* our copper list 'magic' */

void PutCop()
  {
   int line,creg,top;

   ucop=(struct UCopList *)AllocMem(sizeof(struct UCopList),MEMF_CLEAR);

   top=COPHEIGHT;
   if(LACEME) top=top/2;

   CINIT(ucop,top*13);

   for(line=0;line<top;line++)
    {
     if(LACEME) 
      {
       CWAIT(ucop,(line-1)<<1,112);
      }
     else
      {
       CWAIT(ucop,(line-1),112);
      }

     for(creg=4;creg<OSCAN;creg++)
      {
       if(LACEME)
        {
         CMOVE(ucop,custom->color[creg],Coppers[(line<<5)+creg]);
        }
       else
        {
         CMOVE(ucop,custom->color[creg],Coppers[(line<<4)+creg]);
        }
      }
    }
   CEND(ucop);

  printf("Press return.\n");
  Forbid();
  vp->UCopIns=ucop;
  Permit();
  MakeScreen(screen);
  RethinkDisplay();
 }

/* throw up a screen */

void GetScreen(struct BMHD *header)
 {
  IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",33);
  if(!IntuitionBase) 
   {
    printf(" Error: unable to open Intuition!\n");
    return;
   }

   GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",33);
   if(!GfxBase)
    {
     printf(" Error: unable to open graphics.\n");
     return;
    }

   plane.LeftEdge=0;
   plane.TopEdge=0;
   plane.DetailPen=2;
   plane.BlockPen=1;
   plane.Type=CUSTOMSCREEN|SCREENQUIET;
   plane.Font=0;
   plane.DefaultTitle="SHAZAM Screen";
   plane.Gadgets=0;
   plane.CustomBitMap=0;
   plane.ViewModes=0;

   plane.Width=header->w;
   plane.Height=header->h;
   plane.Depth=header->nPlanes;
   plane.ViewModes|=HIRES;
  
   if(header->h>300) { plane.ViewModes|=LACE; LACEME=1; }

   if(header->w>640) OSCAN=14;

   screen=OpenScreen(&plane);
   vp=&screen->ViewPort;
   return;
  }

/* BODY voodoo */

char UnpackRLL(BYTE **pSource,BYTE **pDest,int srcBytes0,int dstBytes0)
  {
   int srcBytes,dstBytes;
   BYTE *source, *dest, c;
   WORD n,minus128=-128;

   source=*pSource;
   dest=*pDest;
   srcBytes=srcBytes0;
   dstBytes=dstBytes0;

   while(dstBytes>0) 
    {
     if((srcBytes-=1)<0) return(1);
     n=UGetByte();

     if (n >= 0)
      {
       n += 1;
       if((srcBytes-=n)<0) return(2);
       if((dstBytes-=n)<0) return(3);
       do
        {
         UPutByte(UGetByte());
        }
       while(--n>0);
      }
     else if(n!=minus128)
      {
       n = -n + 1;
       if((srcBytes-=1)<0) return(4);
       if((dstBytes-=n)<0) return(5);
       c = UGetByte();
       do
        {
         UPutByte(c);
        }
       while(--n>0);
      }
    }

   *pSource=source;  *pDest=dest;
   return(0);
  }
