PackIt pitpit.c

/*			PackIt Routines

	This file contains most of the routines specific to the PackIt application. The files pitmain.c and
pitmenu.c contain more generic code.

	© Copyright 1986 by Harry R. Chesley.
	All rights reserved.
*/

#include <quickdraw.h>
#include <font.h>
#include <event.h>
#include <memory.h>
#include <resource.h>
#include <window.h>
#include <control.h>
#include <dialog.h>
#include <pb.h>
#include <packages.h>
#include <print.h>
#include <toolutil.h>
#include <osutil.h>
#include <syserr.h>

/* Additional stuff not defined above. */
#define NIL 0L
struct AppFile {			/* Finder file info. */
	short vRefNum;
	OSType fType;
	short versNum;
	char fName[100];
};
typedef struct AppFile AppFile;
#define fdFlMask 0x6000		/* Mask of setable Finder flags. */

#define PITFT 'PIT '		/* PackIt file type. */
#define PITCR 'PIT '		/* PackIt creator. */

#define MAXHIST 30			/* Max number of packed files to remember. */

/* Alert IDs. */
#define OPENFALERT 256		/* "Open failed..." */
/*#define ABOUTALERT 257*/	/* "About PackIt..." */
#define LONGPALERT 258		/* "Password too long..." */
#define DISKFALERT 259		/* "Disk full..." */
#define WRITFALERT 260		/* "Write failed..." */
#define READFALERT 261		/* "Read failed..." */
#define INVPALERT 262		/* "Invalid packed file..." */
#define CRCFALERT 263		/* "Bad CRC..." */
#define HCRCFALERT 264		/* "Bad header CRC..." */
#define DELFALERT 265		/* "Really delete file???" */
#define OOMALERT 266		/* "Out of memory!" */
#define H1ALERT 268			/* Help screen 1. */
#define H2ALERT 269			/* Help screen 2. */
#define UPDALERT 270		/* "Unpacking done..." */
#define PPFALERT 271		/* Packing a packed file alert. */
#define FCALERT 272			/* File continuation query. */
#define OFCALERT 273		/* Output file continuation query. */

/* Dialog IDs. */
#define PQDIA 256			/* Packing file query. */
#define UPFILEDIA 257		/* "Unpacking file..." */
#define SETPDIA 258			/* "Set password..." */
#define PSFGDIA 259			/* Pack SFGetFile dialog. */
#define USFGDIA 260			/* Unpack SFGetFile dialog. */
#define ABOUTDIA 261		/* "About..." dialog. */
#define STDDIA 262			/* Status display dialog. */
#define USFPDIA 263			/* Unpack SFPutFile dialog. */

#define COMPITEM 11			/* Item number for Compression checkbox in PSFGDIA. */
#define NOENITEM 12			/* Item # for no encrypt in PSFGDIA. */
#define SENITEM 13			/* Item # for simple encrupt in PSFGDIA. */
#define DENITEM 14			/* Item # for DES encrypt in PSFGDIA. */

#define PTXTITEM 11			/* Item number for PackIt/TEXT checkbox in USFGDIA. */

#define SKIPITEM 9			/* Item number for Skip button in USFPDIA. */
#define SVALLITEM 10		/* Item # for Save All button in USFPDIA. */

/* Window IDs. */
#define FLWIND 256			/* File list window ID. */

/* String IDs. */
#define PWINDSTR 256		/* Packed window string. */
#define UWINDSTR 257		/* Unpacked window string. */

/* File refs: */
int packedFRef;				/* Output file reference. */
int packedVRef;				/* Output volume reference. */

struct packFHead {			/* Packed file info header format. */
	long magic;				/* Magic number (to be sure we don't unpack gibberish). */
	char fName[64];			/* Suggested file name. */
	OSType fType;			/* File type. */
	OSType fCreator;		/* File creator. */
	short fFlags;			/* File (finder) flags. */
	int fLocked;			/* File locked. */
	long DSize;				/* Data fork size (bytes). */
	long RSize;				/* Resource fork size (bytes). */
	long fCrDate;			/* File creation date. */
	long fModDate;			/* File modification date. */
	unsigned hCrc;			/* Header CRC. */
};

struct packFHead hist[MAXHIST];	/* Memory of previously packed files. */

int histIndex;					/* Number of remembered files. */

WindowRecord FLWind;			/* The file list window. */

DialogRecord AbtDia;			/* About dialog. */

DialogRecord StDDia;			/* Status display dialog. */
int StDStr;						/* Which status string to display. */
char StDSize[4][256];			/* Status display sizes. */
long StDITot;					/* Total input size. */
long StDISc;					/* Current scan size. */
long StDIPk;					/* Current pack size. */
long StDOTot;					/* Total output size. */
long StDOCur;					/* Current output size. */

/* Indexes into the status string list: */
#define SCDATA 1	/* Scanning data. */
#define SCRSRC 2	/* Scanning resource. */
#define CODATA 3	/* Compressing data. */
#define CORSRC 4	/* Compressing resource. */
#define PKDATA 5	/* Packing data. */
#define PKRSRC 6	/* Packing resource. */
#define UPDATA 7	/* Unpacking data. */
#define UPRSRC 8	/* Unpacking resource. */
#define SKFILE 9	/* Skipping file. */

char *ioBuf;		/* Run-time allocated IO buffer. */
char *compBuf;		/* Run-time allocated compression buffer. */
long bufSize;		/* Size of the buffers. */

int textFl;			/* Use TEXT filter flag. */

char password[9];	/* Password (Pascal string format). */

int compFile;		/* TRUE if we're to compress the file. */

int cryptFile;		/* Type of encryption. */

#define NOCRYPT 0
#define SIMPCRYPT 1
#define DESCRYPT 2

int fndrFNum;		/* Number of Finder-specified files to process. */
int fndrFInd;		/* Index of Finder-specified file next to be processed. */

RgnHandle stDRgn;	/* Status display picture region. */
PicHandle stDPic;	/* Status display background picture. */

char buf2k[2048];	/* 2K byte buffer. */
char *bPtr;			/* Pointer into regular buffer for copy to 8 byter. */
long bCnt;			/* Count of bytes left in regular buffer. */
int upCnt;			/* Count for updating status display. */
int realEOF;		/* TRUE if really at end-of-file. */

int compErr;		/* IO routines return value. */

int itemType;		/* For use with Get/SetDItem(). */
Handle itemHand;
Rect itemBox;

#define min(a,b) ((a < b) ? a : b)

long ByteSize();
long ByteDeSize();

/*	initAppl()

	Function: Initialize application.

	Algorithm: Just set things to their default values.

	Comments: None.
*/

initAppl()

