// TSMorph - Amiga Morphing program
// Copyright (C) © 1993  Topicsave Limited

// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

// mpaddock@cix.compulink.co.uk

/* Amiga headers */
#define INTUI_V36_NAMES_ONLY
#define INTUITION_IOBSOLETE_H

// Include correct math library
#ifdef MY68881
#include <m68881.h>
extern int rand(void);
#else
#ifdef MY68881_6
#include <math.h>
#include <m68881.h>
#define floor(d) __builtin_fpc(0x0003,d)
#else
#include <math.h>
#endif
#endif

// prevent inclusion of another math library
#define LIBRARIES_MATHFFP_H

// other headers, define some non 2.02 header stuff
#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <intuition/gadgetclass.h>
#ifndef PGA_NewLook
#define PGA_NewLook (PGA_Dummy + 0x000A)
#endif
#include <intuition/imageclass.h>
#include <intuition/icclass.h>
#include <devices/input.h>
#include <workbench/workbench.h>
#include <workbench/startup.h>
#include <libraries/gadtools.h>
#ifndef GTMN_FullMenu
#define GTMN_FullMenu GT_TagBase+62
#endif
#include <libraries/asl.h>

#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/layers_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <clib/input_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/asl_protos.h>
#include <clib/utility_protos.h>
#include <clib/alib_protos.h>

#include <pragmas/exec_pragmas.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/layers_pragmas.h>
#include <pragmas/graphics_pragmas.h>
#include <pragmas/intuition_pragmas.h>
#include <pragmas/input_pragmas.h>
#include <pragmas/gadtools_pragmas.h>
#include <pragmas/asl_pragmas.h>
#include <pragmas/utility_pragmas.h>

#include <proto/diskfont.h>
#include <proto/icon.h>

#include <devices/timer.h>

#include <clib/timer_protos.h>
#include <pragmas/timer_pragmas.h>

#include <clib/alib_protos.h>

#include <rexx/errors.h>
#include <pragmas/rexxsyslib_pragmas.h>
#include <clib/rexxsyslib_protos.h>

#include <libraries/amigaguide.h>
#include <clib/amigaguide_protos.h>
#include <pragmas/amigaguide_pragmas.h>

#include <libraries/reqtools.h>
#include <clib/reqtools_protos.h>
#include <pragmas/reqtools.h>

#include <opal/opallib.h>

#include <libraries/dctv.h>
#include <clib/dctv_protos.h>
#include <pragmas/dctv_pragmas.h>

#include <libraries/nofrag.h>
#include <clib/nofrag_protos.h>
#ifdef LIBRARIES_NOFRAG_H
#undef LIBRARIES_NOFRAG_H
#endif
#include <pragmas/nofrag_pragmas.h>
#ifndef LIBRARIES_NOFRAG_H
#define LIBRARIES_NOFRAG_H 1
#endif

#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

/* Below is not in the 2.02 version of clib/utility_protos.h
                                       pragmas/utility_pragmas.h */
#if INCLUDE_VERSION < 37
LONG Stricmp(UBYTE *string1,UBYTE *string2);
#pragma libcall UtilityBase Stricmp A2 9802
#endif

// IFF header
#include "iffp/ILBMapp.h"

// Progress requester
#include "progress.h"

// JPEG load stuff
#include "JPEG_LS/jinclude.h"

/* Wait pointer */
static USHORT __chip BusyPointerData[] =
{
   0x0000,0x0000,
   0x0400,0x07C0,0x0000,0x07C0,0x0100,0x0380,0x0000,0x07E0,
   0x07C0,0x1FF8,0x1FF0,0x3FEC,0x3FF8,0x7FDE,0x3FF8,0x7FBE,
   0x7FFC,0xFF7F,0x7EFC,0xFFFF,0x7FFC,0xFFFF,0x3FF8,0x7FFE,
   0x3FF8,0x7FFE,0x1FF0,0x3FFC,0x07C0,0x1FF8,0x0000,0x07E0,
   0x0000,0x0000,
};

