/*							----------------------
									main.c

							 © 1992,1993 REELSOFT
							----------------------
*/

#include	<proto/asl.h>
#include	<proto/dos.h>
#include	<proto/exec.h>
#include	<proto/gadtools.h>
#include	<proto/graphics.h>
#include	<proto/icon.h>
#include	<proto/intuition.h>
#include	<proto/powerpacker.h>
#include	<proto/reqtools.h>
#include	<proto/utility.h>
#include	<proto/wb.h>

#include	<libraries/ppbase.h>
#include	<libraries/reqtools.h>

#include	<graphics/gfxmacros.h>
#include	<intuition/gadgetclass.h>
#include	<workbench/icon.h>
#include	<workbench/startup.h>

#include	<ctype.h>
#include	<dos.h>
#include	<fcntl.h>
#include	<ios1.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>

#include	"protos.h"
#include	"macros.h"
#include	"debug.h"

#include	"locale.h"
#include	"window.h"
#include	"main.h"

//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
VOID	__stdargs __main (char *line)
{
	if (*line) {
		while (isspace(*line)) line++ ;
		while (*(++line)!='"') ;
		while (isspace(*(++line))) ;
	}

	//	started from the CLI with one ore more arguments..
	if (*line!='\0') {
		struct	UFB	ufbs[3] ;

		//	open standard files..
		ufbs[0].ufbnxt = &ufbs[1] ;
		ufbs[1].ufbnxt = &ufbs[2] ;
		ufbs[2].ufbnxt = NULL ;
		__ufbs = &ufbs[0] ;
		ufbs[0].ufbfn  = ufbs[1].ufbfn = ufbs[2].ufbfn = NULL ;
		ufbs[0].ufbflg = UFB_RA | O_RAW | UFB_NC ;
		ufbs[1].ufbflg = UFB_WA | O_RAW | UFB_NC ;
		ufbs[2].ufbflg = UFB_WA | O_RAW | UFB_NC | UFB_CLO ;
		ufbs[0].ufbfh  = Input() ;
		ufbs[1].ufbfh  = Outfh = Output() ;
		ufbs[2].ufbfh  = Open ("CONSOLE:",MODE_OLDFILE) ;

		__nufbs += 3 ;

		stdin->_file  = 0 ;
		stdin->_flag  = _IOREAD ;
		stdout->_file = 1 ;
		stdout->_flag = _IOWRT | _IOLBF ;
		stderr->_file = 2 ;
		stderr->_flag = _IOWRT | _IOLBF ;

		if (CLIinit()) CLIonly() ;
	}
	//	started from the Workbench or from the CLI with no argument..
	else if (WBinit()) MainLoop() ;

	Cleanup (TRUE) ;
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
BOOL	WBinit (VOID)
{
	STRPTR	pubscreen = NULL,
			language  = NULL ;

	mcPass = FALSE ;
	mcPrfs = VERB | PATTERN ;

	if (*_WBenchMsg->sm_ArgList->wa_Name) {
		struct	Library		*IconBase ;
		struct	DiskObject	*dobj ;

		if (IconBase = OpenLibrary (ICONNAME,37L)) {
			if (dobj = GetDiskObject (_WBenchMsg->sm_ArgList->wa_Name)) {
				BYTE	*s, **tt = dobj->do_ToolTypes ;

				pubscreen = FindToolType (tt,"PUBSCREEN") ;
				language  = FindToolType (tt,"LANGUAGE") ;

				mcPara[EFF] = (UBYTE)atoi(FindToolType (tt,"EFFICIENCY")) ;
				mcPara[BUF] = (UBYTE)atoi(FindToolType (tt,"BUFFER")) ;
				mcPara[DEC] = (UBYTE)atoi(FindToolType (tt,"DECRUNCHING")) ;

				if (s = FindToolType (tt,"ENCRYPT"))	mcPass = MatchToolValue(s,"ON") ;
				if (s = FindToolType (tt,"COLOR"))		mcApen = (UBYTE)atoi(s) ;
				if ((s = FindToolType (tt,"ASL"))
					&& MatchToolValue (s,"ON"))			mcPrfs |= ASL ;
				if ((s = FindToolType (tt,"VERBOSE"))
					&& NOT MatchToolValue (s,"ON"))		mcPrfs ^= VERB ;
				if ((s = FindToolType (tt,"PATTERN"))
					&& NOT MatchToolValue (s,"ON"))		mcPrfs ^= PATTERN ;

				FreeDiskObject (dobj) ;
			}
			CloseLibrary (IconBase) ;
		}
	}
	Translate (language) ;
	return (Setup(pubscreen)) ;
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
VOID	About (VOID)	//	`About PPMC' requester..
{
	STRPTR	rtargs[4] ;

	RT_TITLE (MSG_ABOUT) ;

	rtargs[0] = &_VERSION_[6] ;
	rtargs[1] = _COPYRIGHT_ ;
	rtargs[2] = LOC(MSG_WRITTEN) ;
	rtargs[3] = LOC(MSG_FREEWARE) ;

	rtEZRequestA ("%s\n%s\n\n%s "AUTHOR"\n\n%s",LOC(MSG_OK_CONTINUE),NULL,rtargs,(struct TagItem *)rtTags) ;
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
VOID	Load (BOOL choose)	//	loading a file (Intuition interface only)..
{
	if (choose && NOT ChooseFile(TRUE)) return ;
	switch (AnalyseFile()) {
		case 1 :	//	unable to open the file!..
					OpenError() ;
		//			break ;
		case 2 :	//	file is empty!..
		/*			{
						STRPTR	rtargs[3] ;

						RT_TITLE (MSG_TITLE_ERROR) ;

						rtargs[0] = (ULONG)Name.fpart ;
						rtargs[1] = (ULONG)LOC(MSG_EMPTY_FILE) ;

						rtEZRequestA ("%s\n%s",LOC(MSG_OK_ABORT),NULL,rtargs,(struct TagItem *)rtTags) ;
					}
					break ;
		*/
		case 3 :	//	user doesn't want to pack an executable file..
					break ;

		case 4 :	//	file is powerpacked and encrypted..
		case 5 :	//	file is powerpacked..
		case 0 :	//	file seems OK..
					ClearGads (mcFsze) ;
					SETTXT (Name.fpart) ;
					SETGAD (Gads[GDX_FileName],Wnd,NULL,(struct TagItem *)gtxtTags) ;

					DISABLE(TRUE) ;
					SETGAD (Gads[GDX_Save],Wnd,NULL,(struct TagItem *)gdisTags) ;

					DISABLE(NOT(mcStat & PACK)) ;
					SETGAD (Gads[GDX_Pack],Wnd,NULL,(struct TagItem *)gdisTags) ;

					DISABLE(mcStat & PACK) ;
					SETGAD (Gads[GDX_UnPack],Wnd,NULL,(struct TagItem *)gdisTags) ;

					FillGauge (EMPTY) ;

					OffMenu (Wnd,MNUM(0,3)) ;
					OffMenu (Wnd,MNUM(0,4)) ;
					OffMenu (Wnd,MNUM(2,(BOOL)(mcStat & PACK))) ;
					OnMenu  (Wnd,MNUM(2,NOT(BOOL)(mcStat & PACK))) ;

					break ;
	}
	return ;
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
const UBYTE	exeofs[] = { 0x00,0x00,0x03,0xf3,0x00,0x00,0x00,0x00 } ;
UBYTE	AnalyseFile (VOID)	//	analyse the loaded file..
{
	struct	FileInfoBlock	fib ;
	BPTR	flock ;
	UBYTE	offset[8] ;

	if (NOT(flock = Open (Name.old,MODE_OLDFILE))) return(1) ;

	if (NOT Read (flock,offset,8L)) {
		Close (flock) ;
		return(2) ;
	}
	ExamineFH (flock,&fib) ;
	Close (flock) ;

	Name.fpart = FilePart (Name.old) ;

	//	is the loaded file executable?..
	if (NOT strncmp (offset,exeofs,8)) {
		if (NOT(mcPrfs & CLI)) {
			STRPTR	rtargs[2] ;

			RT_TITLE (MSG_TITLE_WARNING) ;

			rtargs[0] = Name.fpart ;
			rtargs[1] = LOC(MSG_EXECUTABLE) ;

			if (NOT rtEZRequestA ("\"%s\"\n%s",LOC(MSG_OK_OKCANCEL),NULL,rtargs,(struct TagItem *)rtTags))
				return(3) ;
		}
		else if (NOT mcRArgs[OPT_FORCE]) {
			PutStr (_HEADLINE_) ;
			PutStr (Name.fpart) ;
			PutStr (LOC(MSG_CLI_EXECUTABLE)) ;
			if (CLIRequest()) return(3) ;
		}
	}

	strcpy (Name.new,Name.old) ;
	mcFsze = fib.fib_Size ;

	if (mcPrfs & CLI) {
		PutStr (_HEADLINE_) ;
		Printf ("›1m%s (%ld/%ld)›0m - %s (%ld %s)\n",LOC(MSG_CLI_ORIGINAL_FILE),Name.num,Name.max,Name.fpart,mcFsze,LOC(MSG_CLI_BYTES)) ;
	}

	mcStat |= PACK ;

	//	is the file packed and encrypted?..
	if (NOT strncmp (offset,"PX20",4)) {
		mcStat ^= PACK ;
		return(4) ;
	}

	//	so, maybe it's only packed?..
	if (NOT strncmp (offset,"PP20",4)) {
		mcStat ^= PACK ;
		return(5) ;
	}
	return(0) ;	//	file seems OK..
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
BOOL	ChooseFile (BOOL load)
{
	STRPTR	title  = LOC(load ? MSG_TITLE_LOAD : MSG_TITLE_SAVE),
			button = LOC(load ? MSG_OK_LOAD : MSG_PROJECT_SAVE) ;
	UBYTE	path[256], filename[108] ;
	BOOL	ret ;

	RT_TITLE (MSG_TITLE_ERROR) ;
	if (mcPrfs & ASL) {
		struct	FileRequester	*aslfreq ;
		BOOL	len ;

		if (NOT(aslfreq = AllocAslRequest (ASL_FileRequest,NULL))) {
			rtEZRequestA (LOC(MSG_ERROR_MEMORY),LOC(MSG_OK_ABORT),NULL,NULL,(struct TagItem *)rtTags) ;
			return FALSE ;
		}

		if (load) aslfrTags[9]  = NULL ;
		else {
			stcgfp (path,Name.new) ;
			aslfrTags[9]  = (ULONG)Name.fpart ;
			aslfrTags[11] = (ULONG)path ;
		}
		aslfrTags[5]  = (ULONG)title ;
		aslfrTags[7]  = (ULONG)button ;
		aslfrTags[13] = NOT load ;

		ret = AslRequest (aslfreq,(struct TagItem *)aslfrTags) ;
		if (ret && (len = (BOOL)(aslfreq->fr_File)[0])) {
			strcpy (path,aslfreq->fr_Drawer) ;
			strcpy (filename,aslfreq->fr_File) ;
		}

		FreeAslRequest (aslfreq) ;

		if (NOT ret || NOT len) return FALSE ;
	}
	else {
		struct	rtFileRequester	*rtfreq ;

		if (NOT(rtfreq = rtAllocRequestA (RT_FILEREQ,NULL))) {
			rtEZRequestA (LOC(MSG_ERROR_MEMORY),LOC(MSG_OK_ABORT),NULL,NULL,(struct TagItem *)rtTags) ;
			return FALSE ;
		}

		if (load) filename[0] = '\0' ;
		else {
			strcpy (filename,Name.fpart) ;
			rtfrTags[7] |= FREQF_SAVE ;
		}
	//	if (load) filename[0] = '\0' ;
	//	strcpy (filename,load?"":Name.fpart) ;
	//	rtfrTags[7] |= long ? 0 : FREQF_SAVE ;
		rtfrTags[9]  = (ULONG)button ;

		ret = (BOOL)rtFileRequestA (rtfreq,filename,title,(struct TagItem *)rtfrTags) ;
		if (ret) strcpy (path,rtfreq->Dir) ;

		rtFreeRequest (rtfreq) ;

		if (NOT ret) return FALSE ;
	}

	AddPart (path,filename,256L) ;

	strcpy (load?Name.old:Name.new,path) ;
	if (NOT load) Name.fpart = FilePart(Name.new) ;

	return TRUE ;
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
BOOL	Save (BOOL choose)	//	selecting a file to Save to..
{
	BPTR	flock ;

	//	destination file Name..
	if (mcRArgs[OPT_TO]) strcpy (Name.new,(UBYTE *)mcRArgs[OPT_TO]) ;
	else {
		STRPTR	ext = ".pp" ;
		WORD	len = strlen (Name.new) ;

		if (mcStat & PACK) strcpy (&Name.new[len],ext) ;
		else if (NOT strncmp (&Name.new[len-3],ext,3)) Name.new[len-3] = '\0' ;
	}
	Name.fpart = FilePart (Name.new) ;

	if (choose && NOT ChooseFile(FALSE)) return FALSE ;

	//	checking if the destination file already exists..
	if(NOT access (Name.new,F_OK)) {
		if (NOT(mcPrfs & CLI)) {
			STRPTR	rtargs[2] ;

			RT_TITLE (MSG_TITLE_WARNING) ;

			rtargs[0] = Name.fpart ;
			rtargs[1] = LOC(MSG_FILE_EXISTS) ;

			switch (rtEZRequestA ("%s\n%s",LOC(MSG_OK_OVERWRITE),NULL,rtargs,(struct TagItem *)rtTags)) {
				case 1 :	if (ChooseFile(FALSE)) break ;
				case 0 :	return FALSE ;
				case 2 :	break ;
			}
		}
		else if (NOT mcRArgs[OPT_FORCE]) {
			PutStr (_HEADLINE_) ;
			PutStr (Name.fpart) ;
			PutStr (LOC(MSG_CLI_FILE_EXISTS)) ;
			if (CLIRequest()) return FALSE ;
		}
	}

	//	creating the Save file..
	if (NOT(flock = Open (Name.new,MODE_NEWFILE))) {
		mcPrfs & CLI ? PrintFault (IoErr(),"›32mPPMC›31m") : OpenError() ;
		return FALSE ;
	}

	//	writing the packed/unpacked file..
	if ((mcStat & PACK && NOT ppWriteDataHeader (flock,mcPara[EFF],(BOOL)(mcStat&CRYPT),mcCsum))
		|| (Write (flock,mcData,mcLen) != mcLen))
	{
		if (mcPrfs & CLI) {
			PutStr (_HEADLINE_) ;
			PutStr (LOC(MSG_CLI_ERROR_SAVE)) ;
			PutStr (Name.fpart) ;
		}
		else {
			RT_TITLE (MSG_TITLE_ERROR) ;
			rtEZRequestA (LOC(MSG_ERROR_SAVE),LOC(MSG_OK_ABORT),NULL,NULL,(struct TagItem *)rtTags) ;
		}
		Close (flock) ;
		return FALSE ;
	}
	Close (flock) ;

	if (mcPrfs & CLI) {
		PutStr (_HEADLINE_) ;
		Printf ("›1m%s›0m - %s (%ld %s)\n",
				LOC(mcStat & PACK ? MSG_CLI_PACKED_FILE
								  : MSG_CLI_UNPACKED_FILE ),
				Name.fpart,mcLen+(mcStat & PACK ? 8L : 0L),
				LOC(MSG_CLI_BYTES)) ;
	}
	else {
		SETTXT (LOC(MSG_STATUS_SAVED)) ;
		SETGAD (Gads[GDX_Status],Wnd,NULL,(struct TagItem *)gtxtTags) ;

		SETTXT (Name.fpart) ;
		SETGAD (Gads[GDX_FileName],Wnd,NULL,(struct TagItem *)gtxtTags) ;

		DISABLE(TRUE) ;
		SETGAD (Gads[GDX_Save],Wnd,NULL,(struct TagItem *)gdisTags) ;
		SETGAD (Gads[mcStat&PACK?GDX_Pack:GDX_UnPack],Wnd,NULL,(struct TagItem *)gdisTags) ;

		OffMenu (Wnd,MNUM(0,3)) ;
		OffMenu (Wnd,MNUM(0,4)) ;
		OffMenu (Wnd,MNUM(2,NOT(BOOL)(mcStat & PACK))) ;
	}

	SetProtection (Name.new,2) ;
//	SetComment (Name.new,"Packed by PPMC") ;
	return TRUE ;
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
VOID	GetReady (VOID)
{
	STRPTR	msg = LOC(mcStat & PACK	? MSG_STATUS_LOADING
									: MSG_STATUS_UNPACKING) ;
	FreeCrMem() ;

	if (NOT(mcPrfs & CLI)) {
		SETTXT (msg) ;
		SETGAD (Gads[GDX_Status],Wnd,NULL,(struct TagItem *)gtxtTags) ;

		DISABLE(TRUE) ;
		SETGAD (Gads[GDX_Save],Wnd,NULL,(struct TagItem *)gdisTags) ;

		SetPointer (Wnd,mcBusy,22L,15L,0L,0L) ;
		FillGauge (EMPTY) ;

		OffMenu (Wnd,MNUM(0,3)) ;
		OffMenu (Wnd,MNUM(0,4)) ;
	}
	else if (mcPrfs & VERB && NOT(mcStat & CRYPT && NOT(mcStat & PACK))) {
		PutStr (_HEADLINE_) ;
		PutStr (msg) ;
		Flush (Outfh) ;
	}
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
VOID	GetOff (VOID)
{
	LONG	size	= mcLen +(mcStat & PACK ? 8L : 0L),
			gained	= SDivMod32 (100L*(mcFsze-size),mcFsze) ;
	STRPTR	msg = LOC(mcStat & PACK	? (mcPrfs & CLI	? MSG_PER_PACKED
													: MSG_STATUS_PACKED)
									: MSG_STATUS_UNPACKED) ;
	if (mcPrfs & CLI) {
		STRPTR	format = mcStat & PACK	? "%s100%% %s , %02ld%% %s\n"
										: "%s%s (%02ld%% %s)\n" ;
		Printf (format,_HEADLINE_,msg,gained,LOC(MSG_PER_GAINED)) ;
	}
	else {
		DisplayBeep (Scr) ;

		SETTXT (msg) ;
		SETGAD (Gads[GDX_Status],Wnd,NULL,(struct TagItem *)gtxtTags) ;

		SETNUM (size) ;
		SETGAD (Gads[GDX_Size],Wnd,NULL,(struct TagItem *)gnumTags) ;

	//	SETNUM (gained) ;
	//	SETGAD (Gads[GDX_Gained],Wnd,NULL,(struct TagItem *)gnumTags) ;
		GT_SetGadgetAttrs (Gads[GDX_Gained],Wnd,NULL,GTNM_Number,gained,TAG_DONE) ;

		SETNUM (100L) ;
		SETGAD (Gads[GDX_Packed],Wnd,NULL,(struct TagItem *)gnumTags) ;

		DISABLE(FALSE) ;
		SETGAD (Gads[GDX_Save],Wnd,NULL,(struct TagItem *)gdisTags) ;

		OnMenu (Wnd,MNUM(0,3)) ;
		OnMenu (Wnd,MNUM(0,4)) ;
	}
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
VOID	DisableGads (BOOL on)
{
	UBYTE	x = GDX_CrunEff ;

	DISABLE (on) ;
	while (x <= GDX_About) SETGAD (Gads[x++],Wnd,NULL,(struct TagItem *)gdisTags) ;
	SETGAD (Gads[GDX_Encrypt],Wnd,NULL,(struct TagItem *)gdisTags) ;
	SETGAD (Gads[GDX_Pack],	  Wnd,NULL,(struct TagItem *)gdisTags) ;

	DISABLE (NOT on) ;
	SETGAD (Gads[GDX_Abort],Wnd,NULL,(struct TagItem *)gdisTags) ;
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
VOID	ClearGads (ULONG size)
{
	SETTXT (LOC(mcStat & PACK ? MSG_STATUS_START_PACK : MSG_STATUS_START_UNPACK)) ;
	SETGAD (Gads[GDX_Status],Wnd,NULL,(struct TagItem *)gtxtTags) ;

	SETNUM (size) ;
	SETGAD (Gads[GDX_Size],Wnd,NULL,(struct TagItem *)gnumTags) ;

	SETNUM (0L) ;
	SETGAD (Gads[GDX_Gained],Wnd,NULL,(struct TagItem *)gnumTags) ;
	SETGAD (Gads[GDX_Packed],Wnd,NULL,(struct TagItem *)gnumTags) ;
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
BOOL	LoadData (VOID)
{
	WORD	err = ppLoadData (Name.new,mcPara[DEC],NULL,&mcData,&mcSize,mcStat&PACK?FALSE:AskPass) ;

	mcStat |= DATA ;
	if (mcPrfs & CLI) PutStr ("\r›K") ;
	if (err) {
		STRPTR	errmsg = ppErrorMessage (err) ;

		if (mcPrfs & CLI) {
			PutStr (_HEADLINE_) ;
			PutStr (errmsg) ;	//	not localized!..
		}
		else {
			RT_TITLE (MSG_TITLE_ERROR) ;
			rtEZRequestA (errmsg,LOC(MSG_OK_ABORT),NULL,NULL,(struct TagItem *)rtTags) ;
			if (err != PP_PASSERR) {
				SETTXT (LOC(MSG_STATUS_ANOTHER)) ;
				SETGAD (Gads[GDX_Status],Wnd,NULL,(struct TagItem *)gtxtTags) ;

				gtxtTags[1] = NULL ;
				SETGAD (Gads[GDX_FileName],Wnd,NULL,(struct TagItem *)gtxtTags) ;

				SETNUM (0L) ;
				SETGAD (Gads[GDX_Size],Wnd,NULL,(struct TagItem *)gnumTags) ;
			}
		}
		mcStat ^= DATA ;
		return FALSE ;
	}
	return TRUE ;
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
BOOL	Crunch (VOID)	//	main crunch function..
{
	if (mcPass) {
		if (NOT GetPass()) return FALSE ;
		mcStat |= CRYPT ;
	}
	else mcStat ^= mcStat & CRYPT ;

	GetReady() ;

	//	allocating the Crunching Informations..
	mcInfo = ppAllocCrunchInfo (mcPara[EFF],mcPara[BUF],mcPrfs&VERB?Status:NULL,NULL) ;
	if (NOT mcInfo) {
		STRPTR	msg = LOC(MSG_ERROR_MEMORY) ;

		if (mcPrfs & CLI) {
			WriteChar ('\n') ;
			PutStr (_HEADLINE_) ;
			PutStr (msg) ;
		}
		else {
			RT_TITLE (MSG_TITLE_ERROR) ;
			rtEZRequestA (msg,LOC(MSG_OK_ABORT),NULL,NULL,(struct TagItem *)rtTags) ;
		}
		return FALSE ;
	}
	else mcStat |= INFO ;

	//	loading the file to Pack..
	if (NOT LoadData()) return FALSE ;

	if (NOT(mcPrfs & CLI)) {
		RT_TITLE (MSG_TITLE_WARNING) ;

		SETTXT (LOC(MSG_STATUS_PACKING)) ;
		SETGAD (Gads[GDX_Status],Wnd,NULL,(struct TagItem *)gtxtTags) ;

		DisableGads (TRUE) ;
		ClearMenuStrip (Wnd) ;
	}

	//	crunching..
	mcLen = ppCrunchBuffer (mcInfo,mcData,mcSize) ;

	if (mcPrfs & CLI) {
		switch (mcLen) {
			case PP_BUFFEROVERFLOW :	PutStr (_HEADLINE_) ;
										PutStr (LOC(MSG_ERROR_OVERFLOW)) ;
			case PP_CRUNCHABORTED :		return FALSE ;
		}
	}
	else {
		switch (mcLen) {
			case PP_BUFFEROVERFLOW :
				RT_TITLE (MSG_TITLE_ERROR) ;
				rtEZRequestA (LOC(MSG_ERROR_OVERFLOW),LOC(MSG_OK_ABORT),NULL,NULL,(struct TagItem *)rtTags) ;
			case PP_CRUNCHABORTED :
				FillGauge (EMPTY) ;
				ClearGads (mcSize) ;
				DisableGads (FALSE) ;
				ResetMenuStrip (Wnd,Menus) ;
				return FALSE ;

			default :
				FillGauge (FULL) ;
				DisableGads (FALSE) ;
				ResetMenuStrip (Wnd,Menus) ;
				break ;
		}
	}
	//	checking wether or not to crypt the file..
	if (mcPass) ppDecrypt (mcData,mcLen-4L,ppCalcPasskey(mcPwd)) ;
	GetOff() ;
	return TRUE ;
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
//	this is the callback function called during the crunching..
BOOL __stdargs __saveds	Status (ULONG sofar, ULONG crlen, ULONG totlen, APTR userdata)
{
	struct	IntuiMessage	*imsg ;

	if (sofar) {
		LONG	packed = (100L*sofar)/totlen,
				gained = 100L-(100L*crlen)/sofar ;

		if (mcPrfs & CLI) Printf ("%s %02ld%% %s , %02ld%% %s\r",_HEADLINE_,packed,LOC(MSG_PER_PACKED),gained,LOC(MSG_PER_GAINED)) ;
		else {
			SETNUM (crlen) ;
			SETGAD (Gads[GDX_Size],Wnd,NULL,(struct TagItem *)gnumTags) ;

			SETNUM (gained) ;
			SETGAD (Gads[GDX_Gained],Wnd,NULL,(struct TagItem *)gnumTags) ;

			SETNUM (packed) ;
			SETGAD (Gads[GDX_Packed],Wnd,NULL,(struct TagItem *)gnumTags) ;

			RectFill (RPort,29,WBTop+73,29+213*sofar/totlen,WBTop+84) ;
		}
	}
	if (mcPrfs & CLI) chkabort() ;
	else if (imsg = GT_GetIMsg (Wnd->UserPort)) {
		ULONG	class = imsg->Class ;

		GT_ReplyIMsg (imsg) ;

		if (class == IDCMP_VANILLAKEY && imsg->Code == 0x1b
		 || class == IDCMP_GADGETUP && ((struct Gadget *)imsg->IAddress)->GadgetID == GDX_Abort)
			return (BOOL)(NOT rtEZRequestA (LOC(MSG_CRUNCH_ABORTED),LOC(MSG_OK_RESUME),NULL,NULL,(struct TagItem *)rtTags)) ;
		else if (class == IDCMP_REFRESHWINDOW) Refresh() ;
	}
	return TRUE ;
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
BOOL	Decrunch (VOID)		//	decrunch function..
{
	GetReady() ;

	if (NOT LoadData()) return FALSE ;
	if (NOT(mcPrfs & CLI)) FillGauge (FULL) ;

	mcLen = mcSize ;
	GetOff() ;

	return TRUE ;
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
BOOL	EnterPass (STRPTR pass)	//	custom password prompting function..
{
	BYTE	l, n = 0 ;

	pass[0] = '\0' ;
	do {
		Flush (Outfh) ;
		switch (l = n<16 ? getch() : '\r') {
			case '\r' :		if (n) {
								pass[n] = '\0' ;
								break ;
							}
			case 0x1b :		return FALSE ;

			case '\b' :		if (n) PutStr("\b \b") ;
							n -= n?2:1 ;
							break ;

			default :		pass[n] = l ;
							WriteChar ('·') ;
							break ;
		}
	} while (pass[n++]) ;

	PutStr ("\r›K") ;
	return TRUE ;
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
BOOL	GetPass (VOID)
{
	STRPTR	msg ;
	UBYTE	pass[2][17] ;
	BOOL	again = TRUE ;

	FOREVER {
		msg = LOC(again ? MSG_ENTER_PASSWORD : MSG_REENTER_PASSWORD) ;

		if (mcPrfs & CLI) {
			PutStr (_HEADLINE_) ;
			PutStr (msg) ;
			PutStr (": ") ;
			if (NOT EnterPass (pass[again])) return FALSE ;
		}
		else {
			pass[again][0] = '\0' ;
			rtTags[11] = (ULONG)msg ;
			if (NOT rtGetStringA (pass[again],16L,again?_PPMC_:LOC(MSG_VERIFICATION),NULL,(struct TagItem *)rtTags))
				return FALSE ;
		}

		if (NOT again) {
			if (NOT strcmp (pass[0],pass[1])) {
				strcpy (mcPwd,pass[0]) ;
				mcCsum = ppCalcChecksum (mcPwd) ;
				return TRUE ;
			}
			else if (NOT(mcPrfs & CLI)) DisplayBeep (Scr) ;
		}
		again = NOT again ;
	}
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
BOOL __stdargs __saveds	AskPass (UBYTE *mcPwd, ULONG checksum)
{
	STRPTR	msg = LOC(MSG_ENTER_PASSWORD) ;
	UBYTE	pass[17], try ;

	for (try = 1 ; try < 4 ; try++) {
		if (mcPrfs & CLI) {
			Printf ("›32m[%ld]PPMC:›31m %s: ",try,msg) ;
			if (NOT EnterPass (pass)) return FALSE ;
		}
		else {
			pass[0] = '\0' ;
			rtTags[11] = (ULONG)msg ;
			if (NOT rtGetStringA (pass,16L,_PPMC_,NULL,(struct TagItem *)rtTags))
				break ;
		}

		if (ppCalcChecksum(pass) == checksum) {
			strcpy (mcPwd,pass) ;
			if (mcPrfs & CLI) {
				PutStr (_HEADLINE_) ;
				PutStr (LOC(MSG_STATUS_UNPACKING)) ;
				Flush (Outfh) ;
			}
			return TRUE ;
		}
	}
	return FALSE ;
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
VOID	Refresh (VOID)
{
	GT_BeginRefresh (Wnd) ;
	Render() ;
	if (mcPrfs & PATTERN) {
		RefreshGList (GList,Wnd,NULL,-1) ;
		GT_RefreshWindow (Wnd,NULL) ;
	}
	GT_EndRefresh (Wnd,TRUE) ;
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
VOID	FillGauge (BOOL fill)
{
	if (NOT fill) SetAPen (RPort,0) ;
	RectFill (RPort,29,WBTop+73,241,WBTop+84) ;
	if (fill) mcStat |= GAUGE ;
	else {
		mcStat ^= mcStat & GAUGE ;
		SetAPen (RPort,mcHpen) ;
	}
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
VOID	FreeCrMem (VOID)
{
	if (mcStat & DATA) {
		FreeMem (mcData,mcSize) ;
		mcStat ^= mcStat & DATA ;
	}
	if (mcStat & INFO)	{
		ppFreeCrunchInfo (mcInfo) ;
		mcStat ^= mcStat & INFO ;
	}
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
VOID	OpenError (VOID)
{
	STRPTR	rtargs[3] ;
	UBYTE	errmsg[81] ;

	Fault (IoErr(),NULL,errmsg,80) ;

	RT_TITLE (MSG_TITLE_ERROR) ;

	rtargs[0] = LOC(MSG_ERROR_OPEN) ;
	rtargs[1] = Name.fpart ;
	rtargs[2] = errmsg ;

	rtEZRequestA ("%s\n\"%s\"\n%s",LOC(MSG_OK_ABORT),NULL,rtargs,(struct TagItem *)rtTags) ;
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
VOID	MainLoop (VOID)		//	intuition interface loop..
{
	struct	IntuiMessage	*imsg ;
	struct	AppMessage		*amsg ;
	struct	MsgPort			*wport = Wnd->UserPort ;
	ULONG	appwinsig = 1L << MsgPort->mp_SigBit,
			winsig	  = 1L << wport->mp_SigBit,
			globalsig = ((AppWin?appwinsig:0)|winsig) ;
	UWORD	code ;

	FOREVER if (Wait (globalsig)) {
		if (MsgPort && appwinsig) {
			while (amsg = (struct AppMessage *)GetMsg (MsgPort)) {
				NameFromLock (amsg->am_ArgList->wa_Lock,Name.old,256) ;
				AddPart (Name.old,amsg->am_ArgList->wa_Name,256L) ;
				ReplyMsg ((struct Message *)amsg) ;
				ActivateWindow (Wnd) ;
				Load (FALSE) ;
			}
		}
		if (winsig) {
			while (imsg = GT_GetIMsg (wport)) {
				GT_ReplyIMsg (imsg) ;
				code = imsg->Code ;
				switch (imsg->Class) {
					case IDCMP_REFRESHWINDOW :
							Refresh() ;
							if (mcStat & GAUGE) FillGauge (FULL) ;
							break ;

					case IDCMP_GADGETUP :
							HandleGadgets (((struct Gadget *)imsg->IAddress)->GadgetID,(UBYTE)code) ;
							break ;

					case IDCMP_MENUPICK :
							if (NOT HandleMenus (code)) return ;
							break ;

					case IDCMP_CLOSEWINDOW :
							return ;

					case IDCMP_VANILLAKEY :
							HandleKeys (code) ;
							break ;
				}
			}
		}
	}
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
VOID	HandleGadgets (UWORD gadID, UBYTE code)
{
	switch (gadID) {
		case GDX_About	:	About() ;
							break ;
		case GDX_Load	:	Load (TRUE) ;
							break ;
		case GDX_Save	:	if (NOT Save(TRUE)) strcpy (Name.new,Name.old) ;
							break ;

		case GDX_Encrypt :	mcPass = NOT mcPass ;
							ClearMenuStrip (Wnd) ;
							ItemAddress (Menus,MNUM(1,3))->Flags ^= CHECKED ;
							ResetMenuStrip (Wnd,Menus) ;
							break ;
		case GDX_CrunEff  :
		case GDX_SUBuffer :
		case GDX_DecrEff  :
						{
							UBYTE	mx = gadID - GDX_CrunEff ;

							ClearMenuStrip (Wnd) ;
							ItemAddress (Menus,FULLMENUNUM(1,mx,mcPara[mx]))->Flags ^= CHECKED ;
							mcPara[mx] = code ;
							ItemAddress (Menus,FULLMENUNUM(1,mx,mcPara[mx]))->Flags ^= CHECKED ;
							ResetMenuStrip (Wnd,Menus) ;
						}
							break ;

		case GDX_Pack	:	if (NOT Crunch()) mcStat ^= mcStat & CRYPT ;
							ClearPointer (Wnd) ;
							break ;
		case GDX_UnPack	:	Decrunch() ;
							ClearPointer (Wnd) ;
							break ;
	}
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
BOOL	HandleMenus (UWORD code)
{
	UBYTE	item ;

	while (code != MENUNULL) {
		item = ITEMNUM (code) ;
		switch (MENUNUM (code)) {
			case 0 :	//	`Project' menu
				switch (item) {
					case 0 :	About() ;					//	About...
								break ;

					case 2 :	Load(TRUE) ;	//	Load...
								break ;

					case 3 :	//	Save
					case 4 :	//	Save As...
								if (NOT Save ((BOOL)(ITEMNUM(code)-3)))
									strcpy (Name.new,Name.old) ;
								break ;

					case 6 :	return FALSE ;	//	Quit
				}
				break ;

			case 1 :	//	`Parameters' menu
				switch (item) {
					case 0 :	//	Efficiency
					case 1 :	//	Speedup Buffer
					case 2 :	//	Decrunching Effect
								SETCYC (mcPara[item] = SUBNUM (code)) ;
								SETGAD (Gads[GDX_CrunEff+item],Wnd,NULL,(struct TagItem *)gcycTags) ;
								break ;

					case 3 :	//	Encrypt File
								SETCBX (mcPass = NOT mcPass) ;
								SETGAD (Gads[GDX_Encrypt],Wnd,NULL,(struct TagItem *)gcbxTags) ;
								break ;
				}
				break ;

			case 2 :	//	`Crunching' menu
						if (item) Decrunch() ;
						else if (NOT Crunch()) mcStat ^= mcStat & CRYPT ;
						ClearPointer (Wnd) ;
						break ;
		}
		code = (ItemAddress (Menus,code))->NextSelect ;
	}
	return TRUE ;
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
VOID	HandleKeys (UWORD code)
{
	if (code == Key.encrypt || code == Key.encrypt+32) {
		SETCBX (mcPass = NOT mcPass) ;
		SETGAD (Gads[GDX_Encrypt],Wnd,NULL,(struct TagItem *)gcbxTags) ;

		ClearMenuStrip (Wnd) ;
		(ItemAddress (Menus,MNUM(1,3)))->Flags ^= CHECKED ;
		ResetMenuStrip (Wnd,Menus) ;
	}
	else if ((code == Key.pack || code == Key.pack+32)
			&& ((ItemAddress (Menus,MNUM(2,0)))->Flags & ITEMENABLED))
	{
		if (NOT Crunch()) mcStat ^= mcStat & CRYPT ;
		ClearPointer (Wnd) ;
	}
	else if ((code == Key.unpack || code == Key.unpack+32)
			&& ((ItemAddress (Menus,MNUM(2,1)))->Flags & ITEMENABLED))
	{
		Decrunch() ;
		ClearPointer (Wnd) ;
	}
	else if ((code == Key.save || code == Key.save+32)
			&& ((ItemAddress (Menus,MNUM(0,3)))->Flags & ITEMENABLED)
			&& NOT Save(TRUE)) strcpy (Name.new,Name.old) ;
}

//	Tab size 4