{
	register long max;
	long maxGrow;
	register RgnHandle partRgn;
	pascal void showPic(), showProg(), showSize(), showInFile(), showOutFile();

	fndrFNum = fndrFInd = 0;
	textFl = TRUE;
	compFile = FALSE;
	cryptFile = NOCRYPT;
	password[0] = 0;
	GetNewWindow(FLWIND,&FLWind,-1L);
	GetNewDialog(ABOUTDIA,&AbtDia,-1L);

	/* Set up status dialog. */
	GetNewDialog(STDDIA,&StDDia,-1L);
	stDRgn = NewRgn();
	GetDItem(&StDDia,1,&itemType,&stDPic,&itemBox);
	RectRgn(stDRgn,&itemBox);
	SetDItem(&StDDia,1,userItem,showPic,&itemBox);
	partRgn = NewRgn();
	GetDItem(&StDDia,2,&itemType,&itemHand,&itemBox);
	SetDItem(&StDDia,2,itemType,showProg,&itemBox);
	RectRgn(partRgn,&itemBox); DiffRgn(stDRgn,partRgn,stDRgn);
	GetDItem(&StDDia,3,&itemType,&itemHand,&itemBox);
	SetDItem(&StDDia,3,itemType,showSize,&itemBox);
	RectRgn(partRgn,&itemBox); DiffRgn(stDRgn,partRgn,stDRgn);
	GetDItem(&StDDia,4,&itemType,&itemHand,&itemBox);
	SetDItem(&StDDia,4,itemType,showSize,&itemBox);
	RectRgn(partRgn,&itemBox); DiffRgn(stDRgn,partRgn,stDRgn);
	GetDItem(&StDDia,5,&itemType,&itemHand,&itemBox);
	SetDItem(&StDDia,5,itemType,showSize,&itemBox);
	RectRgn(partRgn,&itemBox); DiffRgn(stDRgn,partRgn,stDRgn);
	GetDItem(&StDDia,6,&itemType,&itemHand,&itemBox);
	SetDItem(&StDDia,6,itemType,showSize,&itemBox);
	RectRgn(partRgn,&itemBox); DiffRgn(stDRgn,partRgn,stDRgn);
	GetDItem(&StDDia,7,&itemType,&itemHand,&itemBox);
	SetDItem(&StDDia,7,itemType,showInFile,&itemBox);
	RectRgn(partRgn,&itemBox); DiffRgn(stDRgn,partRgn,stDRgn);
	GetDItem(&StDDia,8,&itemType,&itemHand,&itemBox);
	SetDItem(&StDDia,8,itemType,showOutFile,&itemBox);
	RectRgn(partRgn,&itemBox); DiffRgn(stDRgn,partRgn,stDRgn);
	DisposeRgn(partRgn);

	/* Allocate the IO and compression buffers. */
	max = MaxMem(&maxGrow);
	max = ((max-30000L)/2L) & ~0x3FFL;
	if (max > 30720L) max = 30720L;
	bufSize = max;
	if (max < 2048L) {
		StopAlert(OOMALERT,NIL);
		exit();
	};
	if ((ioBuf = NewPtr(max)) == NIL) exit();
	if ((compBuf = NewPtr(max)) == NIL) exit();
}

/*	Status Dialog Update Routines

	Function: These routines update the status display depending on the current status.

	Algorithm: Just draw the right stuff.

	Comments: None.
*/

upStDDia()

{
	BeginUpdate(&StDDia);
	DrawDialog(&StDDia);
	EndUpdate(&StDDia);
}

invalStStr()

{
	SetPort(&StDDia);
	GetDItem(&StDDia,2,&itemType,&itemHand,&itemBox);
	InvalRect(&itemBox);
}

invalSizes()

{
	SetPort(&StDDia);
	GetDItem(&StDDia,3,&itemType,&itemHand,&itemBox);
	InvalRect(&itemBox);
	GetDItem(&StDDia,4,&itemType,&itemHand,&itemBox);
	InvalRect(&itemBox);
	GetDItem(&StDDia,5,&itemType,&itemHand,&itemBox);
	InvalRect(&itemBox);
	GetDItem(&StDDia,6,&itemType,&itemHand,&itemBox);
	InvalRect(&itemBox);
}

invalIOFiles()

{
	SetPort(&StDDia);
	GetDItem(&StDDia,7,&itemType,&itemHand,&itemBox);
	InvalRect(&itemBox);
	GetDItem(&StDDia,8,&itemType,&itemHand,&itemBox);
	InvalRect(&itemBox);
}

pascal void showPic(theWind,theItem)

WindowPtr theWind;
int theItem;

{
	GetDItem(theWind,1,&itemType,&itemHand,&itemBox);
	SetClip(stDRgn);
	DrawPicture(stDPic,&itemBox);
	ClipRect(&thePort->portRect);
}

pascal void showProg(theWind,theItem)

WindowPtr theWind;
int theItem;

{
	char str[256];

	GetIndString(str,256,StDStr);
	GetDItem(theWind,2,&itemType,&itemHand,&itemBox);
	TextBox(str+1,(long) *str,&itemBox,teJustCenter);
}

pascal void showSize(theWind,theItem)

WindowPtr theWind;
int theItem;

{
	GetDItem(theWind,theItem,&itemType,&itemHand,&itemBox);
	TextBox((StDSize[theItem-3])+1,(long) StDSize[theItem-3][0],&itemBox,teJustCenter);
}

pascal void showInFile(theWind,theItem)

WindowPtr theWind;
int theItem;

{
	Rect rPack, rComp;
	register int topPack;

	if (StDITot == 0L) return;
	GetDItem(theWind,theItem,&itemType,&itemHand,&itemBox);
	rPack = itemBox;
	rPack.top = rPack.bottom - (((long) rPack.bottom - rPack.top)*StDIPk/StDITot);
	if (rPack.top < itemBox.top) rPack.top = itemBox.top;
	if (rPack.top < rPack.bottom) FillRect(&rPack,&black);
	if (compFile) {
		rComp = itemBox;
		rComp.top = rComp.bottom - (((long) rComp.bottom - rComp.top)*StDISc/StDITot);
		if (rComp.top < itemBox.top) rComp.top = itemBox.top;
		if (rComp.bottom > rPack.top) rComp.bottom = rPack.top;
		if (rComp.top < rComp.bottom) FillRect(&rComp,&dkGray);
	};
}

pascal void showOutFile(theWind,theItem)

WindowPtr theWind;
int theItem;

{
	Rect rPack, rComp;

	if (StDITot == 0L) return;
	GetDItem(theWind,theItem,&itemType,&itemHand,&itemBox);
	rPack = itemBox;
	rPack.top = rPack.bottom - (((long) rPack.bottom - rPack.top)*StDOCur/StDITot);
	if (rPack.top < itemBox.top) rPack.top = itemBox.top;
	if (rPack.top < rPack.bottom) FillRect(&rPack,&black);
	if (compFile) {
		rComp = itemBox;
		rComp.top = rComp.bottom - (((long) rComp.bottom - rComp.top)*StDOTot/StDITot);
		if (rComp.top < itemBox.top) rComp.top = itemBox.top;
		if (rComp.bottom > rPack.top) rComp.bottom = rPack.top;
		if (rComp.top < rComp.bottom) FillRect(&rComp,&ltGray);
	};
}

/*	displayAbout()

	Function: Display a box telling what we're all about.

	Algorithm: Very simple.

	Comments: None.
*/

displayAbout()

{
	EventRecord evtRec;
	int itemType;
	Handle itemHand;
	Rect itemBox;
	Rect clipBox;
	Point pPt;
	register int mode;

	BringToFront(&AbtDia);
	ShowWindow(&AbtDia);
	SetPort(&AbtDia);
	ClipRect(&thePort->portRect);
	TextFont(0);
	TextSize(12);
	TextFace(0);
	TextMode(srcOr);
	DrawDialog(&AbtDia);
	GetDItem(&AbtDia,1,&itemType,&itemHand,&itemBox);
	TextFont(geneva);
	TextSize(24);
	TextMode(mode = srcBic);
	TextFace(boldStyle|underlineStyle);
	pPt.h = itemBox.right - (itemBox.right-itemBox.left)/2 - StringWidth("\PPackIt III")/2;
	pPt.v = itemBox.top;
	clipBox = thePort->portRect;

	do {
		if (mode == srcBic) {
			if (clipBox.top <= thePort->portRect.top) {
				clipBox.right = clipBox.left = itemBox.left + (itemBox.right-itemBox.left)/2;
				TextMode(mode = srcOr);
			} else clipBox.top--;
		} else {
			if (clipBox.left < thePort->portRect.left) {
				clipBox.top = clipBox.bottom = thePort->portRect.bottom;
				TextMode(mode = srcBic);
			} else {
				clipBox.left--;
				clipBox.right++;
			};
		};
		ClipRect(&clipBox);
		MoveTo(pPt.h,pPt.v);
		DrawString("\PPackIt III");
	} while (! EventAvail(mouseDown|keyDown,&evtRec));

	HideWindow(&AbtDia);
}

