
/*****************************************************/
/* IffView.c V1.0                                    */
/* ©1996 Oberdorfer Andreas                          */
/*       Merangasse 40                               */
/*       8010 Graz                                   */
/*       Austria                                     */
/*       +43/316/338234                              */
/* Compiler:  Maxon C/C++ 3.0                        */
/*****************************************************/

#pragma +		// compile in C++ mode

// You should include the standard alib_protos here
// and setting the Link-Libs to amiga.lib
#include	<myamigalib.h>

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

#include	<exec/execbase.h>
#include	<exec/memory.h>
#include	<devices/audio.h>
#include	<graphics/gfxbase.h>
#include	<graphics/videocontrol.h>
#include	<workbench/startup.h>

#include	<pragma/exec_lib.h>
#include	<pragma/dos_lib.h>
#include	<pragma/graphics_lib.h>
#include	<pragma/icon_lib.h>
#include	<pragma/intuition_lib.h>

/*****************************************************/

void	CloseAll(void);
int	WaitAVMsg();
void	SetColors(ULONG *ColTab);

UBYTE	*ViewPic(struct MyIff *Iff);
void	PlaySmpl(struct MyIff *Iff);
void	SetSampleBuffer();
UBYTE	*LoadIff(struct MyIff *Iff);

void	ShowError(UBYTE *Error);

/*****************************************************/

#pragma chip
UWORD	NoMousePointer[8]={0,0,0,0,0,0,0,0};
#pragma fast

#define	MAXFILES	2
#define	SMPLMEMSIZE	(8*1024)

const	UBYTE	Version[]="\0$VER: IffView 1.00 by Poseidon / ©1996 ALCATRAZ Entertainment Software ("__DATE__") Oberdorfer Andreas, Merangasse 40, A-8010 Graz, +43-316-338234";

extern struct Library *SysBase;
extern struct GfxBase *GfxBase;

BOOL	FromWB=FALSE;
BOOL	AutoExit=FALSE;
BOOL	AutoExitEnable=FALSE;
BOOL	AudioDev=FALSE;				// indicates 1 or 2

struct Screen			*ScreenHd=NULL;
struct Window			*WindowHd=NULL;

UBYTE	*SmplMem1=NULL;
UBYTE	*SmplMem2=NULL;
struct IOAudio AudioIO1;
struct IOAudio AudioIO2;
struct IOAudio *AktAudioIO;		// Pointer depends on AudioDev index
struct MsgPort *AudioMP1=NULL;
struct MsgPort *AudioMP2=NULL;
UBYTE	AudChannel[]={0x01,0x02,0x04,0x08};

UBYTE	*RepSmpl,*AktSmpl;
ULONG	RepSmplLen=0,AktSmplLen=0;

struct MyIff
{
	UBYTE	*FileName;
	ULONG	ID;
	ULONG	*MemAdr;
	ULONG	Size;
	ULONG	*Header;
	UBYTE	*CMap;
	BYTE	*Body;
};

struct MyIff Iff[MAXFILES];

UBYTE	IffName1[256];
UBYTE	IffName2[256];
ULONG	LoadC32[256*3+2];
UWORD	LoadC04[256];

/*****************************************************/

void	main(int argc,char **argv)
{
	UBYTE	*Error=NULL;
	LONG	i;

	if	(SysBase->lib_Version>=36)
	{
		atexit(CloseAll);

		for (i=0;i<MAXFILES;i++)
		{
			Iff[i].MemAdr=NULL;
			Iff[i].Header=NULL;
		}

		if	(!FromWB)
		{
			for (i=0;i<MAXFILES;i++)
			{
				if	(argc>=2+i)
					Iff[i].FileName=argv[i+1];
				else
					Iff[i].FileName=NULL;
			}

			if	(Iff[0].FileName)
			{
				if	(!(strcmp(Iff[0].FileName,"?")))
				{
					cout << "\nUsage: IffView Picture [Sample] [AUTOEXIT]\n\n";
					exit(0);
				}
			}

			if	(argc==4)
			{
				strupr(argv[3]);
				if	(!(strcmp(argv[3],"AUTOEXIT")))
					AutoExitEnable=TRUE;
			}

		}

		for (i=0;i<MAXFILES;i++)
		{
			if	(Iff[i].FileName)
			{
				if	((Error=LoadIff(&Iff[i])))
					goto Err;
			}
			else
			{
				if	(!i)
				{
					if	(FromWB)
						Error="No Picture FileName\nToolTypes are:\nPICTURE=FileName\nSAMPLE=FileName\nAUTOEXIT"
					else
						Error="No Picture FileName\nUsage: IffView Picture [Sample] [AUTOEXIT]";
					goto Err;
				}
			}
		}

		if	((Error=ViewPic(&Iff[0])))
			goto Err;

		PlaySmpl(&Iff[1]);

		ScreenToFront(ScreenHd);

		exit(WaitAVMsg());
	}

	Error="AmigaOS 2.0 (V36) or higher required";

Err:
	ShowError(Error);
	exit(10);
}

