🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: mod.mac.sources
Article #41

Unpit version 0.3.1 Source (part 2 of 3)

#41
From: macintosh
Date: Fri, 13 Feb 1987 07:41
603 lines
17600 bytes
[Unpit version 0.3.1 Source - part 2 of 3]

---
	    }
	    DisposDialog(theDialog); theDialog = 0L;
	    decode = 0;
	    bit = 0;	/* flush unused bits */
	    }
	else if (str_cmp_4(temp, "PEnd"))
	    break;
	else {
	    Err2(errNotPackit);
	    return;
	    }
    }
error:				/* should never be reached except by "goto" */
    if (theDialog != 0L)
        DisposDialog(theDialog);
    return;
}

/*
Read compression decoding data.
##############################   Read_Tree   ##############################

    This routine recursively reads the compression decoding data.
    It appears to be Huffman compression.
    
*/

nodeptr read_tree(eflag)
Boolean *eflag;
{	int bit;

	nodeptr np;
	np = freenode++;
	bit = getbit(eflag); if (*eflag) return (0L);
	if (bit == 1) {
		np->flag = 1;
		np->byte = getbyte(eflag); if (*eflag) return (0L);
	}
	else {
		np->flag = 0;
		np->zero = read_tree(eflag); if (*eflag) return (0L);
		np->one  = read_tree(eflag); if (*eflag) return (0L);
	}
	return(np);
}


/*
Read header containing information about the packed file to follow.
###############################   Read_Hdr   ##############################
*/

crctype read_hdr(eflag)
Boolean *eflag;
{
	register int n;

	for (n = 0; n < HDRBYTES; n++) {
		hdr[n] = getbyte(eflag);
		if (*eflag) return;
	}
	datalen = get4(hdr + H_DLENOFF);
	rsrclen = get4(hdr + H_RLENOFF);
	return(upd_crc(0, hdr, (long) HDRBYTES));
}


/*
Write one fork of an unpacked file.
##############################   Write_Fork   #############################

    saveOutput indicates whether or not the output should be saved; to skip
        a file it is still necessary to read it from the .PIT file in order
        to advance to the start of the next file that can be unpacked.

*/

crctype write_fork(saveOutput, out_ref_num, bytes, crc, eflag)
Boolean saveOutput, *eflag;
int out_ref_num;
long bytes;
register crctype crc;
{
	register int b, i;
	long Count;
	static char out_buffer[BSIZE];
	int out_index = 0;
	
	if ((decode == 0) && (bit == 0)) {
	    /* Special-case the unpacking of Packit-I files, as unpacking */
	    /* them using more general-purpose code takes >50% longer (33 */
	    /* vs 19 seconds when unpacking ResEdit on a RAMDisk).  While */
	    /* this code isn't very pretty, it gets the job done. . .     */

	    if ((bytes > 0) && ((Count = pit_max_index-pit_index) > 0L)) {
	        /* we won't be using read_byte(), so empty its buffer */
	        if (bytes < Count) Count = bytes;
	        if (saveOutput && Err(FSWrite(out_ref_num, &Count,
	            &pit_buffer[pit_index]),errWriting)) {
	            *eflag = TRUE; return 0;
	        }
	        crc = upd_crc(crc, &pit_buffer[pit_index], Count);
	        bytes -= Count; pit_index += (int) Count;
	    }

	    while (bytes > 0L) {
	        Count = (bytes >= BSIZE) ? BSIZE : bytes;
	        if (Err(FSRead (pit_ref_num,&Count,out_buffer),errReading) ||
	            (saveOutput && Err( /* this isn't the most elegant code */
		        FSWrite(out_ref_num,&Count,out_buffer),errWriting))) {
	            *eflag = TRUE;
	            return 0;
	        }
	        crc = upd_crc(crc, out_buffer, Count);
	        bytes -= Count; pit_bytes_left -= Count;
	    }
	} else {
	    while (bytes-- > 0) {
		b = getbyte(eflag); if (*eflag) return;
		crc = crc ^ (b << 8);
		for (i = 0; i < 8; i++)
			if (crc & 0x8000)
				crc = (crc << 1) ^ 0x1021;
			else
				crc <<= 1;
		if (saveOutput) {
		    if (out_index == BSIZE) {
			Count = (long) out_index;
			if (Err(FSWrite(out_ref_num, &Count, &out_buffer),
				errWriting)) {
			    *eflag = TRUE;
			    return 0;
			}
			out_index = 0;
    		    }
		    out_buffer[out_index++] = (char) b;
		}
	    }
	    if (saveOutput && (out_index > 0)) {
	        Count = (long) out_index;
	        if (Err(FSWrite(out_ref_num, &Count, &out_buffer),errWriting)) {
	            *eflag = TRUE;
	            return 0;
	        }
	    }
	}
	return(crc & 0xffff);
}

