/****************************************************************************
**                                                                         **
**  ShowIFF - a comfortable IFF picture viewer for CLI and Workbench       **
**                               by                                        **
**  Christian A. Weber, Bruggerweg 2, CH-8037 Zürich, Switzerland          **
**                                                                         **
**  INTERNET: weber@amiga.physik.unizh.ch                                  **
**  UUCP:     cbmehq!cbmswi!mighty!chris@cbmvax.commodore.com              **
**                                                                         **
**  For documentation, refer to the file "ShowIFF.doc".                    **
**                                                                         **
**  All known formats (including Overscan, HAM, Halfbrite, SHAM) are       **
**  supported. If a picture is larger than the screen, you may use the     **
**  mouse to scroll around (If you have enough CHIP memory...)             **
**                                                                         **
**  Any suggestions, donations or bug reports are welcome.                 **
**                                                                         **
*****************************************************************************
**                                                                         **
**  This program is in the Public Domain. Use it at your own risk.         **
**                                                                         **
**  Requirements:  - arp.library V39+                                      **
**                 - iff.library V19+                                      **
**                                                                         **
**  To compile:    - Lattice C compiler V5.04 or above (use 32bit ints)    **
**                 - The file 'arpstartup.o' (My ARP startup module)       **
**                 - The file 'arpglue.lib' for Printf etc.                **
**                                                                         **
*****************************************************************************
**                                                                         **
**    Modification History:                                                **
**    --------------------                                                 **
**                                                                         **
**    15-Oct-87  CHW  V1.0   Created this file!                            **
**    30-Jun-88  CHW  V1.4   Directory scan fixed for FFS, cleaned up      **
**    16-Nov-88  CHW  V1.5   Overscan implemented                          **
**    28-Nov-88  CHW  V1.6   Minimal screen size is now 64x64 pixels       **
**    02-Jan-89  CHW  V1.7   Double-buffering implemented                  **
**    25-Sep-89  CHW  V2.01  Changed to Lattice C and ARP library          **
**    27-Sep-89  CHW  V2.02  Screen scrolling implemented                  **
**    22-Nov-89  CHW  V2.03  Rejects Non-ILBM files correctly              **
**    21-Feb-90  CHW  V2.04  'NoMemForGfx' bug fixed, overscan fixed       **
**    28-Feb-90  CHW  V2.10  SHAM support code added                       **
**    14-Mar-90  CHW  V2.11  Memory fragmentation workaround, cleanup      **
**    08-Apr-90  CHW  V2.12  Pics with more than 6 planes don't guru       **
**    22-Apr-90  CHW  V2.14  New startup code, WB cleanup works now.       **
**    16-Jul-90  CHW  V2.15  Works now properly with MoreRows & OS 2.0     **
**    16-Sep-90  CHW  V2.16  Scrolling SHAM pictures implemented           **
**    03-Oct-90  CHW  V2.17  MaxX0 divided by 2 because of a 2.0 bug       **
**    30-Oct-90  MAB  V2.20  ShowIFF can be turned into an appicon         **
**    11-Jan-91  CHW  V2.21  SHAM color errors fixed                       **
**    11-Jan-91  CHW  V2.22  AppIcon image/pos are taken from .info file   **
**    13-Jan-91  CHW  V2.23  COMMAND mode added, overscan bug fixed (?)    **
**    14-Jan-91  CHW  V2.24  Icon tooltypes added, code moved around       **
**    24-Jan-91  CHW  V2.25  Minor bug fixes                               **
**    03-May-91  CHW  V2.26  Vertical centering and NOCENTER option added  **
**    23-May-91  CHW  V2.27  Pointer option added                          **
**    27-May-91  CHW  V2.28  True Color Table fixed and extended           **
**    23-Jun-91  CHW  V2.29  AppIcon's image can be specified (ICON=...)   **
**    23-Apr-92  CHW  V2.30  RasInfo-bug workaround only under V36-38      **
**                                                                         **
****************************************************************************/

#define APPICON_MODE	/* This works only with the 2.0 include files */