// Libraries
extern struct ExecBase *SysBase;
extern struct DosLibrary *DOSBase;
extern struct IntuitionBase *IntuitionBase;
extern struct Library  *GfxBase, *LayersBase, *IFFParseBase;
extern struct Library *GadToolsBase, *AslBase, *UtilityBase;
extern struct Library *RexxSysBase;
extern struct Library *AmigaGuideBase;
extern struct Library *TimerBase;
extern struct Library *ReqToolsBase;
extern struct Library *DCTVBase;
extern struct NoFragBase *NoFragBase;

extern struct OpalBase *OpalBase;

// Help stuff
extern AMIGAGUIDECONTEXT handle;
extern struct NewAmigaGuide nag;
extern ULONG ASig;

// nofrag.library stuff
extern struct MemoryChain *Chain;
#define MAXMEM 1000

#define H_Help 		10
#define H_Library		11
#define H_ESave		12
#define H_AllocVec	13
#define H_OpenPoints	14
#define H_Progress	15
#define H_AllocPlanes 16
#define H_24			17
#define H_Load			18
#define H_AllocIFF	19
#define H_AllocILBM	20
#define H_FileFormat	21
#define H_SizeMatch	22
#define H_Range		23
#define H_MemPointsR	24
#define H_CloseFile	25
#define H_3Points		26
#define H_Open			27
#define H_ARexx		28
#define H_Really		29
#define HE_OldFormat	30
#define HE_OpalVision 31
#define HE_NoOpal		32

// 24 bitplane bitmap
struct MyBitMap {
	struct BitMap BitMap;
	PLANEPTR xplanes[16];
};

extern char 				FileName[256];	// File name buffer
extern long					OVQuality;	// OpalVision JPG quality
extern BOOL					OVFast;		// OpalVision fast format IFF
extern BOOL					OVThumb;		// OpalVision Thumbail
extern BOOL					HAM6;
extern BOOL					HAM8;
extern BOOL					BW16;
extern BOOL					BW256;
extern BOOL					DCTV3;
extern BOOL					DCTV4;
extern UBYTE 				*arrayr,		// Pointers to red,green and blue
		  						*arrayg,
		  						*arrayb;
extern BOOL					PPM;			// PPM save ?
extern UBYTE				*p[3];// chunky pointers - order for OpalVision
#define RED p[0]
#define BLUE p[2]
#define GREEN p[1]
extern UWORD		swidth;		// scaled width (in full words)
extern struct RastPort 	RP,			// Work Rast Ports
 						TRP;
extern struct MyBitMap 	MyBitMap;	// Real bit map
extern PLANEPTR 			Planes[24];	// Bit planes
extern BOOL					CreateIcons;// Create Icons on pictures?

extern struct OpalScreen *OScrn;
extern char 				*OVFormat;	// Format of OpalVision save JPG or IFF (or anything) default

extern USHORT	width, height, pwidth, pheight, pmode;

extern UBYTE *plane0;					// Planes for temporary bit maps
extern UBYTE *plane1;
extern UBYTE *plane2;
extern UBYTE *plane3;
extern UBYTE *plane4;
extern UBYTE *plane5;
extern UBYTE *plane6;
extern UBYTE *plane7;
 
extern char AnimName[];	// Buffer for file name to save

// Table of parameters
extern struct {
	LONG xf;				// Current frame
	LONG xFrames;		// Number of frames
	LONG xSingle;		// Warp or Morph
	LONG xmove;			// Movement 1 to 2
	LONG xr;				// red of image 1
	LONG xg;				// green
	LONG xb;				// blue
	LONG xr2;			// red of image 2
	LONG xg2;			// green
	LONG xb2;			// blue
	LONG xDo;			// Produce this image
	LONG xrplus;		// Add red
	LONG xgplus;		// green
	LONG xbplus;		// blue
	LONG xrminus;		// subtract red
	LONG xgminus;		// subtract green
	LONG xbminus;		// subtract blue
	LONG xDX;			// X skip
	LONG xDY;			// Y skip
	LONG xStart;		// Start Frame number
	LONG FILLER[32];	// Filler for future expansion
} Arexx;