/*
Convert four input characters into a long.
#################################   Get4   #################################
*/
long
get4(bp)
char *bp;
{
	register int i;
	long value = 0;

	for (i = 0; i < 4; i++) {
		value <<= 8;
		value |= (*bp & BYTEMASK);
		bp++;
	}
	return(value);
}

/*
Read and return a two-byte CRC from the input file.
################################   GetCrc   ################################
*/
crctype getcrc(eflag)
Boolean *eflag;
{
	int value1, value2;
	
	value1 = getbyte(eflag) & BYTEMASK; if (*eflag) return 0;
	value2 = getbyte(eflag) & BYTEMASK; if (*eflag) return 0;
	return((value1 << 8) | value2);
}

/*
Copy n characters from p2 to p1.
#################################   Copy   #################################
*/
copy(p1, p2, n)
char *p1, *p2;
int n;
{
	while (n-- > 0)
		*p1++ = *p2++;
}


/*
Return the next bit in the input stream (MSB first).
################################   GetBit   ################################
*/
static char b;
getbit(eflag)
Boolean *eflag;
{
	if (bit == 0) {
		b = read_byte(eflag) & 0xff; if (*eflag) return 0;
		bit = 8;
	}
	bit--;
	return((b >> bit) & 1);
}

/*
Get the next virtual byte from the input file.
###############################   GetByte   ################################

    This routine returns the next 8 bits.  If decoding is on, it finds the
    byte in the decoding tree based on the bits from the input stream.  If
    decoding is not on, it either gets it directly from the input stream or
    puts it together from 8 calls to getbit(), depending upon whether or not
    we are currently on a byte boundary.  (Note: the calls to getbit() have
    been expanded inline in order to gain some speed . . . ((b >> bit) & 1)
    is used where the value of getbit(eflag) was used before, and it's
    preceded by the statements "if (bit == 0 {...}" and "bit--").
    
*/
getbyte(eflag)
Boolean *eflag;
{
	register nodeptr np;
	register int i, byt;
	
	if (decode) {
		np = nodelist;
		while (np->flag == 0) {
			if (bit == 0) {
				b = read_byte(eflag) & 0xff;
				if (*eflag) return 0;
				bit = 8;
			}
			bit--;
			np = ((int) ((b >> bit) & 1)) ? np->one : np->zero;
		}
		byt = np->byte;
	}
	else {
		if (bit == 0) {	/* on byte boundary? */
			byt = read_byte(eflag) & 0xff; if (*eflag) return 0;
			}
		else {		/* no, put a byte together */
			byt = 0;
			for (i = 8; i > 0; i--) {
				if (bit == 0) {
					b = read_byte(eflag) & 0xff;
					if (*eflag) return 0;
					bit = 8;
				}
				bit--;
				byt = (byt << 1) + (int) ((b >> bit) & 1);
			}
		}
	}
	return(byt);
}