#include <proto/exec.h>
#include <exec/memory.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <graphics/gfxbase.h>
#include <graphics/gfxmacros.h>
#include <arp/arpbase.h>
#include <libraries/dosextens.h>
#include <hardware/custom.h>
#include <workbench/startup.h>
#include <string.h>
#include <libraries/iff.h>				/* Not an official CBM file */

#ifdef APPICON_MODE
#include <proto/icon.h>
#include <proto/wb.h>
#endif

#define VERS	"ShowIFF 2.30"
#define VSTRING	VERS " (23.4.92) by Christian A. Weber"

#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX(a,b) ((a)>(b)?(a):(b))
#define reg register					/* My favorite alias :-) */

#define ARG_PATTERN		0				/* Argument numbers for GADS() */
#define ARG_ALL			1
#define ARG_LOOP		2
#define ARG_DELAY		3
#define ARG_NOBREAK		4
#define ARG_NOCENTER	5
#define ARG_NOOVERSCAN	6
#define ARG_POINTER		7
#define ARG_COMMAND		8
#define ARG_INVALID		9

#define OPTIONSTOOLTYPE		"OPTIONS"
#define ICONTOOLTYPE		"ICON"
#define XPOSTOOLTYPE		"ICONXPOS"
#define YPOSTOOLTYPE		"ICONYPOS"
#define ICONNAMETOOLTYPE	"ICONNAME"
#define APPICONPORTNAME		"ShowIFF-AppIcon"
#define DEFAULTAPPICONNAME	"IFF Picture Viewer"

#define PICF_DYNA 1

/*** External references ***/

extern void exit(LONG);					/* My startup code's exit function */
extern struct Process *ProcessBase;		/* Pointer to our process */
extern struct Library *ArpBase;			/* Also used for DOS functions */
extern struct Custom __far custom;		/* For SHAM/DYNA copper list */
extern BYTE NewOS;						/* 0 if Kick1.2/1.3, >0 if Kick2.x */


/*** Global definitions ***/

char VersionString[] =
	"\0$VER: " VSTRING "\n\r";

char WindowTitle[] =
	"CON:0/24/640/82/" VSTRING;

char CLI_Template[] =
	"Patterns/...,ALL/s,L=LOOP/s,D=DELAY/k,NB=NOBREAK/s,NC=NOCENTER/s,NO=NOOVERSCAN/s,POINTER/s,COMMAND/k";

char CLI_Help[] =
	VSTRING "\n"
	"Usage: ShowIFF [files or patterns] [ALL] [LOOP] [DELAY delay] [NOBREAK]\n"
	"\t\t[NOCENTER] [NOOVERSCAN] [POINTER] [COMMAND \"Command %s args\"]";

char StdWindowName[] = "NIL:";			/* Standard Workbench window name */
struct Library *IconBase,*IFFBase;		/* Some libraries we use */
char *argv[ARG_INVALID+1];				/* Arguments, filled in by GADS() */
BPTR wbwindow;							/* Output window for Workbench mode */
struct DiskObject *dobj;				/* Our icon */
LONG wbdelay=3145728;					/* keep window open for 3 seconds */
IFFFILE ifffile;						/* Pointer to our IFF file */
LONG delay=1000000;						/* Time (in seconds) for each picture */
WORD *emptysprite;						/* ShowIFF's mouse pointer sprite */

struct Picture
{
	struct Screen		*Screen;		/* The picture's screen */
	struct Window		*Window;		/* Window for mouse handling */
	struct BitMap		BitMap;			/* CustomBitMap, can be larger than screen */
	PLANEPTR			MorePlanes[16];	/* Extend bitmap to 24 planes */
	UWORD				*SHAMColors;	/* Ptr to SHAM color table array or NULL */
	WORD				Y0;				/* Beginning of picture relative to screen */
	WORD				MaxX0,MaxY0;	/* Scrolling limits */
	UWORD				ColorCount;		/* Number of colors in color palette */
	UWORD				ColorTab[256];	/* The picture's color table */
	UBYTE				Flags;
} pic1,pic2;							/* 2 pictures for double-buffering */

struct SHAMChunk
{
	struct Chunk	Chunk;
	UWORD			Version;
	UWORD			Colors[1];			/* open array */
};

/***************************************************************************/
/* Free all planes of a bitmap */