/*****************************************************/
/* Get Tooltypes                                     */
/*****************************************************/

extern "C" void	wbmain(struct WBStartup *ws)
{
	struct DiskObject *Icon;
	UBYTE	*ToolStr;

	FromWB=TRUE;

   CurrentDir(ws->sm_ArgList[0].wa_Lock);

	if	((Icon=GetDiskObject(ws->sm_ArgList[0].wa_Name)))
	{
		if	((ToolStr=FindToolType((UBYTE **)Icon->do_ToolTypes,"PICTURE")))
		{
			strncpy(IffName1,ToolStr,256);
			Iff[0].FileName=IffName1;
		}

		if	((ToolStr=FindToolType((UBYTE **)Icon->do_ToolTypes,"SAMPLE")))
		{
			strncpy(IffName2,ToolStr,256);
			Iff[1].FileName=IffName2;
		}

		if	(FindToolType((UBYTE **)Icon->do_ToolTypes,"AUTOEXIT"))
			AutoExitEnable=TRUE;

		FreeDiskObject(Icon);
	}

	main(0,NULL);
}

/*****************************************************/
/* Free ALL SystemResources                          */
/*****************************************************/

void	CloseAll(void)
{
	LONG	i;

	if (AudioDev)
		CloseDevice((struct IORequest *)&AudioIO1);
	if (AudioMP1)
		DeletePort(AudioMP1);
	if (AudioMP2)
		DeletePort(AudioMP2);

	if	(SmplMem1)
		FreeMem(SmplMem1,SMPLMEMSIZE);
	if	(SmplMem2)
		FreeMem(SmplMem2,SMPLMEMSIZE);

	if	(WindowHd)
	{
		ScreenToBack(ScreenHd);
		SetColors(LoadC32);
		CloseWindow(WindowHd);
	}
	if	(ScreenHd)
		CloseScreen(ScreenHd);

	for (i=0;i<MAXFILES;i++)
	{
		if (Iff[i].MemAdr)
		{
			FreeMem(Iff[i].MemAdr,Iff[i].Size);
			Iff[i].MemAdr=0;
		}
	}
}

/*****************************************************/
/* Get IntuiMessage: Mouse, VanillaKeys              */
/*     AudioMessage                                  */
/*****************************************************/

int	WaitAVMsg()
{
	struct IntuiMessage	*IMsg;
	int	Ret=0;
	ULONG	Class;
	UWORD	Code;
	BOOL	Exit=FALSE;

	ULONG	Sig,AudSig=0;

	while	(!Exit)
	{
		if	(AudioDev)
			AudSig=1<<AktAudioIO->ioa_Request.io_Message.mn_ReplyPort->mp_SigBit;

		Sig=Wait((1<<WindowHd->UserPort->mp_SigBit)|(AudSig));

		if	(Sig&AudSig)
		{
			GetMsg(AktAudioIO->ioa_Request.io_Message.mn_ReplyPort);
			if	(AutoExit)
			{
				if	(AutoExit==2)
					Exit=TRUE;
				else
					AutoExit++;
			}
			SetSampleBuffer();
		}

		if	(Sig&(1<<WindowHd->UserPort->mp_SigBit))
		{
			while ((IMsg=(struct IntuiMessage*) GetMsg(WindowHd->UserPort)))
			{
				Class=IMsg->Class;
				Code=IMsg->Code;
				ReplyMsg((struct Message*) IMsg);

				switch (Class)
				{
					case IDCMP_VANILLAKEY:
						if	(Code==27)						// ESC
						{
							Exit=TRUE;
							Ret=5;
						}
						if	((Code==13)||(Code==32))	// Return | Space
							Exit=TRUE;
					break;

					case IDCMP_MOUSEBUTTONS:
						Exit=TRUE;
					break;
				}
			}
		}
	}
	return(Ret);
}