#define f Arexx.xf
#define Frames Arexx.xFrames
#define Single Arexx.xSingle
#define move Arexx.xmove
#define r Arexx.xr
#define g Arexx.xg
#define b Arexx.xb
#define r2 Arexx.xr2
#define g2 Arexx.xg2
#define b2 Arexx.xb2
#define Do Arexx.xDo
#define rplus Arexx.xrplus
#define gplus Arexx.xgplus
#define bplus Arexx.xbplus
#define rminus Arexx.xrminus
#define gminus Arexx.xgminus
#define bminus Arexx.xbminus
#define DX Arexx.xDX
#define DY Arexx.xDY
#define Start Arexx.xStart

extern char buffer[256];
extern char buffer1[512];
extern char	*Postscript;// ARexx postscript

extern WORD x,y;				// Current coords

void help(ULONG hnum);
void DisableWindow(void);
void EnableWindow(void);
void AddMessage(UBYTE *message);

// SaveHAMetc.c
BOOL SaveBW16(UBYTE *FileName,USHORT width,USHORT height, UWORD swidth,
				  USHORT pwidth, USHORT pheight, USHORT pmode,
				  UBYTE *red, UBYTE *green, UBYTE *blue, struct RastPort *rp, struct RastPort *trp);
BOOL SaveBW256(UBYTE *FileName,USHORT width,USHORT height, UWORD swidth,
				  USHORT pwidth, USHORT pheight, USHORT pmode,
				  UBYTE *red, UBYTE *green, UBYTE *blue, struct RastPort *rp, struct RastPort *trp);
BOOL SaveHAM6(UBYTE *FileName,USHORT width,USHORT height, UWORD swidth,
				  USHORT pwidth, USHORT pheight, USHORT pmode,
				  UBYTE *red, UBYTE *green, UBYTE *blue, struct RastPort *rp, struct RastPort *trp);
BOOL SaveHAM8(UBYTE *FileName,USHORT width,USHORT height, UWORD swidth,
				  USHORT pwidth, USHORT pheight, USHORT pmode,
				  UBYTE *red, UBYTE *green, UBYTE *blue, struct RastPort *rp, struct RastPort *trp);
BOOL SaveDCTV(UBYTE *FileName,USHORT width,USHORT height, UWORD swidth,
				  USHORT pwidth, USHORT pheight, USHORT pmode,
				  UBYTE *red, UBYTE *green, UBYTE *blue, struct RastPort *rp, struct RastPort *trp);

/* Frame clicked - irrelevant	*/
int
FrameClicked(void) {
	return 1;
}


/* Line clicked - irrelevant	*/
int
LineClicked(void) {
	return 1;
}

/* Routine for when Stop clicked	*/
int
StopClicked(void) {
   struct EasyStruct EasyStruct = {
      sizeof(struct EasyStruct),
      0,
      NULL,
      NULL,
      NULL,
   };
	UBYTE *title = "TSMorph-render";
	UBYTE *body = "Really quit?";
   struct Window *req;
   UBYTE *gad;
   ULONG ret = 2;
	struct AmigaGuideMsg *agm;
	ULONG signals;
	struct rtHandlerInfo *rth;
   // Disable the window, display a requester and reenable the window
   DisableWindow();
   if (ReqToolsBase) {
   	if (handle) {
			gad = "_Quit|_Help|_Continue";
   	}
   	else {
			gad = "_Quit|_Continue";
   	}
		while (ret == 2) {
			ret = CALL_HANDLER;
	   	if (rtEZRequestTags(body,gad,NULL,NULL,
   									RT_ReqHandler, &rth,
   									RT_Window, ProgressWnd,
   									RT_Underscore, '_',
   									RTEZ_ReqTitle, title,
   									TAG_END) == CALL_HANDLER) {
				while (ret == CALL_HANDLER) {
   				if (!rth->DoNotWait) {
   					signals = Wait(rth->WaitMask | ASig);
   				}
					ret = rtReqHandlerA(rth,signals,NULL);
		   		if (ret == 2) {
			   		help(H_Really);
			   	}
		   		if (signals & ASig) {
					  	while (agm = GetAmigaGuideMsg(handle)) {
				 			ReplyAmigaGuideMsg(agm);
				 		}
				 	}
				}
   		}
   		else {
   			ret = 0;
   		}
   	}
	}
	else {
		EasyStruct.es_TextFormat = body;
		EasyStruct.es_Title = title;
	   if (handle) {
   		EasyStruct.es_GadgetFormat = "Quit|Help|Continue";
	   }
   	else {
   		EasyStruct.es_GadgetFormat = "Quit|Continue";
	   }
		req = BuildEasyRequest(ProgressWnd,&EasyStruct,NULL,NULL);
	   while ((ret = SysReqHandler(req,NULL,TRUE)) == 2) {
   		help(H_Really);
		  	while (agm = GetAmigaGuideMsg(handle)) {
	 			ReplyAmigaGuideMsg(agm);
	 		}
	   }
   	FreeSysRequest(req);
   }
   EnableWindow();
	return !ret;
}