/*	displayHelp()

	Function: Display the help screens.

	Algorithm: Just call Alert() twice.

	Comments: None.
*/

displayHelp()

{
	Alert(H1ALERT,NIL);
	Alert(H2ALERT,NIL);
}

/*	setPassword()

	Function: Set the password for encruption/decryption.

	Algorithm: Just put up a dialog to get the password.

	Comments: None.
*/

setPassword()

{
	char str[256];
	register char *cptr;
	register int i;
	DialogPtr theDialog;
	int itemType;
	Handle itemHand;
	Rect itemBox;
	int hit;

	if ((theDialog = GetNewDialog(SETPDIA,NIL,-1L)) == NIL) return;
	GetDItem(theDialog,4,&itemType,&itemHand,&itemBox);
	SetIText(itemHand,password);
	SelIText(theDialog,4,0,30000);

	do {
		do ModalDialog(NIL,&hit); while ((hit != 1) && (hit != 2));
		if (hit == 2) break;
		GetDItem(theDialog,4,&itemType,&itemHand,&itemBox);
		GetIText(itemHand,str);
		if (*str > 8) StopAlert(LONGPALERT,NIL);
	} while (*str > 8);

	if (hit == 1) {
		ptoc(str);
		for (cptr = str, i = 1; cptr < &str[9]; cptr++)
			if (i == 0) *cptr = 0;
			else i = *cptr;
		ctop(str);
		BlockMove(str,password,9L);
	};

	DisposDialog(theDialog);
}

/*	packFile()

	Function: Query for and pack together a series of files.

	Algorithm: Find the output file name by querying the user with SFPutFile(). Then cycle thru all
	of the files he wants to pack into it, calling packItIn() for each one. Then close the output file.

	Comment: None.
*/

pascal short packDlg(item,theDialog)

int item;
DialogPtr theDialog;

{
	int itemType;
	Handle itemHand;
	Rect itemBox;

	switch (item) {
		case -1:
			break;
		case COMPITEM:
			if (cryptFile == NOCRYPT) compFile = !compFile;
			break;
		case NOENITEM:
			cryptFile = NOCRYPT;
			break;
		case SENITEM:
			if (*password == 0) setPassword();
			cryptFile = SIMPCRYPT;
			compFile = TRUE;
			break;
		case DENITEM:
			if (*password == 0) setPassword();
			cryptFile = DESCRYPT;
			compFile = TRUE;
			break;
		default:
			return(item);
	};

	setPackCtls(theDialog);
	return(item);
}

setPackCtls(theDialog)

DialogPtr theDialog;

{
	int itemType;
	Handle itemHand;
	Rect itemBox;

	GetDItem(theDialog,COMPITEM,&itemType,&itemHand,&itemBox);
	SetCtlValue(itemHand,compFile ? 1 : 0);
	GetDItem(theDialog,NOENITEM,&itemType,&itemHand,&itemBox);
	SetCtlValue(itemHand,(cryptFile == NOCRYPT) ? 1 : 0);
	GetDItem(theDialog,SENITEM,&itemType,&itemHand,&itemBox);
	SetCtlValue(itemHand,(cryptFile == SIMPCRYPT) ? 1 : 0);
	GetDItem(theDialog,DENITEM,&itemType,&itemHand,&itemBox);
	SetCtlValue(itemHand,(cryptFile == DESCRYPT) ? 1 : 0);
}

packFile()

{
	SFReply packFileReply;			/* Packed file info (from SFPutFile()). */
	SFReply inFileReply;			/* Input file info (from SFGetFile()). */
	Point dialogPt;					/* Where to put the get/put file name dialog. */
	register int retVal;			/* Function return values. */
	register int packItRet;			/* packItIn() return value. */
	long magicEnd, magicSize;		/* EOF magic number/size. */
	AppFile fileRef;				/* Finder specified file. */
	int volRef;
	long l;

	/* Find the output file name. */
	dialogPt.v = 40; dialogPt.h = 104;
	SFPutFile(pass(dialogPt),"\PPacked file name:","\PPackIt.pit",NIL,&packFileReply);
	if (!packFileReply.good) return;

	/* Create & open the output (packed) file. */
	retVal = Create(packFileReply.fName,packFileReply.vRefNum,PITCR,PITFT);
	if (retVal == dupFNErr) {
		FSDelete(packFileReply.fName,packFileReply.vRefNum);
		retVal = Create(packFileReply.fName,packFileReply.vRefNum,PITCR,PITFT);
	};
	if (retVal != noErr) {
		StopAlert(OPENFALERT,NIL);
		return;
	};
	if (FSOpen(packFileReply.fName,packFileReply.vRefNum,&packedFRef) != noErr) {
		StopAlert(OPENFALERT,NIL);
		return;
	};
	packedVRef = packFileReply.vRefNum;
	SetFPos(packedFRef,fsFromStart,NIL);

	/* Bring up the status window. */
	histIndex = 0;
	ShowWindow(&FLWind);

	/* Pack up all the files. */
	while (TRUE) {
		/* Get the next file name. */
		updateFL(PWINDSTR,FALSE);
		if (fndrFNum == 0) {
			dialogPt.v = 40; dialogPt.h = 10;
			SFPGetFile(pass(dialogPt),"",NIL,-1,NIL,packDlg,&inFileReply,PSFGDIA,NIL);
			/* Did he say "OK"? */
			if (!inFileReply.good) {
				packItRet = 0;
				break;
			};
			/* Is this a packed file? */
			if (inFileReply.fType == PITFT) {
				ParamText(inFileReply.fName,"","","");
				if (CautionAlert(PPFALERT,NIL) == 1) continue;
			};
			/* Pack it in, close the status box, and check return value for error indications. */
			SetCursor(*GetCursor(watchCursor));
			ShowWindow(&StDDia);
			if ((packItRet = packItIn(inFileReply.fName,inFileReply.vRefNum)) != 0) break;
			Delay(60L,&l); HideWindow(&StDDia);
			SetCursor(&arrow);
		} else {
			if (fndrFInd > fndrFNum) {
				packItRet = 0;
				break;
			};
			/* Pack it in, close the status box, and check return value for error indications. */
			SetCursor(*GetCursor(watchCursor));
			ShowWindow(&StDDia);
			GetAppFiles(fndrFInd++,&fileRef);
			if ((packItRet = packItIn(fileRef.fName,fileRef.vRefNum)) != 0) break;
			Delay(60L,&l); HideWindow(&StDDia);
			SetCursor(&arrow);
		};
	};

	SetCursor(&arrow);

	/* Redraw the status window for appearance. */
	updateFL(PWINDSTR,FALSE);

	/* Now write the EOF magic number. */
	if (packItRet == 0) {
		magicEnd = 'PEnd'; magicSize = sizeof(magicEnd);
		if ((retVal = FSWrite(packedFRef,&magicSize,&magicEnd)) != noErr)
			packItRet = (retVal == dskFulErr) ? DISKFALERT : WRITFALERT;
	};

	/* Close the status window. */
	HideWindow(&FLWind);

closendel:
	/* Close the output file. */
	FSClose(packedFRef);

	/* If there were problems during the packing, delete the file and notify the user. */
	if (packItRet != 0) {
		FSDelete(packFileReply.fName,packFileReply.vRefNum);
		StopAlert(packItRet,NIL);
	};

	/* Make sure the volume is all up to date. */
	if (packedVRef != 0) FlushVol(NIL,packedVRef);
}