/*****************************************************/
/* Set Palette                                       */
/*****************************************************/

void	SetColors(ULONG *ColTab)
{
	ULONG	i;

	if	(SysBase->lib_Version<39)
	{
		for (i=0;i<(ColTab[0]>>16);i++)
		{
			LoadC04[i]=((ColTab[1+i*3]>>20)&0x0f00)+
							((ColTab[2+i*3]>>24)&0x00f0)+
							(ColTab[3+i*3]>>28);
		}
		LoadRGB4(&ScreenHd->ViewPort,LoadC04,ColTab[0]>>16);
	}
	else
		LoadRGB32(&ScreenHd->ViewPort,ColTab);				// V39
}

/*****************************************************/
/* Decrunch or copy ILBM Body                        */
/*****************************************************/

void	DecrunchBody(UBYTE *Body, UBYTE *Dest, ULONG BWidth, ULONG PlnSize, ULONG Depth)
{
	char	b;
	ULONG	x=0,Pln=0;
	UBYTE	*Src,*End;

	Src=Body+8;
	End=Src+*((ULONG *)Body+1);

	while (Src<End)
	{
		b=*(Src++);
		if (b<0)
		{
			while (b<=0)
			{
				*(Dest++)=*Src;
				b++;
				if (++x>=BWidth)
				{
					if	(++Pln>=Depth)
					{
						Dest-=(PlnSize*(Depth-1));
						Pln=0;
					}
					else
					{
						Dest+=(PlnSize-BWidth);
					}
					x=0;
				}
			}
			Src++;
		}
		else
		{
			while (b>=0)
			{
				*(Dest++)=*(Src++);
				b--;
				if (++x>=BWidth)
				{
					if	(++Pln>=Depth)
					{
						Dest-=(PlnSize*(Depth-1));
						Pln=0;
					}
					else
					{
						Dest+=(PlnSize-BWidth);
					}
					x=0;
				}
			}
		}
	}
}

void	CopyBody(UBYTE *Body, UBYTE *Dest, ULONG BWidth, ULONG PlnSize, ULONG Depth)
{
	ULONG	x=0,Pln=0;
	UBYTE	*Src,*End;

	Src=Body+8;
	End=Src+*((ULONG *)Body+1);

	while (Src<End)
	{
		*(Dest++)=*(Src++);

		if (++x>=BWidth)
		{
			if	(++Pln>=Depth)
			{
				Dest-=(PlnSize*(Depth-1));
				Pln=0;
			}
			else
			{
				Dest+=(PlnSize-BWidth);
			}
			x=0;
		}
	}
}

/*****************************************************/
/* Show Picture                                      */
/*****************************************************/