/*
Create a Packit-I or -II format file containing files selected by the user.
#################################   Pack   ################################
*/
pack()
	{
	SFReply PitRecord;
    Point   where;
    int     pit_ref_num, i;
    Boolean Success;
    char   *msg;

    /* Get name of output file */
    where.h = PUT_FILE_X; where.v = PUT_FILE_Y;
    msg = Compress ? "\PSave Packit-II file as:" : "\PSave Packit-I file as:";
    SFPutFile(where,msg,"\PPackit.pit",0L,&PitRecord);
    if (! PitRecord.good) return;

    /* Create and open output file */
    if ((i=Create(PitRecord.fName,PitRecord.vRefNum,'UPIT','PIT '))==dupFNErr) {
        FSDelete(PitRecord.fName,PitRecord.vRefNum);
        i = Create(PitRecord.fName, PitRecord.vRefNum, 'UPIT', 'PIT ');
    }
    if (Err(i,errCreateFile)) return;
    if (Err(FSOpen(PitRecord.fName,PitRecord.vRefNum,&pit_ref_num),
            errOpenDataFk)) return;

     /* Call routine that does real work */
     Success = pack_files(pit_ref_num);
     FSClose(pit_ref_num);
     if (! Success) FSDelete(PitRecord.fName, PitRecord.vRefNum);
     FlushVol(0L, PitRecord.vRefNum);
}


/*
Pass the indicated file header, data fork, resource fork, and computed CRCs
through the supplied function "MyWrite".  This function saves some space in
"pack_files", as compressed files need to be scanned twice (once to get the
frequency counts, once to write the Huffman encodings for the bytes).

###########################    Run_Through_File   ##########################
*/
run_through_file(MyWrite,pit_ref_num,fileHeader,InpRecord,data_len,rsrc_len)
int      	(*MyWrite)();
int	        pit_ref_num;
unsigned char  *fileHeader;
long		data_len, rsrc_len;
SFReply        *InpRecord;
{   long	     Count, bytesLeft;
    register crctype crc;
    unsigned char    io_buffer[BSIZE], fileCRC[2];
    int		     inp_ref_num;


    /* Write file header */
    Count = HDRBYTES + 2;
    if (Err((*MyWrite)(pit_ref_num, &Count, fileHeader), errWriting))
        goto error;

    /* Initialize data/resource CRC */
    crc = 0;

    /* Copy file's data fork to Packit file, if it has one */
    if (bytesLeft = data_len) {
        if (Err(FSOpen(InpRecord->fName,InpRecord->vRefNum,&inp_ref_num),
    		          errOpenDataFk)) goto error;
        while (bytesLeft > 0L) {
            Count = (bytesLeft >= BSIZE) ? BSIZE : bytesLeft;
            if (Err(  FSRead  (inp_ref_num,&Count,io_buffer),errReading) ||
	        Err((*MyWrite)(pit_ref_num,&Count,io_buffer),errWriting)) {
	        FSClose(inp_ref_num);
                goto error;
            }
            crc = upd_crc(crc, io_buffer, Count);
            bytesLeft -= Count;
        }
        FSClose(inp_ref_num);
    }

    /* Copy file's resource fork to Packit file, if it has one */
    if (bytesLeft = rsrc_len) {
        if (Err(Open_Rsrc(InpRecord->fName,InpRecord->vRefNum,fsRdPerm,
    		          &inp_ref_num),errOpenRsrcFk)) goto error;
        while (bytesLeft > 0L) {
            Count = (bytesLeft >= BSIZE) ? BSIZE : bytesLeft;
            if (Err(  FSRead  (inp_ref_num,&Count,io_buffer),errReading) ||
	        Err((*MyWrite)(pit_ref_num,&Count,io_buffer),errWriting)) {
	        FSClose(inp_ref_num);
                goto error;
            }
            crc = upd_crc(crc, io_buffer, Count);
            bytesLeft -= Count;
        }
        FSClose(inp_ref_num);
    }

    /* Write file's data/resource CRC */
    fileCRC[0] = (((unsigned int) crc) >> 8) & 0xff;
    fileCRC[1] = (((unsigned int) crc) & 0xff);
    Count = 2;
    if (Err((*MyWrite)(pit_ref_num,&Count,fileCRC),errWriting)) goto error;
    return FALSE;
error:
    return TRUE;
}