/*	packItIn(fName,vRef)

	Function: Pack the file specified by fName/vRef onto the end of the file open via packedFRef.

	Algorithm: Call PBGetFInfo() to get the file type, etc., then open the data fork using PBOpen(),
	copy it, then open the resource fork with PBOpenRF() and copy it.

	Comments: None.
*/

packItIn(fName,vRef)

char *fName;
short vRef;

{
	struct packFHead header;	/* The file name, creator, type, etc. */
	ParamBlkRec ioRec;			/* Record for communicating with low level IO routines. */
	register long size;			/* Size of data/resource fork. */
	long outSize;				/* Size of output buffer write. */
	unsigned myCrc;				/* Calculated CRC. */
	register int retVal;		/* Return values. */
	long preSz,tabSz,dataSz;

	/* Get & write the file header info. */
	ioRec.ioNamePtr = fName; ioRec.ioVRefNum = vRef;
	ioRec.u.fp.ioFVersNum = ioRec.u.fp.ioFDirIndex = 0;
	if (PBGetFInfo(&ioRec,FALSE) != noErr) return(OPENFALERT);
	header.magic = 'PMag';									/* Magic number. */
	if (compFile || (cryptFile != NOCRYPT))
		*(((char *) &header.magic)+3) = '0'+(cryptFile & 3)+(compFile? 4 : 0);
	BlockMove(fName,header.fName,64L);						/* File name. */
	header.fType = ioRec.u.fp.ioFlFndrInfo.fdType;			/* File type. */
	header.fCreator = ioRec.u.fp.ioFlFndrInfo.fdCreator;	/* File creator. */
	header.fFlags = ioRec.u.fp.ioFlFndrInfo.fdFlags;		/* File (finder) flags. */
	header.fLocked = (ioRec.u.fp.ioFlAttrib && 1);			/* File locked. */
	header.DSize = ioRec.u.fp.ioFlLgLen;					/* Data fork size. */
	header.RSize = ioRec.u.fp.ioFlRLgLen;					/* Resource fork size. */
	header.fCrDate = ioRec.u.fp.ioFlCrDat;					/* Creation date. */
	header.fModDate = ioRec.u.fp.ioFlMdDat;					/* Modification date. */
	header.hCrc = crc(0,&(header.fName),
				(long) sizeof(header)-sizeof(header.magic)-sizeof(header.hCrc));
	/* Let him know what we're packing. */
	if (histIndex >= MAXHIST) {
		BlockMove(&hist[1],hist,(long) sizeof(hist)-sizeof(struct packFHead));
		histIndex = MAXHIST-1;
	};
	BlockMove(&header,&hist[histIndex],(long) sizeof(struct packFHead));
	hist[histIndex].fLocked = FALSE;
	histIndex++;
	updateFL(PWINDSTR,TRUE);

	/* Set sizes. */
	sprintf(StDSize[0],"%ld",StDITot = header.DSize+header.RSize); ctop(StDSize[0]);
	sprintf(StDSize[1],"%ld",((header.DSize+header.RSize)*3)/4); ctop(StDSize[1]);
	sprintf(StDSize[2],"%ld",(header.DSize+header.RSize)/2); ctop(StDSize[2]);
	sprintf(StDSize[3],"%ld",(header.DSize+header.RSize)/4); ctop(StDSize[3]);
	StDISc = StDIPk = StDOCur = 0L;
	StDOTot = compFile ? 0L : StDITot;
	invalSizes(); invalIOFiles();

	/* Do compression pre-scan. */
	if (compFile) {
		retVal = compScan(&header,&ioRec);
		if (retVal != noErr) return(READFALERT);
		HuffStat(&preSz,&tabSz,&dataSz);
		StDOTot = tabSz + dataSz;
		StDOCur = 0L;
		invalIOFiles();
	};

	/* Write it out. */
	if (compFile) {
		writeBytes(&header,(long) sizeof(header.magic));
		if (compErr != noErr)
			return((compErr == dskFulErr) ? DISKFALERT : WRITFALERT);
		compStart();
		compEncode(header.fName,(long) sizeof(header)-sizeof(header.magic));
	} else {
		writeBytes(&header,(long) sizeof(header));
		if (compErr != noErr)
			return((compErr == dskFulErr) ? DISKFALERT : WRITFALERT);
	};

	/* Init the CRC to zero. */
	myCrc = 0;

	/* Copy the data fork. */
	if (size = header.DSize) {
		StDStr = compFile ? CODATA : PKDATA;
		invalStStr(); upStDDia();
		/* Open the fork for reading. */
		ioRec.u.iop.ioPermssn = fsRdPerm;
		ioRec.u.iop.ioMisc = NIL;
		if (PBOpen(&ioRec,FALSE) != noErr) return(OPENFALERT);
		ioRec.u.iop.ioPosMode = fsFromStart; ioRec.u.iop.ioPosOffset = NIL;
		ioRec.u.iop.ioBuffer = ioBuf;
		/* Copy the fork. */
		while (size) {
			ioRec.u.iop.ioReqCount = min(size,bufSize);
			retVal = PBRead(&ioRec,FALSE);
			if ((retVal != noErr) || (ioRec.u.iop.ioReqCount != ioRec.u.iop.ioActCount)) {
				PBClose(&ioRec,FALSE);
				return(READFALERT);
			};
			size -= ioRec.u.iop.ioActCount;
			StDIPk += ioRec.u.iop.ioActCount;
			invalIOFiles(); upStDDia();
			myCrc = crc(myCrc,ioBuf,ioRec.u.iop.ioActCount);
			if (compFile) {
				compEncode(ioBuf,ioRec.u.iop.ioActCount);
			} else {
				StDOCur += ioRec.u.iop.ioActCount;
				writeBytes(ioBuf,ioRec.u.iop.ioActCount);
				if (compErr != noErr) {
					PBClose(&ioRec,FALSE);
					return((compErr == dskFulErr) ? DISKFALERT : WRITFALERT);
				};
			};
			invalIOFiles(); upStDDia();
		};
		/* Close the fork. */
		PBClose(&ioRec,FALSE);
	};

	/* Copy the resource fork. */
	if (size = header.RSize) {
		StDStr = compFile ? CORSRC : PKRSRC;
		invalStStr(); upStDDia();
		/* Open the fork for reading. */
		ioRec.u.iop.ioPermssn = fsRdPerm;
		ioRec.u.iop.ioMisc = NIL;
		if (PBOpenRF(&ioRec,FALSE) != noErr) return(OPENFALERT);
		ioRec.u.iop.ioPosMode = fsFromStart; ioRec.u.iop.ioPosOffset = NIL;
		ioRec.u.iop.ioBuffer = ioBuf;
		/* Copy the fork. */
		while (size) {
			ioRec.u.iop.ioReqCount = min(size,bufSize);
			retVal = PBRead(&ioRec,FALSE);
			if ((retVal != noErr) || (ioRec.u.iop.ioReqCount != ioRec.u.iop.ioActCount)) {
				PBClose(&ioRec,FALSE);
				return(READFALERT);
			};
			size -= ioRec.u.iop.ioActCount;
			StDIPk += ioRec.u.iop.ioActCount;
			invalIOFile(); upStDDia();
			myCrc = crc(myCrc,ioBuf,ioRec.u.iop.ioActCount);
			if (compFile) {
				compEncode(ioBuf,ioRec.u.iop.ioActCount);
			} else {
				StDOCur += ioRec.u.iop.ioActCount;
				writeBytes(ioBuf,ioRec.u.iop.ioActCount);
				if (compErr != noErr) {
					PBClose(&ioRec,FALSE);
					return((compErr == dskFulErr) ? DISKFALERT : WRITFALERT);
				};
			};
			invalIOFiles(); upStDDia();
		};
		/* Close the fork. */
		PBClose(&ioRec,FALSE);
	};
	/* Write the CRC. */
	if (compFile) {
		compEncode(&myCrc,(long) sizeof(myCrc));
	} else {
		writeBytes(&myCrc,(long) sizeof(myCrc));
		if (compErr != noErr)
			return((compErr == dskFulErr) ? DISKFALERT : WRITFALERT);
	};

	if (compFile) {
		compFlush();
		if ((retVal = compRetErr()) != noErr)
			return((retVal == dskFulErr) ? DISKFALERT : WRITFALERT);
	};

	/* Normal return. */
	return(0);
}