void MyFreeBitMap(reg struct BitMap *bm)
{
	reg LONG planesize=bm->BytesPerRow*bm->Rows,i;

	for(i=0; i<bm->Depth; ++i)
		if(bm->Planes[i])
		{
			FreeMem(bm->Planes[i],planesize);
			bm->Planes[i]=0;
		}
}

/***************************************************************************/
/* Initialize a BitMap structure and allocate CHIP memory for the planes */

BOOL MyAllocBitMap(reg struct BitMap *bm,LONG d,LONG w,LONG h)
{
	reg LONG planesize,i;

	InitBitMap(bm,d,w,h);
	planesize=bm->BytesPerRow*bm->Rows;

	for(i=0; i<d; ++i)
		if(!(bm->Planes[i]=AllocMem(planesize,MEMF_CHIP|MEMF_CLEAR)))
		{
			MyFreeBitMap(bm); return FALSE;
		}
	return TRUE;
}

/***************************************************************************/
/* Adjust the X/Y position of a screen for correct overscan display */

void SetOverscan(reg struct Screen *screen)
{
	reg x=GfxBase->NormalDisplayColumns,y=GfxBase->NormalDisplayRows;

	if(!(screen->ViewPort.Modes & HIRES)) x >>= 1;
	if(  screen->ViewPort.Modes & LACE  ) y <<= 1;

	x = (x-screen->Width ) >> 1;
	y = (y-screen->Height) >> 1; if(y>0) y=0;

	/* Avoid garbled pictures: */
	if(GfxBase->ActiView->DyOffset+y < 0) y = 0-GfxBase->ActiView->DyOffset;

	/* Correct overscan HAM color distortions: */
	if(screen->ViewPort.Modes & HAM)
	{
		if(GfxBase->ActiView->DxOffset+x < 96)
			x=96-GfxBase->ActiView->DxOffset;
	}

#if 0
	Printf("S(%ld,%ld) O(%ld,%ld) ",screen->Width,screen->Height,x,y);
#endif

//	if(NewOS) MoveScreen(screen,x,y);
//	else
	{
		screen->ViewPort.DxOffset = x; screen->ViewPort.DyOffset = y;
		MakeScreen(screen); RethinkDisplay();
	}
}

/***************************************************************************/
/* Create intermediate SHAM copper list if this is an SHAM picture */

void MakeSHAMCopList(struct Picture *pic)
{
	reg struct UCopList *ucop;

	if(!pic->SHAMColors) return;	/* Not an SHAM picture, no work to do */

	if(ucop=AllocMem(sizeof(struct UCopList),MEMF_CLEAR))
	{
		reg struct Screen *s=pic->Screen;
		reg LONG i,j,step=(s->ViewPort.Modes&LACE) ? 2:1;
		reg UWORD copx=GfxBase->ActiView->DxOffset+s->Width;
		reg UWORD y0=s->ViewPort.RasInfo->RyOffset;

		if(pic->Flags & PICF_DYNA) step=1;
		for(i=1; i<(s->Height/step); ++i)
		{
			CWAIT(ucop,pic->Y0+(i-1)*step,(UWORD)((copx>>1)%(UWORD)228))
			for(j=1; j<16; ++j)
				CMOVE(ucop,custom.color[j],pic->SHAMColors[16*(y0+i)+j])
		}
		CEND(ucop)
//		FreeVPortCopLists(&s->ViewPort);
		s->ViewPort.UCopIns = ucop;
		pic->SHAMColors[16*y0]=pic->ColorTab[0];	/* Fix some weird SHAM pics */
		LoadRGB4(&s->ViewPort,&pic->SHAMColors[16*y0],16);
	}
}

/***************************************************************************/
/* Free a picture (that is a window, screen and a custom BitMap) */

void ClosePicture(reg struct Picture *pic)
{
	if(pic->Window)
	{
		ScreenToBack(pic->Screen);
		if(!argv[ARG_POINTER]) ClearPointer(pic->Window);
		CloseWindow(pic->Window);
		pic->Window=0;
	}

	if(pic->Screen)
	{
		CloseScreen(pic->Screen);
		pic->Screen=0;
	}

	MyFreeBitMap(&pic->BitMap);
	RemakeDisplay();	/* remake copperlist to increase MEMF_LARGEST */
}