/*
Allow FSWrite() to be passed as a parameter to Run_Through_File (above)
###############################   C_FSWrite   ##############################
*/
int C_FSWrite(arg1, arg2, arg3)
int	arg1;
long   *arg2;
char   *arg3;
{
    return FSWrite(arg1, arg2, arg3);
}


/*
Initialize frequency counts to zero.
###############################   InitCount   ##############################
*/
void init_count()
{   int i;

    for (i = 0; i < 256; i++) {
        nodelist[i].flag   = TRUE;
        nodelist[i].byte   = i;
        nodelist[i].index2 = 0;
        nodelist[i].count  = 0L;
        nodelist[i].zero   = 0L;
        nodelist[i].one    = 0L;
        }
}


/*
Build byte frequency counts on a buffer-by-buffer basis.
###############################   FreqCount   ##############################
*/
int freq_count(Dummy, Count, Buffer)
int            Dummy;
long          *Count;
unsigned char *Buffer;
{   long MyCount = *Count;

    while (MyCount--) {
        nodelist[*Buffer].count += 1;
        Buffer++;
    }
    return noErr;
}


/* Macros to simplify using an ordering relationship with a secondary key */
#define GTR(a,b) ((a->count  > b->count) ||\
                 ((a->count == b->count) && (a->index2  > b->index2)))
#define LEQ(a,b) ((a->count  < b->count) ||\
                 ((a->count == b->count) && (a->index2 <= b->index2)))


/*
Insert a new value into the heap in O(log(n)) time.
##############################   HeapInsert   ##############################
*/
void heapinsert(n)	/* standard heap insertion algorithm from a textbook */
int n;			/* inserts heap[n] into heap stored in heap[1...n-1] */
{   int      i, j;
    nodeptr  item;

    j = n;  i = n / 2;  item = heap[n];
    while ((i > 0) && GTR(heap[i], item)) {
        heap[j] = heap[i];
        j = i; i = i / 2;
    }
    heap[j] = item;
}


/*
Turn a tree whose left & right sons are heaps into a heap in O(log(n)) time.
##############################   HeapAdjust   ##############################
*/
void heapadjust(i, n)	/* standard heap adjustment algorithm */
int i, n;
{   int      j;
    nodeptr  item;

    j = 2 * i; item = heap[i];
    while (j <= n) {
        if ((j < n) && GTR(heap[j], heap[j+1])) j++;
        if (LEQ(item, heap[j])) break;
        heap[j / 2] = heap[j];
        j = 2 * j;
    }
    heap[j / 2] = item;
}


/*
Return the heap element with the smallest value and delete it from the heap.
################################   HeapMin   ###############################
*/
nodeptr heapmin(n)
int *n;
{   nodeptr item;

    item = heap[1];
    heap[1] = heap[*n];
    if (*n -= 1) heapadjust(1, *n);
    return item;
}


/*
Build a minimum weighted binary tree using the counts in nodelist[0..255].
###############################   BuildTree   ##############################
*/
nodeptr build_tree()
{   int      i, n, gen;
    nodeptr  lson, rson, nnod, free;
    	 
    n = 0; free = 0L; gen = 0;
    for (i = 256; i < 512; i++) {	/* All nodes that we know don't */
        nodelist[i].zero = free;	/*     contain frequency counts */
        free = &nodelist[i];		/*     go to the free node list */
        }
    for (i = 0; i < 256; i++)
        if (nodelist[i].count) {	/* If a particular byte doesn't */
            heap[++n] = &nodelist[i];	/*     occur at all, it doesn't */
            if (n > 1) heapinsert(n);	/*     get placed into the tree */
    	} else {			/*     since it would just make */
    	    nodelist[i].zero = free;	/*     the header larger . . .  */
    	    free = &nodelist[i];
    	}
    while (n >= 2) {
        lson = heapmin(&n);		/* The standard algorithm which */
        rson = heapmin(&n);		/*     we use to build the tree */
	nnod = free;			/*     is quite elegant...given */
	free = free->zero;		/*     W1, W2, .. Wn one simply */
	nnod->flag   = FALSE;		/*     solves a smaller problem */
	nnod->zero   = lson;            /*     W1 + W2, W3, .. Wn until */
	nnod->one    = rson;		/*     one gets the trivial W1. */
        nnod->count  = lson->count + rson->count;
        nnod->index2 = ++gen;           /* Use a secondary key to place */
        heap[++n]    = nnod;		/*     W1 + W2 subtree so as to */
        if (n > 1) heapinsert(n);	/*     minimize unweighted sum. */
        }
    return heapmin(&n);
}