/*	compScan(header,ioRec)

	Function: Scan the data for this file and build the compression table.

	Algorithm: Call HuffCount() for each of the header, the data fork, and the resource fork.

	Comments: None.
*/

compScan(header,ioRec)

register struct packFHead *header;
register ParamBlkRec *ioRec;

{
	unsigned myCrc;
	register long size;
	register int retVal;

	HuffInit();
	HuffCount(header->fName,(long) sizeof(struct packFHead)-sizeof(long));

	/* Init the CRC to zero. */
	myCrc = 0;

	/* Count the data fork. */
	if (size = header->DSize) {
		StDStr = SCDATA;
		invalStStr(); upStDDia();
		/* Open the fork for reading. */
		ioRec->u.iop.ioPermssn = fsRdPerm;
		ioRec->u.iop.ioMisc = NIL;
		if (PBOpen(ioRec,FALSE) != noErr) return(OPENFALERT);
		ioRec->u.iop.ioPosMode = fsFromStart; ioRec->u.iop.ioPosOffset = NIL;
		ioRec->u.iop.ioBuffer = ioBuf;
		/* Count the fork. */
		while (size) {
			ioRec->u.iop.ioReqCount = min(size,bufSize);
			retVal = PBRead(ioRec,FALSE);
			if ((retVal != noErr) || (ioRec->u.iop.ioReqCount != ioRec->u.iop.ioActCount)) {
				PBClose(ioRec,FALSE);
				return(READFALERT);
			};
			size -= ioRec->u.iop.ioActCount;
			StDISc += ioRec->u.iop.ioActCount;
			invalIOFiles(); upStDDia();
			myCrc = crc(myCrc,ioBuf,ioRec->u.iop.ioActCount);
			HuffCount(ioBuf,ioRec->u.iop.ioActCount);
		};
		/* Close the fork. */
		PBClose(ioRec,FALSE);
	};

	/* Count the resource fork. */
	if (size = header->RSize) {
		StDStr = SCRSRC;
		invalStStr(); upStDDia();
		/* Open the fork for reading. */
		ioRec->u.iop.ioPermssn = fsRdPerm;
		ioRec->u.iop.ioMisc = NIL;
		if (PBOpenRF(ioRec,FALSE) != noErr) return(OPENFALERT);
		ioRec->u.iop.ioPosMode = fsFromStart; ioRec->u.iop.ioPosOffset = NIL;
		ioRec->u.iop.ioBuffer = ioBuf;
		/* Count the fork. */
		while (size) {
			ioRec->u.iop.ioReqCount = min(size,bufSize);
			retVal = PBRead(ioRec,FALSE);
			if ((retVal != noErr) || (ioRec->u.iop.ioReqCount != ioRec->u.iop.ioActCount)) {
				PBClose(ioRec,FALSE);
				return(READFALERT);
			};
			size -= ioRec->u.iop.ioActCount;
			StDISc += ioRec->u.iop.ioActCount;
			invalIOFiles(); upStDDia();
			myCrc = crc(myCrc,ioBuf,ioRec->u.iop.ioActCount);
			HuffCount(ioBuf,ioRec->u.iop.ioActCount);
		};
		/* Close the fork. */
		PBClose(ioRec,FALSE);
	};
	/* Count the CRC. */
	HuffCount(&myCrc,(long) sizeof(myCrc));

	/* Now compute the table. */
	HuffCode();

	return(0);
}

/*	compStart()/compEncode()/compWrite()

	Function: Encode a series of data blocks. compStart() writes the table and sets up the rest.
	compEncode() encodes one block of data. compWrite() writes out the current bit-stream,
	and is also used to force the residual data out after we're all done.

	Algorithm: Use the Huffman encoding module.

	Comments: None.
*/

compStart()

{
	int compWrite();

	startCrypt();
	compErr = noErr;
	HuffBTable(buf2k,2048L,compWrite);
	bPtr = compBuf; bCnt = bufSize;
}

compEncode(blk,sz)

char *blk;
long sz;

{
	HuffBData(blk,sz);
}

compWrite()

{
	if (compErr == noErr) {
		if (bCnt < 2048L) {
			writeBytes(compBuf,bufSize-bCnt);
			if (compErr != noErr) return;
			bPtr = compBuf; bCnt = bufSize;
		};
		encrypt(buf2k,2048L);
		BlockMove(buf2k,bPtr,2048L);
		bPtr += 2048;
		bCnt -= 2048L;
		BitInit(buf2k,2048L,compWrite);
		StDOCur += 2048L;
		invalIOFiles(); upStDDia();
	};
}

compFlush()

{
	register long bsSize;

	if (compErr == noErr) {
		if ((bsSize = bufSize-bCnt) != 0L) writeBytes(compBuf,bsSize);
		bsSize = ByteSize();
		StDOCur += bsSize;
		if (cryptFile != NOCRYPT) bsSize = (bsSize+7L) & ~7L;
		encrypt(buf2k,bsSize);
		writeBytes(buf2k,bsSize);
		invalIOFiles(); upStDDia();
	};
}

compRetErr()

{
	if (compErr == eofErr) return(noErr);
	return(compErr);
}

writeBytes(buf,sz)

char *buf;
long sz;

{
	long bsSize;
	register char *bsPtr;
	Point dialogPt;
	SFReply contReply;
	int fileRef;
	register int retVal;

	if (compErr == noErr) {
		bsPtr = buf;
		while (sz > 0L) {
			bsSize = min(sz,4096);
			compErr = FSWrite(packedFRef,&bsSize,bsPtr);
			sz -= bsSize; bsPtr += bsSize;
			if (compErr != dskFulErr) continue;
			SetCursor(&arrow);
			if (CautionAlert(OFCALERT,NIL) != 2) return;

			FSClose(packedFRef);
			if (packedVRef != 0) FlushVol(NIL,packedVRef);
			packedFRef = packedVRef = 0;
			dialogPt.v = 40; dialogPt.h = 104;
			SFPutFile(pass(dialogPt),"\PContinuation file:","\PPackIt (continued)",NIL,
					&contReply);
			if (!contReply.good) return;
			updateFL(PWINDSTR,TRUE);
			SetCursor(*GetCursor(watchCursor));
			retVal = Create(contReply.fName,contReply.vRefNum,PITCR,'HRC9');
			if (retVal == dupFNErr) {
				FSDelete(contReply.fName,contReply.vRefNum);
				retVal = Create(contReply.fName,contReply.vRefNum,PITCR,'HRC9');
			};
			if (retVal != noErr) {
				StopAlert(OPENFALERT,NIL);
				return;
			};
			if (FSOpen(contReply.fName,contReply.vRefNum,&fileRef) != noErr) {
				StopAlert(OPENFALERT,NIL);
				return;
			};
			compErr = noErr;
			packedFRef = fileRef;
			packedVRef = contReply.vRefNum;
		};
	};
}