/***************************************************************************/
/* Allocate a picture (Allocate custom BitMap, open screen and window) */

BOOL OpenPicture(reg struct Picture *pic,reg struct BitMapHeader *bmhd)
{
	struct NewScreen ns;
	struct NewWindow nw;

	ClosePicture(pic);

	memset(&ns,0,sizeof(ns));
	memset(&nw,0,sizeof(nw));
	memset(pic,0,sizeof(*pic));

	ns.Width        = GfxBase->NormalDisplayColumns;
	ns.Height       = GfxBase->NormalDisplayRows;
	ns.Depth        = MAX(1,bmhd->nPlanes);	if(ns.Depth>6) ns.Depth=4;
	ns.ViewModes    = (UWORD)GetViewModes(ifffile);
	ns.Type         = CUSTOMSCREEN|CUSTOMBITMAP|SCREENQUIET|SCREENBEHIND;
	ns.CustomBitMap = &pic->BitMap;

	if(!(ns.ViewModes & HIRES))	{ ns.Width  >>= 1; }
	if(  ns.ViewModes & LACE)	{ ns.Height <<= 1; }

	if(argv[ARG_NOCENTER])	ns.Height = MIN(bmhd->h,ns.Height);
	else					ns.Width  = MIN(bmhd->w,ns.Width);

	ns.Width  = MAX(128,ns.Width);
	ns.Height = MAX(128,ns.Height);

	if(!argv[ARG_NOOVERSCAN])
	{
		if((bmhd->w > ns.Width) && (bmhd->w <= (ns.ViewModes&HIRES ? 768:384)))
			ns.Width = bmhd->w;

		if((bmhd->h>ns.Height) && (bmhd->h<=(ns.Height+(ns.ViewModes&LACE?80:40))))
			ns.Height = bmhd->h;
	}

	if(MyAllocBitMap(&pic->BitMap,MAX(bmhd->nPlanes,ns.Depth),
				MAX(bmhd->w,ns.Width),MAX(bmhd->h,ns.Height)))
	{
		reg olddepth      = pic->BitMap.Depth;
		pic->BitMap.Depth = ns.Depth;
		pic->Screen       = OpenScreen(&ns);
		pic->BitMap.Depth = olddepth;

		if(nw.Screen=pic->Screen)
		{
			nw.Width      = ns.Width;
			nw.Height     = ns.Height;
			nw.IDCMPFlags = MOUSEBUTTONS|MOUSEMOVE|DELTAMOVE|VANILLAKEY;
			nw.Flags      = BACKDROP|BORDERLESS|ACTIVATE|SIMPLE_REFRESH
								|NOCAREREFRESH|REPORTMOUSE|RMBTRAP;
			nw.Type       = CUSTOMSCREEN;

			if(pic->Window=OpenWindow(&nw))
			{
				pic->Y0=(ns.Height-bmhd->h)>>1; if(pic->Y0<0) pic->Y0=0;

				pic->MaxX0 = bmhd->w-ns.Width;
				pic->MaxY0 = bmhd->h-ns.Height;

				/*
				**	In 2.0 there's this RasInfo scrolling bug, they
				**	said they'll fix it for newer ROMs, so we'll do an
				**	explicit version check
				*/
				if(ns.ViewModes & HIRES)
				{
					if(	(GfxBase->LibNode.lib_Version >= 36)
					&&	(GfxBase->LibNode.lib_Version <= 38))
					 pic->MaxX0 >>= 1;
				}

				if(!argv[ARG_POINTER])
					SetPointer(pic->Window,emptysprite,1L,16L,0L,0L);

				if(bmhd->nPlanes == 24)
				{
					static UWORD truecoltab[32] =
					{
						0x000,0x00C,0x0C0,0x0CC,0xC00,0xC0C,0xCC0,0xCCC,
						0x080,0x08C,0x0F0,0x0FC,0xC80,0xC8C,0xCF0,0xCFC,
						0x800,0x80C,0x8C0,0x8CC,0xF00,0xF0C,0xFC0,0xFCC,
						0x880,0x88C,0x8F0,0x8FC,0xF80,0xF8C,0xFF0,0xFFC
					};
					memcpy(pic->ColorTab,truecoltab,sizeof(truecoltab));
					pic->Screen->BitMap.Planes[0]=pic->BitMap.Planes[23];	/* B7 */
					pic->Screen->BitMap.Planes[1]=pic->BitMap.Planes[15];	/* G7 */
					pic->Screen->BitMap.Planes[2]=pic->BitMap.Planes[7];	/* R7 */
					pic->Screen->BitMap.Planes[3]=pic->BitMap.Planes[14];	/* G6 */
					pic->Screen->BitMap.Planes[4]=pic->BitMap.Planes[6];	/* R7 */
					pic->ColorCount = (ns.ViewModes&HIRES) ? 16:32;
				}
				else
				{
					if(!(pic->ColorCount=GetColorTab(ifffile,pic->ColorTab)))
					{
						/* Provide default colors for pictures without a CMAP */
						pic->ColorCount=2;
						pic->ColorTab[0]=0xeca; pic->ColorTab[1]=0x000;
					}
				}

				if(pic->ColorCount>32) pic->ColorCount = 32; /* Old DigiView */
				LoadRGB4(&pic->Screen->ViewPort,pic->ColorTab,pic->ColorCount);
				SetOverscan(pic->Screen);
				return TRUE;
			}
		}
	}
	ClosePicture(pic);
	return FALSE;
}

