/********************************************** Date: 20-Feb-89 *****/
/*                                                                  */
/* Vedi.c - written by Diego Perini, based on Rett Andersen's ideas */
/*                                                                  */
/* Copyright (C) 1989, QUEST Software - Verona - IT                 */
/* All rights reserved                                              */
/*                                                                  */
/* Last modified: 22 March 1990        Registered Developer: ECI004 */
/*                                                                  */
/* Show any kind of IFF pictures including the new IFF_BEAM Picture */
/*                                                                  */
/********************************************************************/
/*                                                                  */
/* IFF Format and routines in pd courtesy of Electronic Arts and C= */
/*                                                                  */
/********************************************************************/
/*                                                                  */
/* Released in the public domain to spread the use of BeamPalettes. */
/*                                                                  */
/* Feel free to modify it and include it in your valuable projects. */
/*                                                                  */
/********************************************************************/

#include <exec/types.h>
#include <exec/tasks.h>
#include <exec/memory.h>
#include <exec/interrupts.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <graphics/view.h>
#include <graphics/gfxbase.h>
#include <graphics/gfxmacros.h>
#include <hardware/custom.h>
#include <hardware/intbits.h>
#include <hardware/dmabits.h>
#include <intuition/intuitionbase.h>
#include <workbench/workbench.h>
#include <workbench/startup.h>

#include <iff/ilbm.h>
#include <iff/readpict.h>

#include <functions.h>

#define LOCAL   static

#define VERSION   1
#define REVISION  18

#define MIN(a,b)   ((a)<(b)?(a):(b))
#define MAX(a,b)   ((a)>(b)?(a):(b))

/* MAX_SCAN_LINES value should be changed to allow for NTSC support */
#define MAX_COLS_WIDTH   768             /* max # of cols horizzont */
#define MAX_SCAN_LINES   592             /* max # of lines vertical */
#define MAX_COLOR_REGS    32             /* max # of color register */

#define EHB   EXTRA_HALFBRITE            /* EHB short like HAM.(dp) */

#define HX   MAX_COLS_WIDTH + 1          /* used in DisplayPic() to */
#define LX   MAX_COLS_WIDTH / 2 + 1      /* check picture x limit's */

#define HY   MAX_SCAN_LINES + 1          /* used in DisplayPic() to */
#define LY   MAX_SCAN_LINES / 2 + 1      /* check picture y limit's */

struct Preferences *Prefs;                 /* ptr to Preferences    */

struct GfxBase *GfxBase;                   /* ptr to GfxBase        */
struct IntuitionBase *IntuitionBase;       /* ptr to IntuitionBase  */

LOCAL struct ViewPort  *vp = {0};          /* the viewport we get   */
LOCAL struct BitMap     bm = {0};          /* the bitmap allocated  */

LOCAL struct Screen *screen;               /* my wonderfull screen  */
LOCAL struct Window *window;               /* my backdrop window    */

struct UCopList *ucoplist;                 /* for copper lists      */

struct Interrupt CopInt;                   /* for copper interrupts */

LOCAL ILBMFrame iFrame;                    /* current ILBM Frame    */

LOCAL struct WBStartup *wbStartup = NULL;  /* workbench arguments   */
extern struct WBStartup *WBenchMsg;        /* workbench message     */

LOCAL struct IntuiMessage *msg;            /* message structure     */

unsigned long
      class = NULL,                        /* message class         */
      code  = NULL,                        /* message code          */
      count = NULL;                        /* current scan line     */

BOOL Quit;                                 /* used in wait user end */

short NormalCols, NormalRows;           /* don't ask me about    */

int ScreenNTSC = 0;                        /* this set your system  */

extern UWORD BeamPalette[MAX_SCAN_LINES][MAX_COLOR_REGS];
                      /* hold the palette for 592 lines */
                      /* for Interlaced Overscan in PAL */

USHORT Black[32] = {0x0000,0x0000,0x0000,0x0000,
                    0x0000,0x0000,0x0000,0x0000,
                    0x0000,0x0000,0x0000,0x0000,
                    0x0000,0x0000,0x0000,0x0000,
                    0x0000,0x0000,0x0000,0x0000,
                    0x0000,0x0000,0x0000,0x0000,
                    0x0000,0x0000,0x0000,0x0000,
                    0x0000,0x0000,0x0000,0x0000};
                      /* used to clear the main screen  */