/*	unpackFile()

	Function: Query for a file, then unpack it.

	Algorithm: Call SFGetFile() to get the file name, then call unpackFN() to unpack it.

	Comments: None.
*/

pascal Boolean unpackFF(pBlock)

ParmBlkPtr pBlock;

{
	if (!textFl) return(FALSE);
	if ((pBlock->u.fp.ioFlFndrInfo.fdType == 'PIT ') || (pBlock->u.fp.ioFlFndrInfo.fdType == 'TEXT'))
		return(FALSE);
	return(TRUE);
}

pascal short unpackDlg(item,theDialog)

int item;
DialogPtr theDialog;

{
	int itemType;
	Handle itemHand;
	Rect itemBox;

	if ((item == -1) || (item == PTXTITEM)) {
		GetDItem(theDialog,PTXTITEM,&itemType,&itemHand,&itemBox);
		if (item == -1) SetCtlValue(itemHand,textFl ? 1 : 0);
		else {
			SetCtlValue(itemHand,(textFl = !textFl) ? 1 : 0);
			return(101);
		};
	};

	return(item);
}

unpackFile()

{
	Point dialogPt;
	SFReply packReply;

	/* Get the packed file name. */
	dialogPt.v = 40; dialogPt.h = 104;
	SFPGetFile(pass(dialogPt),"",unpackFF,-1,NIL,unpackDlg,&packReply,USFGDIA,NIL);
	/* Did he say OK? */
	if (!packReply.good) return;

	/* Do the file. */
	unpackFN(packReply.fName,packReply.vRefNum);
}

/*	unpackApp(n)

	Function: Unpack the n files given by the Finder.

	Algorithm: For each file, check that it's the right type, then call unpackFN() to unpack it.

	Comments: None.
*/

unpackApp(n)

int n;

{
	register int i;
	AppFile fileRef;
	FInfo fileFndr;
	register DialogPtr theDialog;
	int itemType;
	Handle itemHand;
	Rect itemBox;
	int hit;

	if (n < 1) return;

	GetAppFiles(1,&fileRef);
	if (fileRef.fType != PITFT) {
		if ((theDialog = GetNewDialog(PQDIA,NIL,-1L)) == NIL) return;
		while (TRUE) {
			setPQCtls(theDialog);
			ModalDialog(NIL,&hit);
			if ((hit == 1) || (hit == 2)) break;
			switch (hit) {
				case 4:
					if (cryptFile == NOCRYPT) compFile = !compFile;
					break;
				case 5:
					cryptFile = NOCRYPT;
					break;
				case 6:
					if (*password == 0) setPassword();
					cryptFile = SIMPCRYPT;
					compFile = TRUE;
					break;
				case 7:
					if (*password == 0) setPassword();
					cryptFile = DESCRYPT;
					compFile = TRUE;
					break;
			};
		};
		DisposDialog(theDialog);
		if (hit == 2) return;
		fndrFNum = n;
		fndrFInd = 1;
		packFile();
	} else {
		for (i = 1; i <= n; i++) {
			GetAppFiles(i,&fileRef);
			if (fileRef.fType != PITFT) continue;
			if (GetFInfo(fileRef.fName,fileRef.vRefNum,&fileFndr) != noErr) continue;
			if (fileFndr.fdCreator != PITCR) continue;
			unpackFN(fileRef.fName,fileRef.vRefNum);
			ClrAppFiles(i);
		};
	};
}

setPQCtls(theDialog)

DialogPtr theDialog;

{
	int itemType;
	Handle itemHand;
	Rect itemBox;

	GetDItem(theDialog,4,&itemType,&itemHand,&itemBox);
	SetCtlValue(itemHand,compFile ? 1 : 0);
	GetDItem(theDialog,5,&itemType,&itemHand,&itemBox);
	SetCtlValue(itemHand,(cryptFile == NOCRYPT) ? 1 : 0);
	GetDItem(theDialog,6,&itemType,&itemHand,&itemBox);
	SetCtlValue(itemHand,(cryptFile == SIMPCRYPT) ? 1 : 0);
	GetDItem(theDialog,7,&itemType,&itemHand,&itemBox);
	SetCtlValue(itemHand,(cryptFile == DESCRYPT) ? 1 : 0);
}

/*	unpackFN(name,vRef)

	Function: Open the file specified by name/vRef and unpack it.

	Algorithm: Open it with FSOpen(), then for each packed file in it, call outPackIt().

	Comments: None.
*/

int unpackAll;
int reallyQuit;

pascal short unpDlg(item,theDialog)

int item;
DialogPtr theDialog;

{
	int itemType;
	Handle itemHand;
	Rect itemBox;

	if (item == SKIPITEM) {
		reallyQuit = FALSE;
		return(putCancel);
	} else if (item == SVALLITEM) {
		unpackAll = TRUE;
		return(putSave);
	};
	return(item);
}

unpackFN(name,vRef)

char *name;
short vRef;

{
	Point dialogPt;				/* Where to put the query dialog. */
	SFReply outFileReply;		/* Unpacked file query reply (from SFPutFile()). */
	struct packFHead header;	/* Packed file header. */
	register int alertVal;		/* What alert (if any) to do. */
	register int retVal;		/* Return values. */
	DialogPtr statusDialog;		/* Pointer to the status dialog. */
	long l;

	/* Assume no alert. */
	alertVal = 0;

	/* Open the file. */
	if (FSOpen(name,vRef,&packedFRef) != noErr) {
		StopAlert(OPENFALERT,NIL);
		return;
	};
	SetFPos(packedFRef,fsFromStart,0L);
	compFile = FALSE; cryptFile = NOCRYPT;
	compDestart();

	/* Show the status window. */
	histIndex = 0;
	ShowWindow(&FLWind);

	/* For each file, call outPackIt(). */
	unpackAll = FALSE;
	while (TRUE) {
		/* Read in the header info. */
		allign();
		compFile = FALSE;
		cryptFile = NOCRYPT;
		retVal = readBS(&header,(long) sizeof(header.magic));
		if ((retVal != noErr) && (retVal != eofErr)) {
			alertVal = READFALERT;
			break;
		};
		if (header.magic == 'PEnd') break;
		if (((header.magic >= 'PMa1') && (header.magic <= 'PMa7'))) {
			compFile = (header.magic & 4) != 0;
			cryptFile = header.magic & 3;
			if ((cryptFile != NOCRYPT) && (*password == 0)) setPassword();
		} else if (header.magic != 'PMag') {
			alertVal = INVPALERT;
			break;
		} else {
			compFile = FALSE;
			cryptFile = NOCRYPT;
		};
		synchBuf();

		if (compFile) compTable();

		retVal = readBS(&header.fName,(long) sizeof(header)-sizeof(header.magic));
		if (retVal != noErr) {
			alertVal = READFALERT;
			break;
		};
		if (header.hCrc != crc(0,&(header.fName),
						(long) sizeof(header)-sizeof(header.magic)-sizeof(header.hCrc))) {
			alertVal = HCRCFALERT;
			break;
		};
		/* Find the output file name. */
		updateFL(UWINDSTR,FALSE);
		if (unpackAll) {
			BlockMove(header.fName,outFileReply.fName,(long) sizeof(outFileReply.fName));
			outFileReply.good = TRUE;
		} else {
			reallyQuit = TRUE;
			dialogPt.v = 40; dialogPt.h = 33;
			SFPPutFile(pass(dialogPt),"\PUnpacked file name:",header.fName,unpDlg,
						&outFileReply,USFPDIA,NIL);
			if ((!outFileReply.good) && reallyQuit) break;
		};
		reallyQuit = FALSE;
		/* Let him know this may take a while. */
		SetCursor(*GetCursor(watchCursor));
		/* Let him know what we're packing. */
		if (histIndex >= MAXHIST) {
			BlockMove(&hist[1],hist,(long) sizeof(hist)-sizeof(struct packFHead));
			histIndex = MAXHIST-1;
		};
		BlockMove(&header,&hist[histIndex],(long) sizeof(struct packFHead));
		if (outFileReply.good)
			BlockMove(outFileReply.fName,hist[histIndex].fName,
						(long) sizeof(outFileReply.fName));
		hist[histIndex].fLocked = outFileReply.good;
		histIndex++;
		updateFL(UWINDSTR,outFileReply.good);

		if (!outFileReply.good) {
			if (cryptFile == DESCRYPT) {
				ShowWindow(&StDDia);
				StDStr = SKFILE;
				invalStStr();
				/* Set sizes. */
				sprintf(StDSize[0],"%ld",StDITot = header.DSize+header.RSize); ctop(StDSize[0]);
				sprintf(StDSize[1],"%ld",((header.DSize+header.RSize)*3)/4); ctop(StDSize[1]);
				sprintf(StDSize[2],"%ld",(header.DSize+header.RSize)/2); ctop(StDSize[2]);
				sprintf(StDSize[3],"%ld",(header.DSize+header.RSize)/4); ctop(StDSize[3]);
				StDISc = StDOCur = 0L;
				StDOTot = 0L;
				if ((StDIPk = ByteDeSize()) > StDITot) StDIPk = StDITot;
				invalSizes(); invalIOFiles();
				upStDDia();
			};
			skipBS(header.DSize + header.RSize + 2L);
			if (cryptFile == DESCRYPT) {
				invalIOFiles(); upStDDia(); Delay(60L,&l); HideWindow(&StDDia);
			};
			SetCursor(&arrow);
			continue;
		};

		ShowWindow(&StDDia);
		alertVal = outPackIt(&header,outFileReply.fName,outFileReply.vRefNum);
		invalIOFiles(); upStDDia(); Delay(60L,&l); HideWindow(&StDDia);
		SetCursor(&arrow);
		if (alertVal != 0) break;
	};

	/* Let him know we're done and get rid of the info window. */
	if ((alertVal == 0) && (!reallyQuit)) {
		updateFL(UWINDSTR,FALSE);
		NoteAlert(UPDALERT,NIL);
	};
	HideWindow(&FLWind);

closenalert:
	/* Close the packed file. */
	FSClose(packedFRef);

	/* Do an alert if we're here due to errors. */
	if (alertVal != 0) StopAlert(alertVal,NIL);
}