/***************************************************************************/
/* Close all resources and exit to CLI/WorkBench */

void Fail(reg char *reason)
{
	Puts(reason);

	ClosePicture(&pic1); ClosePicture(&pic2);
	if(ifffile)	CloseIFF(ifffile);
	if(dobj)	FreeDiskObject(dobj);

	if(wbwindow)
	{
		WaitForChar(wbwindow,wbdelay);	/* Let them see our text */
		Close(wbwindow);
	}

	if(IFFBase)		CloseLibrary(IFFBase);
	if(IconBase)	CloseLibrary(IconBase);

	exit(0L);	/* Back to the startup code (important for Workbench) */
}

/***************************************************************************/
/* That's the big one! Load & display a picture (with mouse-scrolling),
   return FALSE if user presses the RMB (abort), else return TRUE */

BOOL ShowPicture(reg char *name)
{
	reg BOOL cont=TRUE;
	reg struct BitMapHeader *bmhd;

	if(!strcmp(name+strlen(name)-5,".info")) return TRUE;	/* Skip icons */
	Printf("%s ... ",name);

	if(ifffile) CloseIFF(ifffile);

	if(!(ifffile=OpenIFF(name)))
	{
		Puts("- not an IFF file!");
		return TRUE;
	}

	if(*(((ULONG *)ifffile)+2) != ID_ILBM)
	{
		Puts("- not an ILBM file!");
		return TRUE;
	}

	if(!(bmhd=GetBMHD(ifffile)))
	{
		Printf("- Mangled IFF file (Error %ld)\n",IFFError());
		return TRUE;
	}

	Printf("%ld x %ld x %ld ",bmhd->w,bmhd->h,bmhd->nPlanes);
retry:
	if(OpenPicture(&pic1,bmhd))
	{
		if(DecodePic(ifffile,&pic1.BitMap))
		{
			reg struct SHAMChunk *sham;
			reg LONG i;
			reg WORD xoff=0,yoff=0;

			if(pic1.Y0)
			{
				struct Screen *s=pic1.Screen;
				ScrollRaster(&s->RastPort,0,-pic1.Y0,0,0,
					s->BitMap.BytesPerRow<<3,s->BitMap.Rows);
			}

			if(sham=FindChunk(ifffile,ID_SHAM))
			{
				Printf("SHAM ");
				if(sham->Version == 0)
				{
					pic1.SHAMColors = sham->Colors;
				}
				else Printf("Unsupported mode: %ld ",(LONG)sham->Version);

				MakeSHAMCopList(&pic1);
			}

			if(sham=FindChunk(ifffile,ID_CTBL))
			{
				Printf("DYNA ");
				pic1.SHAMColors = &sham->Version;	/* DYNA has no version */
				pic1.Flags |= PICF_DYNA;
				MakeSHAMCopList(&pic1);
			}

			ScreenToFront(pic1.Screen);
			ClosePicture(&pic2);
			for(i=delay*50L; i>=0; --i)
			{
				reg struct IntuiMessage *msg;

				WaitTOF();
				while(msg=(struct IntuiMessage *)GetMsg(pic1.Window->UserPort))
				{
newmsg:				switch(msg->Class)
					{
					case MOUSEMOVE:

						xoff+=msg->MouseX;
						yoff+=msg->MouseY;
						if(xoff>pic1.MaxX0) xoff=pic1.MaxX0;
						if(xoff<0) xoff=0;
						if(yoff>pic1.MaxY0) yoff=pic1.MaxY0;
						if(yoff<0) yoff=0;
						pic1.Screen->ViewPort.RasInfo->RxOffset=xoff;
						pic1.Screen->ViewPort.RasInfo->RyOffset=yoff;
						if(pic1.SHAMColors)
						{
							reg struct IntuiMessage *m2;
							MakeSHAMCopList(&pic1);
							while(m2=GetMsg(pic1.Window->UserPort))
							{
								ReplyMsg(msg); msg=m2;
								if(msg->Class == MOUSEMOVE)
								{
									xoff+=msg->MouseX;
									yoff+=msg->MouseY;
								}
								else goto newmsg;
							}
						}
						/* ScrollVPort(&pic1.Screen->ViewPort); */
						MakeScreen(pic1.Screen); RethinkDisplay();
						break;

					case VANILLAKEY:
						if(msg->Code == 'c') if(argv[ARG_COMMAND])
						{
							char buf[200];
							SPrintf(buf,argv[ARG_COMMAND],name,name);
							Execute(buf,NULL,Output());
						}
						break;

					case MOUSEBUTTONS:
						if(!argv[ARG_NOBREAK])
						{
							if(msg->Code == MENUDOWN)	goto usrbreak;
							if(msg->Code == SELECTDOWN)	goto showend;
						}
						break;
					}
					ReplyMsg(msg);
				}
				if(!argv[ARG_NOBREAK]) if(CheckAbort(NULL))
				{
usrbreak:			Puts("***BREAK"); cont=FALSE; goto showend2;
				}
			}
showend:	Puts("- Done");
showend2:	pic2=pic1; memset(&pic1,0,sizeof(pic1));
		}
		else
		{
			ClosePicture(&pic1);
			Printf("- Decode error: %ld\n",IFFError());
		}
	}
	else if(pic2.Window)
	{
		ClosePicture(&pic2); goto retry;
	}
	else Puts("- Can't open screen!");
	return cont;
}