UBYTE	*ViewPic(struct MyIff *Iff)
{
	struct Rectangle Oscan;
	struct TagItem	VCTags[]=
	{
			VTAG_BORDERBLANK_SET,	TRUE,
			TAG_DONE
	};

	struct TagItem ModeTags[]=
	{
		BIDTAG_NominalWidth,		0,
		BIDTAG_NominalHeight,	0,
		BIDTAG_DesiredWidth,		0,
		BIDTAG_DesiredHeight,	0,
		BIDTAG_Depth,				0,
		TAG_DONE
	};

	struct BitMap PicBM;
	UBYTE	*PicMem;

	ULONG	Width,Height,Depth,DisplayID=LORES_KEY;

	UBYTE	*Dest,Comp;
	ULONG	i,BWidth,PlnSize;
	BOOL	StandardBM=FALSE;

	if	(Iff->Header[0]!='BMHD')
		return("Not a IFF Picture");

	if (*((UBYTE *)Iff->Header+17))
		return("No Masking Plane support");

	Comp=(*((UBYTE *)Iff->Header+18));
	if	(Comp>1)
		return("Unknown Picture Compression");

	Width=*((UWORD *)Iff->Header+4);
	Height=*((UWORD *)Iff->Header+5);
	Depth=*((UBYTE *)Iff->Header+16);
	if	(Depth>8)
		return("More than 256 Colors not supported");

	if	(Iff->CMap)
	{
		if	(*((ULONG *)Iff->CMap+1)!=(1<<Depth)*3)
			return("ColorMap doesn't match depth (HAM)");
	
		LoadC32[0]=(1<<Depth)<<16;
		for (i=0;i<(1<<Depth)*3;i++)
			LoadC32[1+i]=0;
		LoadC32[1+i]=0;
	}
	else
		return("No ColorMap in Picture");

	if	(SysBase->lib_Version>=39)
	{
		ModeTags[0].ti_Data=Width;
		ModeTags[1].ti_Data=Height;
		ModeTags[2].ti_Data=Width;
		ModeTags[3].ti_Data=Height;
		ModeTags[4].ti_Data=Depth;
		DisplayID=BestModeIDA(ModeTags);
	}
	else
	{
		if	(Width>352)
			DisplayID|=HIRES_KEY;
		if	(Height>283)
			DisplayID|=LORESLACE_KEY;
	}

	if	(QueryOverscan(DisplayID,&Oscan,OSCAN_STANDARD))
	{
		Oscan.MinX=((Oscan.MaxX-Oscan.MinX+1)-Width)/2;
		Oscan.MinY=((Oscan.MaxY-Oscan.MinY+1)-Height)/2;
		if	(Oscan.MinY>0)
			Oscan.MinY=0;
	}
	else
	{
		Oscan.MinX=0;
		Oscan.MinY=0;
	}

	BWidth=Width/8;
	PlnSize=BWidth*Height;

	if (!(PicMem=(UBYTE *)AllocMem(PlnSize*Depth,MEMF_CHIP|MEMF_CLEAR)))
		return("Out of ChipMemory");

	InitBitMap(&PicBM,Depth,Width,Height);
	Dest=PicMem;
	for (i=0;i<Depth;i++)
	{
		PicBM.Planes[i]=Dest;
		Dest+=PlnSize;
	}

	if	(Comp)
		DecrunchBody(Iff->Body,PicMem,BWidth,PlnSize,Depth);
	else
		CopyBody(Iff->Body,PicMem,BWidth,PlnSize,Depth);

	if (!(ScreenHd=OpenScreenTags(NULL, 			// V36
		SA_Left,				Oscan.MinX,
		SA_Top,				Oscan.MinY,
		SA_Width,			Width,
		SA_Height,			Height,
		SA_Depth,			Depth,
		SA_Title,			Iff->FileName,
		SA_ShowTitle,		FALSE,
		SA_Type,				CUSTOMSCREEN | SCREENQUIET,
		SA_AutoScroll,		TRUE,
		SA_Behind,			TRUE,
		SA_Overscan,		OSCAN_STANDARD,
		SA_DisplayID,		DisplayID,					// V37 (ScreenModeReq = V38)
		SA_VideoControl,	VCTags,						// V39
		SA_Colors32,		LoadC32,
		TAG_DONE)))
		return("Can't open Screen");

	if	(SysBase->lib_Version<39)
		SetColors(LoadC32);

	if (!(WindowHd=OpenWindowTags(NULL,				// V36
		WA_Left,				0,
		WA_Top,				0,
		WA_Width,			Width,
		WA_Height,			Height,
		WA_Flags,			WFLG_NOCAREREFRESH|
								WFLG_ACTIVATE|
								WFLG_BORDERLESS|
								WFLG_BACKDROP|
								WFLG_RMBTRAP,
		WA_IDCMP,			IDCMP_MOUSEBUTTONS |
								IDCMP_VANILLAKEY,
		WA_CustomScreen,	ScreenHd,
		TAG_DONE)))
		return("Can't open Window");

	SetPointer(WindowHd,NoMousePointer,1,16,0,0);

	// We have to Blit the Picture to the Screen
	// cause the ScreenBitmap may be Chunky (or unknown) on Grapiccards
	// so the Graphiccarddriver will do the needed conversion for us
	BltBitMap(&PicBM,0,0,WindowHd->RPort->BitMap,0,0,Width,Height,0x00c0,0xff,NULL);
	WaitBlit();

	FreeMem(PicMem,PlnSize*Depth);

	for (i=0;i<(1<<Depth)*3;i++)
		LoadC32[1+i]=Iff->CMap[8+i]<<24;
	SetColors(LoadC32);

	for (i=i;i<(1<<Depth)*3;i++)
		LoadC32[1+i]=0;

	return(NULL);
}