/*	outPackIt(header,fName,vRef)

	Function: Unpack one file out of the packed file.

	Algorithm: Create the file & fill in the header ingo from the structure pointed at by header,
	then copy the data and resource forks.

	Comments: None.
*/

outPackIt(header,fName,vRef)

struct packFHead *header;
char *fName;
short vRef;

{
	ParamBlkRec ioRec;			/* Low level IO record. */
	register int retVal;		/* Return values. */
	register long size;			/* Size of the forks. */
	long crcSize;				/* Size of CRC to be read. */
	register unsigned myCrc;	/* The calculated CRC. */
	unsigned inputCrc;			/* The read CRC. */

	/* Init the calculated CRC to zero. */
	myCrc = 0;

	/* Create & open the new file. */
	ioRec.ioNamePtr = fName; ioRec.ioVRefNum = vRef;
	ioRec.u.fp.ioFVersNum = ioRec.u.fp.ioFDirIndex = 0;
	retVal = Create(fName,vRef,'PIT ','TEXT');
	if (retVal == dupFNErr) {
		PBDelete(&ioRec,FALSE);
		retVal = Create(fName,vRef,'PIT ','TEXT');
	};
	if (retVal != noErr) return(OPENFALERT);

	/* Set sizes. */
	sprintf(StDSize[0],"%ld",StDITot = header->DSize+header->RSize); ctop(StDSize[0]);
	sprintf(StDSize[1],"%ld",((header->DSize+header->RSize)*3)/4); ctop(StDSize[1]);
	sprintf(StDSize[2],"%ld",(header->DSize+header->RSize)/2); ctop(StDSize[2]);
	sprintf(StDSize[3],"%ld",(header->DSize+header->RSize)/4); ctop(StDSize[3]);
	StDISc = StDOCur = 0L;
	StDOTot = 0L;
	if ((StDIPk = ByteDeSize()) > StDITot) StDIPk = StDITot;
	invalSizes(); invalIOFiles();

	/* Copy the data fork. */
	if (size = header->DSize) {
		StDStr = UPDATA;
		invalStStr(); upStDDia();
		/* Open the fork for reading. */
		ioRec.u.iop.ioPermssn = fsWrPerm;
		ioRec.u.iop.ioMisc = NIL;
		if (PBOpen(&ioRec,FALSE) != noErr) return(OPENFALERT);
		ioRec.u.iop.ioPosMode = fsFromStart; ioRec.u.iop.ioPosOffset = NIL;
		ioRec.u.iop.ioBuffer = ioBuf;
		/* Copy the fork. */
		while (size) {
			ioRec.u.iop.ioReqCount = min(size,bufSize);
			retVal = readBS(ioBuf,ioRec.u.iop.ioReqCount);
			if (retVal != noErr) {
				PBClose(&ioRec,FALSE);
				return(READFALERT);
			};
			size -= ioRec.u.iop.ioReqCount;
			myCrc = crc(myCrc,ioBuf,ioRec.u.iop.ioReqCount);
			if ((retVal = PBWrite(&ioRec,FALSE)) != noErr) {
				PBClose(&ioRec,FALSE);
				return((retVal == dskFulErr) ? DISKFALERT : WRITFALERT);
			};
			StDOCur += ioRec.u.iop.ioActCount;
			invalIOFiles(); upStDDia();
		};
		/* Close the fork. */
		PBClose(&ioRec,FALSE);
	};

	/* Copy the data fork. */
	if (size = header->RSize) {
		StDStr = UPRSRC;
		invalStStr(); upStDDia();
		/* Open the fork for reading. */
		ioRec.u.iop.ioPermssn = fsWrPerm;
		ioRec.u.iop.ioMisc = NIL;
		if (PBOpenRF(&ioRec,FALSE) != noErr) return(OPENFALERT);
		ioRec.u.iop.ioPosMode = fsFromStart; ioRec.u.iop.ioPosOffset = NIL;
		ioRec.u.iop.ioBuffer = ioBuf;
		/* Copy the fork. */
		while (size) {
			ioRec.u.iop.ioReqCount = min(size,bufSize);
			retVal = readBS(ioBuf,ioRec.u.iop.ioReqCount);
			if (retVal != noErr) {
				PBClose(&ioRec,FALSE);
				return(READFALERT);
			};
			size -= ioRec.u.iop.ioReqCount;
			myCrc = crc(myCrc,ioBuf,ioRec.u.iop.ioReqCount);
			if ((retVal = PBWrite(&ioRec,FALSE)) != noErr) {
				PBClose(&ioRec,FALSE);
				return((retVal == dskFulErr) ? DISKFALERT : WRITFALERT);
			};
			StDOCur += ioRec.u.iop.ioActCount;
			invalIOFiles(); upStDDia();
		};
		/* Close the fork. */
		PBClose(&ioRec,FALSE);
	};

	/* Check for decompression errors. */
	if (compRetErr() != noErr) return(READFALERT);

	/* Set the file header info. */
	ioRec.u.fp.ioFDirIndex = 0;
	if (PBGetFInfo(&ioRec,FALSE) != noErr) return(WRITFALERT);
	ioRec.u.fp.ioFlFndrInfo.fdType = header->fType;			/* File type. */
	ioRec.u.fp.ioFlFndrInfo.fdCreator = header->fCreator;	/* File creator. */
	ioRec.u.fp.ioFlFndrInfo.fdFlags &= ~fdFlMask;			/* File (finder) flags. */
	ioRec.u.fp.ioFlFndrInfo.fdFlags |= (header->fFlags & fdFlMask);
	ioRec.u.fp.ioFlCrDat = header->fCrDate;					/* Creation date. */
	ioRec.u.fp.ioFlMdDat = header->fModDate;				/* Modification date. */
	if (PBSetFInfo(&ioRec,FALSE) != noErr) return(WRITFALERT);

	/* Check the CRC. */
	retVal = readBS(&inputCrc,(long) sizeof(inputCrc));
	if (retVal != noErr) return(READFALERT);
	if (inputCrc != myCrc) NoteAlert(CRCFALERT,NIL);

	/* Lock the file if indicated. */
	if (header->fLocked) PBSetFLock(&ioRec,FALSE);

	/* Make sure it's all out. */
	if (PBFlushVol(&ioRec,FALSE) != noErr) return(WRITFALERT);

	/* Normal Return. */
	return(0);
}