/***************************************************************************/
/* Expand a pattern and call ShowPicture() for each matching file,
   return FALSE if the user presses ^C or ShowPicture() returns FALSE */

BOOL ShowPattern(char *pathname)
{
	char buf[256];
	reg BPTR file;
	reg BOOL cont=TRUE;

	if(PreParse(pathname,buf))	/* TRUE if name contains any wildcards */
	{
		reg struct {
			struct AnchorPath APath;
			char   FullPath[255];		/* cheap way to extend ap_Buf[] */
		} *myanchor;
iswild:
		if(myanchor=ArpAlloc(sizeof(*myanchor)))
		{
			reg BOOL showit=TRUE;
			reg LONG error;

			myanchor->APath.ap_StrLen = 255;	/* Want full path built */
			myanchor->APath.ap_Flags = APF_DOWILD;
			myanchor->APath.ap_BreakBits=SIGBREAKF_CTRL_C;

			error=FindFirst(pathname,&myanchor->APath);
			while(!error)
			{
				/* +1 because of a lattice bug */
				if(SetSignal(0L,SIGBREAKF_CTRL_E+1) & SIGBREAKF_CTRL_E)
				{
					Puts("***DIR BREAK"); showit=FALSE;
					/* myanchor->APath.ap_Flags &= ~APF_DODIR; */
				}

				if(myanchor->APath.ap_Info.fib_DirEntryType>=0)	/* Dir */
				{
					if(argv[ARG_ALL])
					{
						if(!(myanchor->APath.ap_Flags & APF_DIDDIR))
						{
							myanchor->APath.ap_Flags |= APF_DODIR;
							showit=TRUE;
						}
						myanchor->APath.ap_Flags &= ~APF_DIDDIR;
					}
				}
				else if(showit) if(!ShowPicture(myanchor->APath.ap_Buf))
				{
					cont = FALSE; break;
				}
				error=FindNext(&myanchor->APath);
			}

			FreeAnchorChain(&myanchor->APath);

			switch(error)
			{
				case 0:							break;

				case ERROR_BREAK:				Puts("***BREAK"); cont=FALSE; break;

				case ERROR_OBJECT_NOT_FOUND:	Puts("File not found."); break;

				case ERROR_BUFFER_OVERFLOW:		Puts("Path too long!"); break;

				case ERROR_NO_MORE_ENTRIES:		break;	/* Normal termination */

				default:	Printf("IO error %ld!\n",error); break;
			}
		}
		else Fail("No mem for anchor!");
	}
	else if(file=Open(pathname,MODE_OLDFILE))	/* Just one file ? */
	{
		Close(file); return ShowPicture(pathname);
	}
	else	/* No wildcards, and not a file: it's a device or a directory */
	{
		strcpy(buf,pathname); TackOn(pathname=buf,"*");
		goto iswild;	/* Not really elegant, but it works */
	}

	return cont;
}