struct   NewScreen      ns = {
   0, 0,                                  /* LeftEdge and TopEdge   */
   8, 8,                                  /* Width and Height       */
   1,                                     /* Depth                  */
   0, 1,                                  /* DetailPen and BlockPen */
   NULL,                                  /* Special display modes  */
   CUSTOMSCREEN,                          /* Screen Type            */
   NULL,                                  /* Use my font            */
   NULL,                                  /* Title of the screen    */
   NULL,                                  /* No gadgets yet         */
   NULL                                   /* Ptr to CustomBitmap    */
   };

struct   NewWindow      nw ={
   0, 0,                                  /* LeftEdge and TopEdge   */
   8, 8,                                  /* Width and Height       */
   -1, -1,                                /* DetailPen and BlockPen */
   VANILLAKEY,                            /* Intuition IDCMP Flags  */
   ACTIVATE
   |SMART_REFRESH
   |RMBTRAP
   |BACKDROP
   |BORDERLESS,                           /* New Window Flags       */
   NULL, NULL,                            /* Gadget, Image pointers */
   NULL,                                  /* Title of the window    */
   NULL,                                  /* Put Screen ptr here    */
   NULL,                                  /* SuperBitMap pointer    */
   10, 10,                                /* MinWidth and MinHeight */
   768, MAX_SCAN_LINES,                   /* MaxWidth and MaxHeight */
   CUSTOMSCREEN                           /* Type of window         */
   };

LOCAL char MsgOkay[]        = { "(IFF_OKAY) Didn't find a FORM ILBM in the file." };
LOCAL char MsgEndMark[]     = { "(END_MARK) How did you get this message?" };
LOCAL char MsgDone[]        = { "(IFF_DONE) All done."};
LOCAL char MsgDos[]         = { "(DOS_ERROR) The DOS returned an error." };
LOCAL char MsgNot[]         = { "(NOT_IFF) Not an IFF file." };
LOCAL char MsgNoFile[]      = { "(NO_FILE) No such file found." };
LOCAL char MsgClientError[] = { "(CLIENT_ERROR) Vedi bug or insufficient RAM."};
LOCAL char MsgForm[]        = { "(BAD_FORM) A malformed FORM ILBM." };
LOCAL char MsgShort[]       = { "(SHORT_CHUNK) A malformed FORM ILBM." };
LOCAL char MsgBad[]         = { "(BAD_IFF) A mangled IFF file." };

LOCAL char *IFFPMessages[-(long)LAST_ERROR+1] = {
   MsgOkay,         /* IFF_OKAY     */
   MsgEndMark,      /* END_MARK     */
   MsgDone,         /* IFF_DONE     */
   MsgDos,          /* DOS_ERROR    */
   MsgNot,          /* NOT_IFF      */
   MsgNoFile,       /* NO_FILE      */
   MsgClientError,  /* CLIENT_ERROR */
   MsgForm,         /* BAD_FORM     */
   MsgShort,        /* SHORT_CHUNK  */
   MsgBad           /* BAD_IFF      */
   };

struct UCopList *
MakeBeamList(nRows,nCols) /*----- Generates Screen Copper List -----*/
int nRows;
int nCols;
   {
   struct UCopList *ucoplist;
   struct CopIns *copins;
   int i, j, step;

   if (vp->Modes & LACE)
      step = 2;
   else
      step = 1;

   SetRGB4(vp, 0L, 0L, 0L, 0L);
   for( j = 1 ; j < nCols ; j++ ) {
      SetRGB4(vp, (long)j,
                  (long)((BeamPalette[0][j] >> 8 ) & 0xf),
                  (long)((BeamPalette[0][j] >> 4 ) & 0xf),
                  (long)((BeamPalette[0][j] >> 0 ) & 0xf));
      }

   ucoplist = (struct UCopList *)AllocMem(12L,MEMF_PUBLIC|MEMF_CLEAR);

   CINIT(ucoplist, (long)(nRows + nRows * nCols + 2));

   for( i = step ; i < nRows ; i += step ) {
      copins = ucoplist->FirstCopList->CopPtr;
      CWAIT(ucoplist, (long)(i), 0L);
      copins->OpCode |= CPR_NT_LOF;

      for( j = 1 ; j < nCols ; j++ ) {
         copins = ucoplist->FirstCopList->CopPtr;
         CMOVE(ucoplist, custom.color[j], (long)BeamPalette[i][j]);
         copins->OpCode |= CPR_NT_LOF;
         }

      if (vp->Modes & LACE) {
         copins = ucoplist->FirstCopList->CopPtr;
         CWAIT(ucoplist, (long)(i), 0L);
         copins->OpCode |= CPR_NT_SHT;

         for( j = 1 ; j < nCols ; j++ ) {
            copins = ucoplist->FirstCopList->CopPtr;
            CMOVE(ucoplist, custom.color[j], (long)BeamPalette[i+1][j]);
            copins->OpCode |= CPR_NT_SHT;
            }
         }
      }

   CEND(ucoplist);

   return(ucoplist);
   }