/*	compDestart()/readBS()

	Function: Decode a compressed file. compDestart() reads in the table and sets up the bit-stream
	for the rest of the decoding. readBS() reads a specified number of bytes, decompressing if
	appropriate.

	Algorithm: Use the Huffman encoding module.

	Comments: None.
*/

compDestart()

{
	int compRead();

	bCnt = 0L;
	upCnt = 0;
	compErr = noErr;
	realEOF = FALSE;
	BitInit(compBuf,0L,compRead);
}

allign()

{
	if (cryptFile == NOCRYPT) ByteAllign();
	else {
		cryptFile = NOCRYPT;
		compRead();
	};
}

compTable()

{
	HuffDTable();
}

readBS(blk,sz)

char *blk;
long sz;

{
	if (compFile) HuffDData(blk,sz);
	else BlkDecode(blk,sz);
	if (compErr == eofErr) return(noErr);
	return(compErr);
}

skipBS(sz)

long sz;

{
	register long cnt;

	for (cnt = sz; cnt; cnt -= min(cnt,bufSize)) readBS(ioBuf,min(cnt,bufSize));
}

compRead()

{
	long size;
	Point dialogPt;
	SFReply contReply;
	SFTypeList fileTypes;
	int fileRef;

	if ( (compErr == noErr) || ((compErr == eofErr) && (!realEOF)) ) {
		if ((bCnt == 0L) || ((bCnt < 8L) && (cryptFile != NOCRYPT))) {
			if (compErr == eofErr) {
				SetCursor(&arrow);
				if (CautionAlert(FCALERT,NIL) != 2) {
					SetCursor(*GetCursor(watchCursor));
					realEOF = TRUE;
				} else {
					fileTypes[0] = 'HRC9';
					dialogPt.v = 40; dialogPt.h = 104;
					SFGetFile(pass(dialogPt),"",NIL,1,fileTypes,NIL,&contReply);
					SetCursor(*GetCursor(watchCursor));
					updateFL(UWINDSTR,TRUE);
					if (contReply.good)
						if (FSOpen(contReply.fName,contReply.vRefNum,&fileRef) == noErr) {
							FSClose(packedFRef);
							packedFRef = fileRef;
							compErr = noErr;
						};
				};
			};
			if (bCnt > 0L) BlockMove(bPtr,compBuf,bCnt);
			size = bufSize-bCnt;
			compErr = FSRead(packedFRef,&size,(bPtr = compBuf)+bCnt);
			bCnt += size;
		};
		size = (cryptFile == NOCRYPT) ? 2048L : 8L;
		size = min(size,bCnt);
		BlockMove(bPtr,buf2k,size);
		bPtr += size; bCnt -= size;
		if ((StDIPk += size) > StDITot) StDIPk = StDITot;
		if (((upCnt += size) & 0x3FF) == 0) {
			invalIOFiles();
			upStDDia();
		};
		if (cryptFile != NOCRYPT) decrypt(buf2k,8L);
		if ((compErr == noErr) || (compErr == eofErr)) BitInit(buf2k,size,compRead);
	};
}

synchBuf()

{
	long size;
	long ByteDeSize();

	if (cryptFile != NOCRYPT) {
		startCrypt();
		size = ByteDeSize();
		bPtr -= size; bCnt += size;
		compRead();
	};
}

/*	crc(oldcrc,blk,sz)

	Function: Return the CRC on the block pointed to by blk, of size sz, starting with the crc oldcrc.

	Algorithm: From Tanenbaum and some XModem/CRC documentation.

	Comments: I will later change this to a faster, table-driven algorithm.
*/

crc(oldcrc,blk,sz)

unsigned oldcrc;
char *blk;
long sz;

{
	register unsigned crc;
	register char *ptr;
	register long cnt;
	register unsigned i;

	crc = oldcrc; ptr = blk; cnt = sz;

	while (cnt--) {
		crc = crc ^ ( ((int) *ptr++) << 8 );
		for (i = 0; i < 8; i++)
			if (crc & 0x8000) crc = (crc<<1) ^ 0x1021;
			else crc <<= 1;
	};

	return(crc);
}

/*	deleteFile()

	Function: Query for a file, verify it, and delete it.

	Algorithm: Call SFGetFile() to get the file name, then CautionAlert() to verify, then FSDelete()
	to delete it.

	Comments: None.
*/

deleteFile()

{
	SFReply delFileReply;		/* File query reply (from SFGetFile()). */
	Point dialogPt;				/* Where to put the query dialog. */

	/* Get the file to delete. */
	dialogPt.v = 40; dialogPt.h = 82;
	SFGetFile(pass(dialogPt),"",NIL,-1,NIL,NIL,&delFileReply);
	/* Did he say "OK"? */
	if (!delFileReply.good) return;

	/* Give him a chance to change his mind. */
	ParamText(delFileReply.fName,"\P","\P","\P");
	if (CautionAlert(DELFALERT,NIL) != 1) return;

	/* Delete the file. */
	FSDelete(delFileReply.fName,delFileReply.vRefNum);
}

/*	updateFL(strNum,doOutline)

	Function: Update the file list window from hist.

	Algorithm: Draw the list of unpacked or packed files and how they were packed.

	Comments: None.
*/

updateFL(strNum,doOutline)

int strNum;
int doOutline;

{
	register int v;
	register int lh;
	int lh12;
	register int i;
	FontInfo fInf;

	SetPort(&FLWind);
	EraseRect(&thePort->portRect);
	TextFont(geneva); TextFace(outlineStyle);
	TextSize(9);
	GetFontInfo(&fInf);
	lh = fInf.ascent + fInf.descent + fInf.leading;
	TextFont(0); TextSize(12);
	GetFontInfo(&fInf);
	lh12 = fInf.ascent + fInf.descent + fInf.leading;

	v = thePort->portRect.top + 20;

	TextFace(underlineStyle);
	MoveTo(10,v);
	DrawString(*GetString(strNum));
	v += lh12;

	TextFont(geneva); TextSize(9);
	TextFace(0);
	i = 0;
	while  ((v+(histIndex-i)*lh) >= thePort->portRect.bottom) i++;
	for (; i < histIndex; i++, v += lh) {
		if (doOutline && (i == (histIndex-1))) TextFace(outlineStyle);
		else if (hist[i].fLocked) TextFace(boldStyle);
		MoveTo(20,v);
		DrawString(hist[i].fName);
		TextFace(0);
	};
}