/*****************************************************/
/* Play Sample                                       */
/*****************************************************/

void	PlaySmpl(struct MyIff *Iff)
{
	volatile UBYTE	*LowPassFilter=(UBYTE *) 0xbfe001;
	UBYTE	*Smpl;
	ULONG	SmplLen;

	if	(Iff->Header)
	{
		if	(Iff->Header[0]!='VHDR')
		{
			ShowError("Not a IFF Sample");
			return;
		}

		if	(*((UBYTE *)Iff->Header+15+8))
		{
			ShowError("Sample Compression not supported");
			return;
		}

		if	((AudioMP1=CreatePort(NULL,0)))
		if	((AudioMP2=CreatePort(NULL,0)))
		{
			AudioIO1.ioa_Request.io_Message.mn_ReplyPort   =AudioMP1;
			AudioIO1.ioa_Request.io_Message.mn_Node.ln_Pri =127;
			AudioIO1.ioa_Request.io_Command                =ADCMD_ALLOCATE;
			AudioIO1.ioa_Request.io_Flags                  =ADIOF_NOWAIT;
			AudioIO1.ioa_AllocKey                          =0;
			AudioIO1.ioa_Data                              =AudChannel;
			AudioIO1.ioa_Length                            =sizeof(AudChannel);	

			if	(!OpenDevice("audio.device",0,(struct IORequest *)&AudioIO1,0L))
				AudioDev=1;
		}
		if	(!AudioDev)
		{
			ShowError("Can't allocate AudioChannel");
			return;
		}

		AudioIO1.ioa_Request.io_Command=CMD_WRITE;
		AudioIO1.ioa_Request.io_Flags=ADIOF_PERVOL;

		AudioIO1.ioa_Volume=64;

		if(GfxBase->DisplayFlags&PAL)
			AudioIO1.ioa_Period=(3546895/(*((UWORD *)Iff->Header+6+4)));	// PAL
		else
			AudioIO1.ioa_Period=(3579545/(*((UWORD *)Iff->Header+6+4)));	// NTSC

		AudioIO1.ioa_Cycles=1;

		AudioIO2=AudioIO1;
		AudioIO1.ioa_Request.io_Message.mn_ReplyPort=AudioMP1;
		AudioIO2.ioa_Request.io_Message.mn_ReplyPort=AudioMP2;

		if	(!(SmplMem1=(UBYTE *)AllocMem(SMPLMEMSIZE,MEMF_CHIP|MEMF_CLEAR)))
		{
			ShowError("Out of ChipMemory");
			return;
		}
		if	(!(SmplMem2=(UBYTE *)AllocMem(SMPLMEMSIZE,MEMF_CHIP|MEMF_CLEAR)))
		{
			ShowError("Out of ChipMemory");
			return;
		}

		*LowPassFilter|=0x02;

		AudioIO1.ioa_Data=SmplMem1;
		AudioIO2.ioa_Data=SmplMem2;

		Smpl=Iff->Body+8;
		SmplLen=Iff->Header[2];
		RepSmpl=Smpl+SmplLen;
		RepSmplLen=Iff->Header[3];

		if	(SmplLen)
		{
			AktSmpl=Smpl;
			AktSmplLen=SmplLen;
		}
		else
		{
			if	(RepSmplLen)
			{
				AktSmpl=RepSmpl;
				AktSmplLen=RepSmplLen;
			}
			else
			{
				ShowError("Sample VHDR damaged");
				return;
			}
		}

		AktAudioIO=&AudioIO1;
		SetSampleBuffer();
		SetSampleBuffer();
	}
}

void	SetSampleBuffer()
{
	ULONG	Len;

	if	(AktSmplLen)
	{
		if	(AktSmplLen>SMPLMEMSIZE)
			Len=SMPLMEMSIZE;
		else
			Len=AktSmplLen;

		CopyMem(AktSmpl,AktAudioIO->ioa_Data,Len);
		AktAudioIO->ioa_Length=Len;
		BeginIO((struct IORequest *)AktAudioIO);

		AktSmplLen-=Len;

		if	(AktSmplLen)
			AktSmpl+=Len;
		else
		{
			AktSmpl=RepSmpl;
			if	(!(AktSmplLen=RepSmplLen))
			{
				if (AutoExitEnable)
					AutoExit=1;
			}
		}
	}

	if	(AudioDev==1)
	{
		AktAudioIO=&AudioIO2;
		AudioDev=2;
	}
	else
	{
		AktAudioIO=&AudioIO1;
		AudioDev=1;
	}
}