/*
Write a single bit to the output file.
################################   PutBit   ###############################

    Note that this routine reuses a buffer that was allocated for Unpack.

*/
char OutChar;
int  BitCount;

putbit(bit, file)
int bit, file;
{   long Count;

    OutChar <<= 1;
    if (bit) OutChar |= 1;
    if (++BitCount == 8) {
        pit_buffer[pit_index++] = OutChar;
        BitCount = 0; OutChar = (char) 0;
        if (pit_index == BSIZE) {
            Count = BSIZE;
            pit_index = 0;
            return FSWrite(file, &Count, pit_buffer);
        }
    }
    return noErr;
}


/*
Write a byte to the output file, using eight calls to putbit().
###############################   PutByte   ###############################
*/
putbyte(byt, file)
int byt, file;
{   int retval, i;

    for (i = 7; i >= 0; i--) {
        retval = putbit((byt & (1 << i)) ? 1 : 0, file);
        if (retval != noErr) return retval;
    }
    return noErr;
}

/*
Initialize various status variables before outputting a compressed file.
#############################   Init_Output   #############################
*/
void init_output()
{
    pit_index = 0;
    BitCount  = 0;
    OutChar   = (char) 0;
}

/*
Add needed bit padding and empty buffer after outputting a compressed file.
#############################   Flush_Output   ############################
*/
int flush_output(file)
int file;
{   int  errcode;
    long Count;

    while (BitCount) { if (errcode = putbit(0, file)) return errcode; }
    if  (pit_index)  {
        Count = pit_index;
        return FSWrite(file, &Count, pit_buffer);
    }
    return noErr;
}


/*
Write a copy of the Huffman tree we're using for compression to pit_file.
##############################   Write_Tree   #############################
*/
int write_tree(root, file)
nodeptr   root;
int 	  file;
{   int   errno;
--- end of part 2 ---
Technical Details
Message-ID: <2278@felix.UUCP>
Path: arc1-ca.pugleaf.net!utzoo!utgpu!water!watnot!watmath!clyde!cuae2!ihnp4!ptsfa!lll-lcc!pyramid!hplabs!felix!macintosh
References: <2277@felix.UUCP>

Show Full Headers
Complete Article Headers
Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP
Path: arc1-ca.pugleaf.net!utzoo!utgpu!water!watnot!watmath!clyde!cuae2!ihnp4!ptsfa!lll-lcc!pyramid!hplabs!felix!macintosh
From: macintosh@felix.UUCP
Newsgroups: mod.mac.sources
Subject: Unpit version 0.3.1 Source (part 2 of 3)
Message-ID: <2278@felix.UUCP>
Date: Fri, 13-Feb-87 02:41:22 EST
Article-I.D.: felix.2278
Posted: Fri Feb 13 02:41:22 1987
Date-Received: Sat, 14-Feb-87 14:53:04 EST
References: <2277@felix.UUCP>
Sender: macintosh@felix.UUCP
Reply-To: macintosh@felix.UUCP (The Moderator)
Organization: FileNet Corp., Costa Mesa, Ca.
Lines: 603
Approved: bytebug@felix.UUCP (Roger L. Long)