struct BitMap *
getBitMap(ptilbmFrame) /*----- Open screen & return bitmap ptr -----*/
ILBMFrame *ptilbmFrame;
{
   struct BitMap *destBitMap;                      /* a BitMap ptr  */

   ns.Width     = ptilbmFrame->bmHdr.w;            /* screen width  */
   ns.Height    = ptilbmFrame->bmHdr.h;            /* screen height */
   ns.Depth     = ptilbmFrame->bmHdr.nPlanes;      /* screen depth  */
   ns.Type     |= SCREENBEHIND;   /* screen opens in the back of Wb */
   ns.ViewModes = NULL;                            /* no modes yet  */

   /* if a CAMG chunk exists set the modes according to it's values */
   if ( ptilbmFrame->foundCAMG )
      ns.ViewModes = ptilbmFrame->camgChunk.ViewModes;
   else /* set the modes according to the dimensions of the picture */
      {
      if ( ns.Width  > 384 )                 /* max width in lo-res */
         ns.ViewModes |= HIRES;

      if ( ns.Height > 296 )                 /* max height no laced */
         ns.ViewModes |= LACE;

      if ((ptilbmFrame->bmHdr.nPlanes == 6) &&
          (ptilbmFrame->nColorRegs  > 16))
         ns.ViewModes |= EHB; /* if 6 planes & 32 colors mode = EHB */

      if ((ptilbmFrame->bmHdr.nPlanes == 6) &&
          (ptilbmFrame->nColorRegs == 16))
         ns.ViewModes |= HAM; /* if 6 planes & 16 colors mode = HAM */
      }

   if ((screen = (struct Screen *)OpenScreen(&ns)) == NULL) return(0);

   vp = &screen->ViewPort;   /* viewport address for current screen */

   LoadRGB4(vp,&Black[0],(long)ptilbmFrame->nColorRegs);
       /* load the colormap for the current picture in the viewport */

   nw.Width  = ns.Width ; /* window width  = screen width  */
   nw.Height = ns.Height; /* window height = screen height */
   nw.Screen = screen;    /* window screen = actual screen */

   window = (struct Window *)OpenWindow(&nw);

   destBitMap = (struct BitMap *)screen->RastPort.BitMap;

   return(destBitMap);          /* destBitMap allocated */
   }

void
DisplayPic(ptilbmFrame) /*------------ Display picture -------------*/
ILBMFrame *ptilbmFrame;
{
   vp = &screen->ViewPort;   /* viewport address for current screen */

   /* the next two IF blocks are needed to center overscan pictures */
   /* picture width  must not be larger than 384 lo-res pixel horiz */
   /* picture height must not be larger than 296 lo-res pixel vert  */
   /* if the picture outruns the dimensions it will not be centered */

   if (((vp->Modes & HIRES) && (ns.Width < HX)) || (ns.Width  < LX))
      vp->DxOffset = (vp->Modes & HIRES)
                   ? ((NormalCols / 1) - ns.Width ) / 2 :
                     ((NormalCols / 2) - ns.Width ) / 2 ;

   if (((vp->Modes & LACE) && (ns.Height < HY)) || (ns.Height < LY))
      vp->DyOffset = (vp->Modes & LACE )
                   ? ((2 * NormalRows) - ns.Height) / 2 :
                     ((1 * NormalRows) - ns.Height) / 2 ;

   LoadRGB4(vp,&ptilbmFrame->colorMap[0],
           (long)ptilbmFrame->nColorRegs);
       /* load the colormap for the current picture in the viewport */

   if (ptilbmFrame->foundBEAM) { /* BEAM chunk exists make coplist  */
      vp->DspIns = NULL;    /* do not include Display Instructions  */
      vp->SprIns = NULL;    /* do not include Sprite Instructions   */
      vp->ClrIns = NULL;    /* do not include Clear Instructions    */
      vp->UCopIns = MakeBeamList(ptilbmFrame->bmHdr.h,
                                 ptilbmFrame->nColorRegs);
      }

   RemakeDisplay(); /* remake entire display including user coplist */
   ScreenToFront(screen);/* bring the screen to front when all done */

   Quit = FALSE;                             /* set quit flag FALSE */
   while (Quit == FALSE) {                   /* wait til quit TRUE  */
      Wait( 1L << window->UserPort->mp_SigBit); /* do not busy wait */
      while (msg = (struct IntuiMessage *)GetMsg(window->UserPort)) {
         class  = msg->Class;                /* store message class */
         code   = msg->Code;                 /* store message code  */

         ReplyMsg(msg);                      /* reply to messages   */

         switch(class) {                           /* check class   */
            case VANILLAKEY:                       /* a key press ? */
               if ((code == '\003') ||             /* quit keys are */
                   (code == '\033')) Quit = TRUE;  /* ESC or CTRL-C */
               break;
            default:                          /* skip all the other */
               break;                         /* class of messages  */
            }      /* end of switch(class) block */
         }      /* end of while (msg = ... block */
      }      /* end of while (Quit==FALSE) block */
   ScreenToBack(screen);     /* send the screen back before closing */
                             /* avoids screen flash while releasing */
                             /* copper lists & closing user screens */
}