/* Disables the Progress window
 * Disables the 'Stop' gadget and displays a wait pointer
 */
void
DisableWindow(void) {
	if (ProgressWnd) {
		GT_SetGadgetAttrs(ProgressGadgets[GDX_Stop],ProgressWnd,NULL,GA_Disabled,TRUE,TAG_END);
		SetPointer(ProgressWnd, BusyPointerData, 16, 16, -6, 0);
	}
}

/* Enables the Progress window
 * Enables the 'Stop' gadget and clears the pointer
 */
void
EnableWindow(void) {
	if (ProgressWnd) {
		GT_SetGadgetAttrs(ProgressGadgets[GDX_Stop],ProgressWnd,NULL,GA_Disabled,FALSE,TAG_END);
		ClearPointer(ProgressWnd);
	}
}

APTR
MyAllocVec(ULONG size,ULONG requirements) {
	ULONG *MyMem;
	if (Chain && (size < MAXMEM)) {
		if (MyMem = AllocVecItem(Chain,size+4,requirements)) {
			*MyMem = size;
			return ++MyMem;
		}
		else {
			return NULL;
		}
	}
	else {
		if (MyMem = AllocVec(size+4,requirements)) {
			*MyMem = size;
			return ++MyMem;
		}
		else {
			return NULL;
		}
	}
}

void MyFreeVec(APTR memptr) {
	ULONG *MyMem;
	MyMem = memptr;
	--MyMem;
	if (Chain && (*MyMem < MAXMEM)) {
		FreeVecItem(Chain,MyMem);
	}
	else {
		FreeVec(MyMem);
	}
}

APTR
MyAllocMem(ULONG size,ULONG requirements) {
	ULONG *MyMem;
	if (Chain && (size < MAXMEM)) {
		if (MyMem = AllocItem(Chain,size+4,requirements)) {
			*MyMem = size;
			return ++MyMem;
		}
		else {
			return NULL;
		}
	}
	else {
		if (MyMem = AllocMem(size+4,requirements)) {
			*MyMem = size;
			return ++MyMem;
		}
		else {
			return NULL;
		}
	}
}

void MyFreeMem(APTR memptr,ULONG size) {
	ULONG *MyMem;
	MyMem = memptr;
	--MyMem;
	if (Chain && (*MyMem < MAXMEM)) {
		FreeItem(Chain,MyMem,size+4);
	}
	else {
		FreeMem(MyMem,size+4);
	}
}


// Note no free() is needed
APTR
Mymalloc(ULONG size) {
	if (Chain && (size < MAXMEM)) {
		return AllocItem(Chain,size,0);
	}
	else {
		return malloc(size);
	}
}

APTR
Mystrdup(UBYTE *str) {
	UBYTE *t;
	if (t = Mymalloc(strlen(str)+1)) {
		strcpy(t,str);
	}
	else {
		return NULL;
	}
}

/* Display an error message
 * ErrorMessage	: The main text
 * Gadget			: Text for one gadget
 * extra				: more of the text
 * helpnum			: Number of a help node
 *
 * See the TSMorph source code for more of how this actually works
 */
