Date : Wed, 27 Nov 1991 02:54:06 GMT
From : cis.ohio-state.edu!zaphod.mps.ohio-state.edu!think.com!spool.mu.edu!munnari.oz.au!newsroom.utas.edu.au!bruny.cc.utas.edu.au!u895217@ucbvax.Berkeley.EDU (Scott Marshall)
Subject: Re: LBR files on MS-DOS
arj@cam-orl.co.uk (Andy Jackson) writes:
>arj@cam-orl.co.uk (Andy Jackson) writes:
>: Sorry if this is the wrong place to ask.....
>:
>: I have recently aquired some files (archives ?) in .LBR format. Is there
>: any way that I can get at the contents on a PC.
>:
>Sorry about the waste of bandwidth...
> I found a copy of delbr11.exe on wuarchive.wustl.edu. This
>extracted the files, but now I have a what appear to be a number of
>compressed files. They have all had the middle letter of the extension
>converted to a Z. i.e.
> AAAAAAA.ASM => AAAAAAA.AZM
>What do I need to extract these now ???.
A program to do this is following:
/*----------------------------------------------------------------------------*/
/* UNCRunch/C - LZW uncruncher compatible with Z-80 CP/M program CRUNCH
2.3 */
/* (C) Copyright 1986 - Frank Prindle */
/* May be reproduced for non-profit use only */
/*----------------------------------------------------------------------------*/
/* This program is largely based on the previous works of Steven Greenberg,
*/
/* Joe Orost, James Woods, Ken Turkowski, Steve Davies, Jim McKie, Spencer
*/
/* Thomas, Terry Welch, and (of course) Lempel and Ziv. */
/*----------------------------------------------------------------------------*/
/* Portability related assumptions: */
/* 1. fopen called with "rb" or "wb" is sufficient to make getc/putc
xfer */
/* bytes in binary, without newline or control-z translation (this
is */
/* default for UNIX anyway, and the "b" has no effect).
*/
/* 2. size of type "long" is at least 4 bytes (for getbuf to work).
*/
/*----------------------------------------------------------------------------*/
#define VERSION "2.31D"
#define DATE "11 June 1987"
/*#define DUMBLINKER*/ /*define this if linker makes a huge executable image*/
/*containing mostly zeros (big tables will instead be*/
/*allocated at run time)*/
#define XLAT_FN_CHARS {'/','%'} /*FOR UNIX*/
/*define comma separated pairs of {undesirable char,*/
/*replacement char} for filenames - no need to include*/
/*control characters, as these are automatically*/
/*translated to question marks (?)*/
/*----------------------------------------------------------------------------*/
#include "stdio.h"
/*Macro definition - ensure letter is lower case*/
#define tolower(c) (((c)>='A' && (c)<='Z')?(c)-('A'-'a'):(c))
/*Macro definition - is character a control (non-printing) character?*/
#define iscntrl(c) (c<' ' || c=='\177')
#define TABLE_SIZE 4096 /*size of main lzw table for 12 bit codes*/
#define XLATBL_SIZE 5003 /*size of physical translation table*/
/*special values for predecessor in table*/
#define NOPRED 0x6fff /*no predecessor in table*/
#define EMPTY 0x8000 /*empty table entry (xlatbl only)*/
#define REFERENCED 0x2000 /*table entry referenced if this bit set*/
#define IMPRED 0x7fff /*impossible predecessor*/
#define EOFCOD 0x100 /*special code for end-of-file*/
#define RSTCOD 0x101 /*special code for adaptive reset*/
#define NULCOD 0x102 /*special filler code*/
#define SPRCOD 0x103 /*spare special code*/
#define REPEAT_CHARACTER 0x90 /*following byte is repeat count*/
#ifdef DUMBLINKER
/*main lzw table and it's structure*/
struct entry {
short predecessor; /*index to previous entry, if any*/
char suffix; /*character suffixed to previous entries*/
} *table;
/*auxilliary physical translation table*/
/*translates hash to main table index*/
short *xlatbl;
/*byte string stack used by decode*/
char *stack;
#else
struct entry {
short predecessor; /*index to previous entry, if any*/
char suffix; /*character suffixed to previous entries*/
} table[TABLE_SIZE];
/*auxilliary physical translation table*/
/*translates hash to main table index*/
short xlatbl[XLATBL_SIZE];
/*byte string stack used by decode*/
char stack[TABLE_SIZE];
#endif
/*other global variables*/
char codlen; /*variable code length in bits (9-12)*/
short trgmsk; /*mask for codes of current length*/
char fulflg; /*full flag - set once main table is full*/
short entry; /*next available main table entry*/
long getbuf; /*buffer used by getcode*/
short getbit; /*residual bit counter used by getcode*/
char entflg; /*inhibit main loop from entering this code*/
char repeat_flag; /*so send can remember if repeat required*/
int finchar; /*first character of last substring output*/
int lastpr; /*last predecessor (in main loop)*/
short cksum; /*checksum of all bytes written to output file*/
FILE *infd; /*currently open input file*/
FILE *outfd; /*currently open output file*/
main(argc,argv)
int argc;
char *argv[];
{
/*usage check*/
if (argc-- < 2)
{
printf("Usage : uncr <crunched_file_name> ...\n");
exit(0);
}
/*who am i*/
printf("UNCRunch/C Version %s (%s)\n\n",VERSION,DATE);
#ifdef DUMBLINKER
/*allocate storage now for the big tables (keeps load short)*/
table=(struct entry *)malloc(TABLE_SIZE*sizeof(struct entry));
xlatbl=(short *)malloc(XLATBL_SIZE*sizeof(short));
stack=(char *)malloc(TABLE_SIZE*sizeof(char));
if(table==NULL || xlatbl==NULL || stack==NULL)
{
printf("***** not enough memory to run this program!\n");
exit(0);
}
#endif
/*do all the files specified*/
while(argc--) uncrunch(*++argv);
/*all done all files*/
exit(0);
}
/*uncrunch a single file*/
uncrunch(filename)
char *filename;
{
int c;
char *p;
char *index();
char outfn[80]; /*space to build output file name*/
int pred; /*current predecessor (in main loop)*/
char reflevel; /*ref rev level from input file*/
char siglevel; /*sig rev level from input file*/
char errdetect; /*error detection flag from input file*/
short file_cksum; /*checksum read from input file*/
/*filename character translation array*/
static int xlat_fn_chars[][2] = {XLAT_FN_CHARS, {-1,-1}};
/*initialize variables for uncrunching a file*/
intram();
/*open input file*/
if ( 0 == (infd = fopen(filename,"rb")) )
{
printf("***** can't open %s\n", filename);
return;
}
/*verify this is a crunched file*/
if (getc(infd) != 0x76 || getc(infd) != 0xfe)
{
printf("***** %s is not a crunched file\n",filename);
fclose(infd);
return;
}
/*extract and build output file name*/
printf("%s --> ",filename);
for(p=outfn; (*p=getc(infd))!='\0'; p++)
{
int *q;
/*translate to lower case and strip high order bit*/
*p=tolower((*p)&0x7f);
/*make any control character into a question mark*/
if(iscntrl(*p)) *p = '?';
/*translate undesirable characters in file name*/
for(q = &xlat_fn_chars[0][0]; *q!=(-1); q+=2)
{
if(*p == *q)
{
*p = *(q+1);
break;
}
}
}
printf("%s\n",outfn);
*(index(outfn,'.')+4)='\0'; /*truncate non-name portion*/
if(p=index(outfn,' ')) *p='\0'; /*truncate trailing blanks*/
/*read the four info bytes*/
reflevel=getc(infd);
siglevel=getc(infd);
errdetect=getc(infd);
getc(infd); /*skip spare*/
/*make sure we can uncrunch this format file*/
/*note: this program does not support CRUNCH 1.x format*/
if(siglevel < 0x20 || siglevel > 0x2f)
{
printf("***** this version of UNCR cannot process %s!\n",
filename);
fclose(infd);
return;
}
/*open output file*/
if ( 0 == (outfd =fopen( outfn,"wb")) )
{
printf("***** can't create %s\n",outfn);
fclose(infd);
return;
}
/*set up atomic code definitions*/
initb2();
/*main decoding loop*/
pred=NOPRED;
for(;;)
{
/*remember last predecessor*/
lastpr=pred;
/*read and process one code*/
if((pred=getcode())==EOFCOD) /*end-of-file code*/
{
break; /*all lzw codes read*/
}
else if(pred==RSTCOD) /*reset code*/
{
entry=0;
fulflg=0;
codlen=9;
trgmsk=0x1ff;
pred=NOPRED;
entflg=1;
initb2();
}
else /*a normal code (nulls already deleted)*/
{
/*check for table full*/
if(fulflg!=2)
{
/*strategy if table not full*/
if(decode(pred)==0)enterx(lastpr,finchar);
else entflg=0;
}
else
{
/*strategy if table is full*/
decode(pred);
entfil(lastpr,finchar); /*attempt to reassign*/
}
}
}
/*verify checksum if required*/
if(errdetect==0)
{
file_cksum=getc(infd);
file_cksum|=getc(infd)<<8;
if((file_cksum&0xffff)!=(cksum&0xffff))
{
printf("***** checksum error detected in ");
printf("%s!\n",filename);
}
}
/*close files*/
fclose(infd);
fclose(outfd);
/*all done this file*/
return;
}
/*initialize the lzw and physical translation tables*/
initb2()
{
register int i;
register struct entry *p;
p=table;
/*first mark all entries of xlatbl as empty*/
for(i=0;i<XLATBL_SIZE;i++) xlatbl[i]=EMPTY;
/*enter atomic and reserved codes into lzw table*/
for(i=0;i<0x100;i++) enterx(NOPRED,i); /*first 256 atomic codes*/
for(i=0;i<4;i++) enterx(IMPRED,0); /*reserved codes*/
}
/*enter the next code into the lzw table*/
enterx(pred,suff)
int pred; /*table index of predecessor*/
int suff; /*suffix byte represented by this entry*/
{
register struct entry *ep;
ep = &table[entry];
/*update xlatbl to point to this entry*/
figure(pred,suff);
/*make the new entry*/
ep->predecessor = (short)pred;
ep->suffix = (char)suff;
entry++;
/*if only one entry of the current code length remains, update to*/
/*next code length because main loop is reading one code ahead*/
if(entry >= trgmsk)
{
if(codlen<12)
{
/*table not full, just make length one more bit*/
codlen++;
trgmsk=(trgmsk<<1)|1;
}
else
{
/*table almost full (fulflg==0) or full (fulflg==1)*/
/*just increment fulflg - when it gets to 2 we will*/
/*never be called again*/
fulflg++;
}
}
}
/*find an empty entry in xlatbl which hashes from this predecessor/suffix*/
/*combo, and store the index of the next available lzw table entry in it*/
figure(pred,suff)
int pred;
int suff;
{
short *hash();
auto int disp;
register short *p;
p=hash(pred,suff,&disp);
/*follow secondary hash chain as necessary to find an empty slot*/
while(((*p)&0xffff) != EMPTY)
{
p+=disp;
if(p<xlatbl)p+=XLATBL_SIZE;
}
/*stuff next available index into this slot*/
*p=entry;
}
/*hash pred/suff into xlatbl pointer*/
/*duplicates the hash algorithm used by CRUNCH 2.3*/
short *hash(pred,suff,disploc)
int pred;
int suff;
int *disploc;
{
register int hashval;
hashval=(((pred>>4)^suff) | ((pred&0xf)<<8)) + 1;
*disploc=hashval-XLATBL_SIZE;
return (xlatbl + hashval);
}
/*initialize variables for each file to be uncrunched*/
intram()
{
trgmsk=0x1ff; /*nine bits*/
codlen=9; /* " */
fulflg=0; /*table empty*/
entry=0; /* " */
getbit=0; /*buffer emtpy*/
entflg=1; /*first code always atomic*/
repeat_flag=0; /*repeat not active*/
cksum=0; /*zero checsum*/
}
/*return a code of length "codlen" bits from the input file bit-stream*/
getcode()
{
register int hole;
int code;
/*always get at least a byte*/
getbuf=(getbuf<<codlen)|(((long)getc(infd))<<(hole=codlen-getbit));
getbit=8-hole;
/*if is not enough to supply codlen bits, get another byte*/
if(getbit<0)
{
getbuf |= ((long)getc(infd))<<(hole-8);
getbit+=8;
}
if(feof(infd))
{
printf("***** Unexpected EOF on input file!\n");
return EOFCOD;
}
/*skip spare or null codes*/
if((code=((getbuf>>8) & trgmsk)) == NULCOD || code == SPRCOD)
{
return getcode(); /*skip this code, get next*/
}
return code;
}
/*decode this code*/
decode(code)
short code;
{
register char *stackp; /*byte string stack pointer*/
register struct entry *ep;
ep = &table[code];
if(code>=entry)
{
/*the ugly exception, "WsWsW"*/
entflg=1;
enterx(lastpr,finchar);
}
/*mark corresponding table entry as referenced*/
ep->predecessor |= REFERENCED;
/*walk back the lzw table starting with this code*/
stackp=stack;
while(ep > &table[255]) /*i.e. code not atomic*/
{
*stackp++ = ep->suffix;
ep = &table[(ep->predecessor)&0xfff];
}
/*then emit all bytes corresponding to this code in forward order*/
send(finchar=(ep->suffix)&0xff); /*first byte*/
while(stackp > stack) /*the rest*/
{
send((*--stackp)&0xff);
}
return(entflg);
}
/*write a byte to output file*/
/*repeat byte (0x90) expanded here*/
/*checksumming of output stream done here*/
send(c)
register int c;
{
static int savec; /*previous byte put to output*/
/*repeat flag may have been set by previous call*/
if(repeat_flag)
{
/*repeat flag was set - emit (c-1) copies of savec*/
/*or (if c is zero), emit the repeat byte itself*/
repeat_flag=0;
if(c)
{
cksum+=(savec)*(c-1);
while(--c) putc(savec,outfd);
}
else
{
putc(REPEAT_CHARACTER,outfd);
cksum+=REPEAT_CHARACTER;
}
}
else
{
/*normal case - emit c or set repeat flag*/
if(c==REPEAT_CHARACTER)
{
repeat_flag++;
}
else
{
putc(savec=c,outfd);
cksum+=(c&0xff);
}
}
}
/*attempt to reassign an existing code which has*/
/*been defined, but never referenced*/
entfil(pred,suff)
int pred; /*table index of predecessor*/
int suff; /*suffix byte represented by this entry*/
{
auto int disp;
register struct entry *ep;
short *hash();
short *p;
p=hash(pred,suff,&disp);
/*search the candidate codes (all those which hash from this new*/
/*predecessor and suffix) for an unreferenced one*/
while(*p!=(short)EMPTY)
{
/*candidate code*/
ep = &table[*p];
if(((ep->predecessor)&REFERENCED)==0)
{
/*entry reassignable, so do it!*/
ep->predecessor=pred;
ep->suffix=suff;
/*discontinue search*/
break;
}
/*candidate unsuitable - follow secondary hash chain*/
/*and keep searching*/
p+=disp;
if(p<xlatbl)p+=XLATBL_SIZE;
}
}
/*end of source file*/