/*** PrintS *****/

PrintS(msg)
char *msg;
{
   if (!wbStartup)  printf(msg);
}

/*** GoodBye *****/

void
GoodBye(msg)
char *msg;
{
   PrintS(msg);
   PrintS("\n\n");
   exit(0);
}

/*** OpenArg() *****/

long
OpenArg(wa)
struct WBArg *wa;
{
   struct FileLock *olddir;
   struct FileHandle *file;

   if (wa->wa_Lock)
      olddir = (struct FileLock *)CurrentDir(wa->wa_Lock);

   file = (struct FileHandle *)Open(wa->wa_Name, MODE_OLDFILE);

   if (wa->wa_Lock)
      CurrentDir(olddir);

   return((long)file);
}

/*** main0 *****/

void
main0(wa)
struct WBArg *wa;
{
   long file;
   IFFP iffp = NO_FILE;

   file = OpenArg(wa);

   if (file) {
      iffp = ReadPicture(file, &iFrame);
      Close(file);
      }

   if (iffp == IFF_DONE)
      DisplayPic(&iFrame);

   PrintS(IFFPMessages[-iffp]);
   PrintS("\n");

   if (window) CloseWindow(window);
   if (screen) CloseScreen(screen);
}

/*** main() *****/

void
main(argc, argv)
int argc;
char **argv;
{
   struct WBArg wbArg, *wbArgs;
   long olddir;

   PrintS("\n*** Vedi - IFF File Viewer - Ver. 1.18 ***\n\n");
   PrintS("Copyright (c) 1989, QUEST Software - Verona - IT\n");
   PrintS("All rights reserved\n");

   if (!(IntuitionBase = (struct IntuitionBase *)
      OpenLibrary("intuition.library",LIBRARY_VERSION)))
      GoodBye("No Intuition Library!");
   if (!(GfxBase = (struct GfxBase *)
      OpenLibrary("graphics.library",LIBRARY_VERSION)))
      GoodBye("No Graphics Library!");

   NormalCols = GfxBase->NormalDisplayColumns;
   NormalRows = GfxBase->NormalDisplayRows;

   Prefs = (struct Preferences *)AllocMem
                ((long)sizeof(struct Preferences),0L);
   GetPrefs(Prefs,(long)sizeof(struct Preferences));

   if (GfxBase->DisplayFlags & PAL) ScreenNTSC = 0;
   else ScreenNTSC = 1;

   if (!argc) {
      wbStartup = WBenchMsg;
      wbArgs = wbStartup->sm_ArgList;
      argc = wbStartup->sm_NumArgs;
      while (argc > 1) {
         olddir = (long)CurrentDir(wbArgs[1].wa_Lock);
         main0(&wbArgs[1]);
         argc--;
         wbArgs = &wbArgs[1];
         }

      if (argc < 2) {
         PrintS ("\nUsage from Workbench:\n");
         PrintS ("Click left mouse button on Vedi, hold 'SHIFT' key\n");
         GoodBye("pressed while double-clicking on the picture icon");
         }

      }
   else {
      if ((argc < 2) || (*argv[1] == '?'))
         GoodBye("\nUsage from CLI: Vedi <filename>");
      wbArg.wa_Lock = NULL;
      while (argc > 1) {
         wbArg.wa_Name = argv[1];
         PrintS("\nReading file ");
         PrintS(wbArg.wa_Name);
         PrintS("...\n\n");
         PrintS("Press ESC to exit...\n\n");
         main0(&wbArg);
         PrintS("\n");
         argc--;
         argv = &argv[1];
         }
      }

   FreeMem(Prefs, (long)sizeof(struct Preferences));

   CloseLibrary(GfxBase);
   CloseLibrary(IntuitionBase);

   exit(0);
}