void
Error(char *ErrorMessage,char *Gadget,char *extra,ULONG helpnum) {
   struct EasyStruct EasyStruct = {
      sizeof(struct EasyStruct),
      0,
      NULL,
      NULL,
      NULL
   };
   UBYTE *title = "TSMorph-render Error";
   struct Window *req;
   UBYTE gad[32] = "Help|";
   UBYTE gad1[32] = "_Help|_";
   ULONG ret = CALL_HANDLER;
	struct AmigaGuideMsg *agm;
	ULONG signals;
	struct rtHandlerInfo *rth;
	// Disable the window and display the requester
   DisableWindow();
   if (ReqToolsBase) {
   	if (!handle) {
			strcpy(gad1,"_");
   	}
		strcat(gad1,Gadget);
		while (ret) {
			ret = CALL_HANDLER;
	   	if (rtEZRequestTags(ErrorMessage,gad1,NULL,&extra,
   									RT_ReqHandler, &rth,
   									RT_Window, ProgressWnd,
   									RT_Underscore, '_',
   									RTEZ_ReqTitle, title,
   									TAG_END) == CALL_HANDLER) {
				while (ret == CALL_HANDLER) {
   				if (!rth->DoNotWait) {
   					signals = Wait(rth->WaitMask | ASig);
   				}
					ret = rtReqHandlerA(rth,signals,NULL);
		   		if (ret == 1) {
			   		help(helpnum);
			   	}
		   		if (signals & ASig) {
					  	while (agm = GetAmigaGuideMsg(handle)) {
				 			ReplyAmigaGuideMsg(agm);
				 		}
				 	}
				}
   		}
   		else {
   			ret = 0;
   		}
   	}
   }
   else {
		EasyStruct.es_TextFormat = ErrorMessage;
		EasyStruct.es_Title = title;
	   if (handle) {
   		strcat(gad,Gadget);
	   }
   	else {
   		strcpy(gad,Gadget);
	   }
		EasyStruct.es_GadgetFormat = gad;
		req = BuildEasyRequest(ProgressWnd,&EasyStruct,NULL,extra);
		while (ret) {
			signals  = Wait((1L << req->UserPort->mp_SigBit) | ASig);
			if (signals & (1L << req->UserPort->mp_SigBit)) {
   			ret = SysReqHandler(req,NULL,FALSE);
	   		if (ret == 1) {
		   		help(helpnum);
		   	}
		   }
	   	if (signals & ASig) {
			  	while (agm = GetAmigaGuideMsg(handle)) {
			 		ReplyAmigaGuideMsg(agm);
			 	}
			}
	   }
   	FreeSysRequest(req);
   }
   EnableWindow();
}

/* Tries to execute a Rexx script
 * msgtxt = name of script
 * IgnoreError = TRUE then do not display error message
 * Returns:	error message
 */
LONG
SendRxMsg(char *msgtxt,BOOL IgnoreError) {
	struct MsgPort *reply_port;
	struct MsgPort *rx_port;
	struct RexxMsg *rx_msg;
	LONG 				ret 			= RC_FATAL;

	if (reply_port = CreateMsgPort()) {
   	if (rx_msg = CreateRexxMsg(reply_port,"TSM","REXX")) {
      	rx_msg->rm_Args[0] = msgtxt;
			if (FillRexxMsg(rx_msg,1,0)) {
				rx_msg->rm_Action = RXCOMM;
        		Forbid();
        		if (rx_port = (struct MsgPort *)FindPort("REXX")) {
          		PutMsg(rx_port, (struct Message *)rx_msg);
          		Permit();
          		WaitPort(reply_port);
          		ReplyMsg(GetMsg(reply_port));
          		ret = rx_msg->rm_Result1;
		     	}
        		else {
          		Permit();
        		}
        		ClearRexxMsg(rx_msg, 1);
      	}
     		DeleteRexxMsg(rx_msg);
    	}
    	DeleteMsgPort(reply_port);
  	}
	// Display an error message (if required)
  	if (!IgnoreError && ret) {
  		Error("Error sending ARexx message\n'%s'","Quit",msgtxt,H_ARexx);
  	}
  	return ret;
}

/* Handle 'Help' key	*/
int
ProgressRawKey(void) {
	switch (ProgressMsg.Code) {
	case 0x5f:
		help(H_Help);
	}
	return 1;
}