/*****************************************************/
/* Load Iff File                                     */
/*****************************************************/

UBYTE	*LoadIff(struct MyIff *Iff)
{
	FILE	*LoadHD;
	ULONG	LoadBuff[3];
	ULONG	*Data,*DataEnd;
	UBYTE	*Error=NULL;

	if ((LoadHD=fopen(Iff->FileName,"r")))
	{
		if (fread(LoadBuff,sizeof(LoadBuff),1,LoadHD))
		{
			if (LoadBuff[0]=='FORM')
			{
				LoadBuff[1]-=4;
				Iff->ID=LoadBuff[2];
				Iff->Size=LoadBuff[1];

				switch (LoadBuff[2])
				{
					case '8SVX':
					case 'ILBM':
						if	(!(Iff->MemAdr=(ULONG *)AllocMem(LoadBuff[1],MEMF_PUBLIC|MEMF_CLEAR)))
							Error="Out of Memory";
					break;

					default:
						Error="IFF type not supported";
					break;
				}

				if	(Iff->MemAdr)
				{
					if (fread(Iff->MemAdr,Iff->Size,1,LoadHD))
					{
						Iff->Header=NULL;
						Iff->Body=NULL;
						Iff->CMap=NULL;

						Data=Iff->MemAdr;
						(UBYTE *)DataEnd=(UBYTE *)Data+Iff->Size;

						while	((Data<DataEnd)&&(!Iff->Body))
						{
							switch (*Data)
							{
								case 'BMHD':
									Iff->Header=Data;
								break;

								case 'VHDR':
									Iff->Header=Data;
								break;

								case 'CMAP':
									Iff->CMap=(UBYTE *)Data;
								break;

								case 'BODY':
									Iff->Body=(UBYTE *)Data;
								break;
							}
							(UBYTE *)Data+=(*(Data+1)+1&(-2L))+8;
						}
						if	((!Iff->Header)||(!Iff->Body))
							Error="IFF file damaged";
					}
					else
						Error="IFF file damaged";
				}
			}
			else
				Error="Not a IFF File";
		}
		else
			Error="Can't load File";
		fclose(LoadHD);
	}
	else
		Error="Can't open File";

	return(Error);
}

/*****************************************************/
/* Requester on Error                                */
/*****************************************************/

struct EasyStruct ReqES={sizeof(struct EasyStruct),0,0,0,0};

void	ShowError(UBYTE *Error)
{
	struct IntuiMessage	*IMsg;
	struct Window *ReqWin;
	LONG	Erg=0;
	struct Gadget			*IAdr;
	ULONG						IClass;
	UWORD						ICode;

	ReqES.es_Title="IffView Error";
	ReqES.es_TextFormat=Error;
	ReqES.es_GadgetFormat="Ok";

	if	(FromWB)
	{
		if ((ReqWin=BuildEasyRequest(NULL,&ReqES,IDCMP_VANILLAKEY)))		// V36
		{
			Erg=-1;
			while (Erg==-1)
			{
				Wait(1<<ReqWin->UserPort->mp_SigBit);
				while ((IMsg=(struct IntuiMessage *)GetMsg(ReqWin->UserPort)))
				{
					IClass=IMsg->Class;
					ICode=IMsg->Code;
					IAdr=(struct Gadget*) IMsg->IAddress;
					ReplyMsg((struct Message *)IMsg);

					if	(IClass==IDCMP_GADGETUP)
						Erg=IAdr->GadgetID;

					if (IClass==IDCMP_VANILLAKEY)
					{
						if (ICode==27)					// ESC Key = rightmost Gadget
							Erg=0;
						if (ICode==13)					// RETURN Key = leftmost Gadget
							Erg=1;
					}
				}
			}
			FreeSysRequest(ReqWin);

			return;
		}
	}
	cout << "\n" << (char *)Error << "\n\n";
}