#ifdef APPICON_MODE
/***************************************************************************/
/* Started without arguments: create or delete our appicon */

void AppIconStuff(void)
{
	struct Library *WorkbenchBase;
	struct MsgPort *msgport;
  	struct AppIcon *ai;
	struct AppMessage *amsg;

	if(!(WorkbenchBase = OpenLibrary(WORKBENCH_NAME,36))) return;

	if(msgport=FindPort(APPICONPORTNAME))	/* Already running ? */
	{
		Signal(msgport->mp_SigTask,SIGBREAKF_CTRL_C);	/* Kill it */
	}	
	else if(msgport = CreatePort(APPICONPORTNAME,0)) 
	{
		char *tooltype;
		struct DiskObject *appdobj=NULL;

		if(tooltype=FindToolType(dobj->do_ToolTypes,ICONTOOLTYPE))
			appdobj=GetDiskObject(tooltype);
		if(!appdobj) appdobj=dobj;

		if(tooltype=FindToolType(dobj->do_ToolTypes,XPOSTOOLTYPE))
			appdobj->do_CurrentX = Atol(tooltype);
		else
			appdobj->do_CurrentX = NO_ICON_POSITION;

		if(tooltype=FindToolType(dobj->do_ToolTypes,YPOSTOOLTYPE))
			appdobj->do_CurrentY = Atol(tooltype);
		else
			appdobj->do_CurrentY = NO_ICON_POSITION;

		if(!(tooltype=FindToolType(dobj->do_ToolTypes,ICONNAMETOOLTYPE)))
			tooltype = DEFAULTAPPICONNAME;

		if(ai = AddAppIconA(0,0,tooltype,msgport,NULL,appdobj,NULL)) 
		{
			while(!(Wait(1L << msgport->mp_SigBit | SIGBREAKF_CTRL_C)
						 & SIGBREAKF_CTRL_C))
			{
				reg BPTR oldcos;

				if(wbwindow=Open(WindowTitle,MODE_OLDFILE))
				{
					oldcos = ProcessBase->pr_COS;
					ProcessBase->pr_COS = wbwindow;
				}

				while(amsg = GetMsg(msgport)) 
				{
					reg i,cont=TRUE;
					struct WBArg *arg = amsg->am_ArgList;
  					for(i=0; (i < amsg->am_NumArgs) && cont; i++,arg++)
  					{
						reg BPTR oldcd=0;
						if(arg->wa_Lock) oldcd=CurrentDir(arg->wa_Lock);
						else DisplayBeep(NULL);
						cont=ShowPattern(arg->wa_Name&&*arg->wa_Name ? arg->wa_Name:"*");
						if(oldcd) CurrentDir(oldcd);
					}
					ReplyMsg(amsg);
				}
				ClosePicture(&pic2); 		/* Close last screen */
				if(wbwindow)
				{
					WaitForChar(wbwindow,2097152);	/* Wait 2 seconds */
					ProcessBase->pr_COS = oldcos;
					Close(wbwindow); wbwindow=0;
					SetSignal(0,SIGBREAKF_CTRL_C);	/* Clear break sig */
				}
			}
			RemoveAppIcon(ai);
		}

		if(appdobj != dobj) FreeDiskObject(appdobj);

		/* Make sure there are no more messages pending */
		while(amsg = GetMsg(msgport)) ReplyMsg(amsg);
		DeletePort(msgport);
	}
	CloseLibrary(WorkbenchBase);
}
#endif