// save an image
BOOL
SaveFile(void) {
	struct ILBMInfo 	*ilbm;		// IFF stuff to save
	BPTR					fh;			// File handle for PPM
	BOOL OkFlag = TRUE;
	struct BitMap 	TBM;				// Temp bit map
	struct DiskObject *MyDiskObject;	// The Icon
	char					*iconname;	// iconname
	char					*iconname1;	// alternate iconname
	if (!PPM) {
		// Not PPM
		if (OVFormat) {
			// OpalVision so convert RGB to OpalVision
			RGBtoOV(OScrn,p,0,0,swidth,height);
		}
		else {
			// Set up BitMap
			InitBitMap(&MyBitMap,(DCTV3?3:((DCTV4|BW16)?4:(HAM6?6:((HAM8|BW256)?8:24)))),width,height);
			InitRastPort(&RP);
			RP.BitMap = &MyBitMap;
			InitRastPort(&TRP);
			InitBitMap(&TBM,(DCTV3?3:((DCTV4|BW16)?4:(HAM6?6:((HAM8|BW256)?8:8)))),width,1);
			TBM.BytesPerRow = (((width+15)>>4)<<1);
			TBM.Rows = 1;
			TRP.BitMap = &TBM;
			TBM.Planes[0]=plane0;
			TBM.Planes[1]=plane1;
			TBM.Planes[2]=plane2;
			TBM.Planes[3]=plane3;
			TBM.Planes[4]=plane4;
			TBM.Planes[5]=plane5;
			TBM.Planes[6]=plane6;
			TBM.Planes[7]=plane7;
			if (!HAM6 && !HAM8 && !BW16 && !BW256 && !DCTV3 && !DCTV4) {
				// i.e. 24 bit ILBM?
				// so convert chunky to planar
				MyBitMap.BitMap.Depth = 8;
				MyBitMap.BitMap.Planes[0] = Planes[0];
				MyBitMap.BitMap.Planes[1] = Planes[1];
				MyBitMap.BitMap.Planes[2] = Planes[2];
				MyBitMap.BitMap.Planes[3] = Planes[3];
				MyBitMap.BitMap.Planes[4] = Planes[4];
				MyBitMap.BitMap.Planes[5] = Planes[5];
				MyBitMap.BitMap.Planes[6] = Planes[6];
				MyBitMap.BitMap.Planes[7] = Planes[7];
				WritePixelArray8(&RP,0,0,width-1,height-1,RED,&TRP);
				WaitBlit();
				MyBitMap.BitMap.Planes[0] = Planes[8];
				MyBitMap.BitMap.Planes[1] = Planes[9];
				MyBitMap.BitMap.Planes[2] = Planes[10];
				MyBitMap.BitMap.Planes[3] = Planes[11];
				MyBitMap.BitMap.Planes[4] = Planes[12];
				MyBitMap.BitMap.Planes[5] = Planes[13];
				MyBitMap.BitMap.Planes[6] = Planes[14];
				MyBitMap.BitMap.Planes[7] = Planes[15];
				WritePixelArray8(&RP,0,0,width-1,height-1,GREEN,&TRP);
				WaitBlit();
				MyBitMap.BitMap.Planes[0] = Planes[16];
				MyBitMap.BitMap.Planes[1] = Planes[17];
				MyBitMap.BitMap.Planes[2] = Planes[18];
				MyBitMap.BitMap.Planes[3] = Planes[19];
				MyBitMap.BitMap.Planes[4] = Planes[20];
				MyBitMap.BitMap.Planes[5] = Planes[21];
				MyBitMap.BitMap.Planes[6] = Planes[22];
				MyBitMap.BitMap.Planes[7] = Planes[23];
				WritePixelArray8(&RP,0,0,width-1,height-1,BLUE,&TRP);
				WaitBlit();
			}
			// Reset up BitMap
			MyBitMap.BitMap.Depth = (DCTV3?3:((DCTV4|BW16)?4:(HAM6?6:((HAM8|BW256)?8:24))));
			MyBitMap.BitMap.Planes[0] = Planes[0];
			MyBitMap.BitMap.Planes[1] = Planes[1];
			MyBitMap.BitMap.Planes[2] = Planes[2];
			MyBitMap.BitMap.Planes[3] = Planes[3];
			MyBitMap.BitMap.Planes[4] = Planes[4];
			MyBitMap.BitMap.Planes[5] = Planes[5];
			MyBitMap.BitMap.Planes[6] = Planes[6];
			MyBitMap.BitMap.Planes[7] = Planes[7];
			MyBitMap.BitMap.Planes[8] = Planes[8];
			MyBitMap.BitMap.Planes[9] = Planes[9];
			MyBitMap.BitMap.Planes[10] = Planes[10];
			MyBitMap.BitMap.Planes[11] = Planes[11];
			MyBitMap.BitMap.Planes[12] = Planes[12];
			MyBitMap.BitMap.Planes[13] = Planes[13];
			MyBitMap.BitMap.Planes[14] = Planes[14];
			MyBitMap.BitMap.Planes[15] = Planes[15];
			MyBitMap.BitMap.Planes[16] = Planes[16];
			MyBitMap.BitMap.Planes[17] = Planes[17];
			MyBitMap.BitMap.Planes[18] = Planes[18];
			MyBitMap.BitMap.Planes[19] = Planes[19];
			MyBitMap.BitMap.Planes[20] = Planes[20];
			MyBitMap.BitMap.Planes[21] = Planes[21];
			MyBitMap.BitMap.Planes[22] = Planes[22];
			MyBitMap.BitMap.Planes[23] = Planes[23];
		}
	}
	// Set up output file name
   sprintf(FileName,AnimName,f+Start-1);
	if (PPM) {
		// PPM Save is very simple P6 mode
		AddMessage("Saving PPM image");
		if (fh = Open(FileName,MODE_NEWFILE)) {
			sprintf(buffer,"P6\n%ld %ld\n255\n",width,height);
			FPuts(fh,buffer);
			arrayr=RED;
			arrayg=GREEN;
			arrayb=BLUE;
			// loop thru lines
			for (y=0;
				  (y<height) && OkFlag;
				  y++) {
				// Loop thru columns
				for (x=0;
					  x < width;
					  x++) {
					FPutC(fh,*arrayr);
					FPutC(fh,*arrayg);
					FPutC(fh,*arrayb);
					arrayr++;
					arrayg++;
					arrayb++;
				}
				// skip part word at end of line
				arrayr += (swidth - width);
				arrayg += (swidth - width);
				arrayb += (swidth - width);
			}
			if (!Close(fh)) {
				OkFlag = FALSE;
			}
		}
		else {
			OkFlag = FALSE;
		}
	}
	else {
		// Save non PPM
		if (OVFormat) {
			// Save OpalVision
			if (!strcmp(OVFormat,"JPG")) {
				AddMessage("Saving Opal JPEG image");
				// Save JPEG
				OkFlag = !SaveJPEG24(OScrn,FileName,(OVThumb?0:NOTHUMBNAIL),OVQuality);
			}
			else {
				AddMessage("Saving Opal IFF24 image");
				// Save IFF
				OkFlag = !SaveIFF24(OScrn,FileName,NULL,(OVThumb?0:NOTHUMBNAIL)|(OVFast?OVFASTFORMAT:0));
			}
		}
		else {
			if (HAM6) {
				AddMessage("Saving HAM6 image");
				OkFlag = SaveHAM6(FileName,width,height,swidth,
										pwidth,pheight,pmode,
										RED,GREEN,BLUE,&RP,&TRP);
			}
			else {
				if (HAM8) {
					AddMessage("Saving HAM8 image");
					OkFlag = SaveHAM8(FileName,width,height,swidth,
											pwidth,pheight,pmode,
											RED,GREEN,BLUE,&RP,&TRP);
				}
				else {
					if (BW16) {
						AddMessage("Saving BW16 image");
						OkFlag = SaveBW16(FileName,width,height,swidth,
												pwidth,pheight,pmode,
												RED,GREEN,BLUE,&RP,&TRP);
					}
					else {
						if (BW256) {
							AddMessage("Saving BW256 image");
							OkFlag = SaveBW256(FileName,width,height,swidth,
													 pwidth,pheight,pmode,
													 RED,GREEN,BLUE,&RP,&TRP);
						}
						else {
							if (DCTV3 || DCTV4) {
								AddMessage(DCTV3?"Saving DCTV3 image":"Saving DCTV4 image");
								OkFlag = SaveDCTV(FileName,width,height,swidth,
														pwidth,pheight,pmode,
														RED,GREEN,BLUE,&RP,&TRP);
							}
							else {
								AddMessage("Saving ILBM24 image");
								// allocate some IFF stuff
								if (ilbm = (struct ILBMInfo *)MyAllocMem(sizeof(struct ILBMInfo),MEMF_PUBLIC|MEMF_CLEAR)) {
									if (ilbm->ParseInfo.iff = AllocIFF()) {
										OkFlag = !saveilbm(ilbm, &MyBitMap, pmode,
																 width,  height, pwidth, pheight,
																 NULL, 0, 0,	/* colortable */
																 mskNone, 0,	/* masking, transparent */
																 NULL, NULL, 	/* chunklists */
																 FileName);
										// Close everything down cleanly
										FreeIFF(ilbm->ParseInfo.iff);
									}
									else {
										OkFlag = FALSE;
									}
									MyFreeMem(ilbm,sizeof(struct ILBMInfo));
								}
								else {
									OkFlag = FALSE;
								}
							}
						}
					}
				}
			}
		}
	}
	if (OkFlag) {
		if (CreateIcons) {
			// try to save an Icon
			if (MyDiskObject = GetDiskObject(FileName)) {
				// Do not overwrite if already present
				FreeDiskObject(MyDiskObject);
			}
			else {
				// Set up two possible names
				if (OVFormat) {
					if (!strcmp(OVFormat,"JPG")) {
						iconname = "ENV:TSMorph/def_jpg";
						iconname1 = "ENV:SYS/def_jpg";
					}
					else {
						iconname = "ENV:TSMorph/def_iff";
						iconname1 = "ENV:SYS/def_iff";
					}
				}
				else {
					if (PPM) {
						iconname = "ENV:TSMorph/def_ppm";
						iconname1 = "ENV:SYS/def_ppm";
					}
					else {
						if (BW16) {
							iconname = "ENV:TSMorph/def_bw16";
							iconname1 = "ENV:SYS/def_bw16";
						}
						else {
							if (BW256) {
								iconname = "ENV:TSMorph/def_bw256";
								iconname1 = "ENV:SYS/def_bw256";
							}
							else {
								if (HAM6) {
									iconname = "ENV:TSMorph/def_ham6";
									iconname1 = "ENV:SYS/def_ham6";
								}
								else {
									if (HAM8) {
										iconname = "ENV:TSMorph/def_ham8";
										iconname1 = "ENV:SYS/def_ham8";
									}
									else {
										if (DCTV3) {
											iconname = "ENV:TSMorph/def_dctv3";
											iconname1 = "ENV:SYS/def_dctv3";
										}
										else {
											if (DCTV4) {
												iconname = "ENV:TSMorph/def_dctv4";
												iconname1 = "ENV:SYS/def_dctv4";
											}
											else {
												iconname = "ENV:TSMorph/def_ilbm";
												iconname1 = "ENV:SYS/def_ilbm";
											}
										}
									}
								}
							}
						}
					}
				}
				// Try two names, as last resort use default project
				if ((MyDiskObject = GetDiskObject(iconname)) ||
					 (MyDiskObject = GetDiskObject(iconname1)) ||
					 (MyDiskObject = GetDefDiskObject(WBPROJECT))) {
					PutDiskObject(FileName,MyDiskObject);
					FreeDiskObject(MyDiskObject);
				}
			}
		}
	}
	if (!OkFlag) {
		Error("Error saving file '%s'","Quit",FileName,H_ESave);
	}
	else {
		// Post image processing
		if (strcmp(Postscript,"OFF")) {
			strcpy(buffer,Postscript);
			strcat(buffer," %ld %ld %ld %s");
			sprintf(buffer1,buffer,f+Start-1,Frames,Single,FileName);
			AddMessage("Postscript processing");
			OkFlag = !SendRxMsg(buffer1,FALSE);
		}
	}
	return OkFlag;
}