/***************************************************************************/
/* Main program: parse the command line or WorkBench-args */

void ARPMain(LONG arglen,reg char *argline)
{
   	if(!(IconBase = OpenLibrary("icon.library",0)))	Fail("No icon.library!");
	if(!(IFFBase  = OpenLibrary(IFFNAME,0L)))		Fail("No iff.library!");

	if(IFFBase->lib_Version < IFFVERSION)
		Puts("WARNING: you have an old version of iff.library!");

	emptysprite=ArpAllocMem(20L,MEMF_CHIP|MEMF_CLEAR);

	if(arglen)		/* From CLI */
	{
		if(*argline != '\n')
		{
			if(GADS(argline,strlen(argline),CLI_Help,argv,CLI_Template)>0)
			{
				if(argv[ARG_DELAY]) delay=Atol(argv[ARG_DELAY]);
				do
				{
					reg char **pattern=(char **)argv[ARG_PATTERN];
					if(*pattern)
					{
						while(*pattern) if(!ShowPattern(*pattern++)) goto done;
					}
					else ShowPattern("*");
				} while(argv[ARG_LOOP]);
done: ;
			}
			else Fail(CLI_Help);
		}
		else Fail(CLI_Help);
	}
	else	/* Called from Workbench */
	{
		reg struct WBStartup *startup = (struct WBStartup *)argline;

		argv[ARG_ALL]        = (void *)TRUE;			/* Set defaults */
//		argv[ARG_LOOP]       = (void *)FALSE;
//		argv[ARG_NOBREAK]    = (void *)FALSE;
//		argv[ARG_NOOVERSCAN] = (void *)FALSE;

	    if(dobj = GetDiskObject(startup->sm_ArgList->wa_Name))
		{
			reg char *tooltype;
			if(tooltype=FindToolType(dobj->do_ToolTypes,OPTIONSTOOLTYPE))
			{
				GADS(tooltype,strlen(tooltype),NULL,argv,CLI_Template);
				if(argv[ARG_DELAY]) delay=Atol(argv[ARG_DELAY]);
			}
		}
		if(startup->sm_NumArgs > 1)	/* Started with some arguments */
		{
			reg i;
			struct WBArg *arg = startup->sm_ArgList;

			if(wbwindow=Open(WindowTitle,MODE_OLDFILE))		/* Open window */
				ProcessBase->pr_COS=wbwindow;

			for(i=1; i<startup->sm_NumArgs; ++i)
			{
				arg++;
				if(arg->wa_Lock) CurrentDir(arg->wa_Lock);
				else Puts("Can't lock dir!");
				if(arg->wa_Name && *arg->wa_Name)
				{
					if(!ShowPattern(arg->wa_Name)) break;
				}
				else ShowPattern("*");
			}
		}
		else	/* no arguments, just clicked */
#ifdef APPICON_MODE
		if(NewOS) AppIconStuff(); else
#endif
		{
			if(wbwindow=Open(WindowTitle,MODE_OLDFILE))		/* Open window */
				ProcessBase->pr_COS=wbwindow;

			wbdelay=6291456;	/* 6 seconds delay */
#ifdef GERMAN
			Fail("Bitte klicken Sie alle gewünschten Bild-Dateien oder Schubladen bei ge-\n"
		     "drückter SHIFT-Taste an, klicken Sie danach 2x auf das ShowIFF-Piktogramm.");
#else
			Fail("Please select all drawers and files to view while holding\n"
		     "down the SHIFT key, then double-click the ShowIFF icon.");
#endif
		}
	}
	wbdelay=1048576;			/* One second delay */
#ifdef GERMAN
	Fail("Fertig.");
#else
	Fail("All done.");
#endif

}

