/* * LZ Decompression functions * * Copyright 1996 Marcus Meissner */ /* * FIXME: return values might be wrong */ #include #include #include #include #include #include #include #include "windows.h" #include "file.h" #include "ldt.h" #include "lzexpand.h" #include "stddebug.h" #include "debug.h" #include "xmalloc.h" #include "string32.h" #define strdupW2A(x) STRING32_DupUniToAnsi(x) #define strdupA2W(x) STRING32_DupAnsiToUni(x) #define strcpyAW(a,b) STRING32_AnsiToUni(a,b) /* The readahead length of the decompressor. Reading single bytes * using _lread() would be SLOW. */ #define GETLEN 2048 /* Format of first 14 byte of LZ compressed file */ struct lzfileheader { BYTE magic[8]; BYTE compressiontype; CHAR lastchar; DWORD reallength; }; static BYTE LZMagic[8]={'S','Z','D','D',0x88,0xf0,0x27,0x33}; static struct lzstate { HFILE lzfd; /* the handle used by the program */ HFILE realfd; /* the real filedescriptor */ CHAR lastchar; /* the last char of the filename */ DWORD reallength; /* the decompressed length of the file */ DWORD realcurrent; /* the position the decompressor currently is */ DWORD realwanted; /* the position the user wants to read from */ BYTE table[0x1000]; /* the rotating LZ table */ UINT curtabent; /* CURrent TABle ENTry */ BYTE stringlen; /* length and position of current string */ DWORD stringpos; /* from stringtable */ WORD bytetype; /* bitmask within blocks */ BYTE *get; /* GETLEN bytes */ DWORD getcur; /* current read */ DWORD getlen; /* length last got */ } *lzstates=NULL; static int nroflzstates=0; /* reads one compressed byte, including buffering */ #define GET(lzs,b) _lzget(lzs,&b) #define GET_FLUSH(lzs) lzs->getcur=lzs->getlen; static int _lzget(struct lzstate *lzs,BYTE *b) { if (lzs->getcurgetlen) { *b = lzs->get[lzs->getcur++]; return 1; } else { int ret = _lread32(lzs->realfd,lzs->get,GETLEN); if (ret==HFILE_ERROR) return HFILE_ERROR; if (ret==0) return 0; lzs->getlen = ret; lzs->getcur = 1; *b = *(lzs->get); return 1; } } /* internal function, reads lzheader * returns BADINHANDLE for non filedescriptors * return 0 for file not compressed using LZ * return UNKNOWNALG for unknown algorithm * returns lzfileheader in *head */ static INT read_header(HFILE fd,struct lzfileheader *head) { BYTE buf[14]; if (_llseek(fd,0,SEEK_SET)==-1) return LZERROR_BADINHANDLE; /* We can't directly read the lzfileheader struct due to * structure element alignment */ if (_lread32(fd,buf,14)<14) return 0; memcpy(head->magic,buf,8); memcpy(&(head->compressiontype),buf+8,1); memcpy(&(head->lastchar),buf+9,1); /* FIXME: consider endianess on non-intel architectures */ memcpy(&(head->reallength),buf+10,4); if (memcmp(head->magic,LZMagic,8)) return 0; if (head->compressiontype!='A') return LZERROR_UNKNOWNALG; return 1; } /* * LZStart [LZEXPAND.7] [LZ32.6] */ INT16 LZStart(void) { dprintf_file(stddeb,"LZStart(void)\n"); return 1; } /* * LZInit [LZEXPAND.3] [LZ32.2] * * initializes internal decompression buffers, returns lzfiledescriptor. * (return value the same as hfSrc, if hfSrc is not compressed) * on failure, returns error code <0 * lzfiledescriptors range from 0x400 to 0x410 (only 16 open files per process) * we use as much as we need, we just OR 0x400 to the passed HFILE. * * since _llseek uses the same types as libc.lseek, we just use the macros of * libc */ HFILE LZInit(HFILE hfSrc) { struct lzfileheader head; struct lzstate *lzs; DWORD ret; dprintf_file(stddeb,"LZInit(%d)\n",hfSrc); ret=read_header(hfSrc,&head); if (ret<=0) { _llseek(hfSrc,0,SEEK_SET); return ret?ret:hfSrc; } lzstates=xrealloc(lzstates,(++nroflzstates)*sizeof(struct lzstate)); lzs = lzstates+(nroflzstates-1); memset(lzs,'\0',sizeof(*lzs)); lzs->realfd = hfSrc; lzs->lzfd = hfSrc | 0x400; lzs->lastchar = head.lastchar; lzs->reallength = head.reallength; lzs->get = xmalloc(GETLEN); lzs->getlen = 0; lzs->getcur = 0; /* Yes, preinitialize with spaces */ memset(lzs->table,' ',0x1000); /* Yes, start 16 byte from the END of the table */ lzs->curtabent = 0xff0; return lzs->lzfd; } /* * LZDone [LZEXPAND.9] [LZ32.8] */ void LZDone(void) { dprintf_file(stddeb,"LZDone()\n"); } /* * GetExpandedName [LZEXPAND.10] * * gets the full filename of the compressed file 'in' by opening it * and reading the header * * "file." is being translated to "file" * "file.bl_" (with lastchar 'a') is being translated to "file.bla" * "FILE.BL_" (with lastchar 'a') is being translated to "FILE.BLA" */ INT16 GetExpandedName16(LPCSTR in,LPSTR out) { struct lzfileheader head; HFILE fd; OFSTRUCT ofs; INT fnislowercased,ret,len; LPSTR s,t; dprintf_file(stddeb,"GetExpandedName(%s)\n",in); fd=OpenFile(in,&ofs,OF_READ); if (fd==HFILE_ERROR) return LZERROR_BADINHANDLE; ret=read_header(fd,&head); if (ret<=0) { _lclose(fd); return LZERROR_BADINHANDLE; } /* This line will crash if the caller hasn't allocated enough memory * for us. */ strcpy(out,in); /* look for directory prefix and skip it. */ s=out; while (NULL!=(t=strpbrk(s,"/\\:"))) s=t+1; /* now mangle the basename */ if (!*s) { /* FIXME: hmm. shouldn't happen? */ fprintf(stddeb,__FILE__":GetExpandedFileName(), specified a directory or what? (%s)\n",in); _lclose(fd); return 1; } /* see if we should use lowercase or uppercase on the last char */ fnislowercased=1; t=s+strlen(s)-1; while (t>=out) { if (!isalpha(*t)) { t--; continue; } fnislowercased=islower(*t); break; } if (isalpha(head.lastchar)) { if (fnislowercased) head.lastchar=tolower(head.lastchar); else head.lastchar=toupper(head.lastchar); } /* now look where to replace the last character */ if (NULL!=(t=strchr(s,'.'))) { if (t[1]=='\0') { t[0]='\0'; } else { len=strlen(t)-1; if (t[len]=='_') t[len]=head.lastchar; } } /* else no modification necessary */ _lclose(fd); return 1; } /* * GetExpandedNameW [LZ32.11] */ INT32 GetExpandedName32W(LPCWSTR in,LPWSTR out) { char *xin,*xout; INT32 ret; xout = malloc(lstrlen32W(in)+3); xin = strdupW2A(in); ret = GetExpandedName16(xin,xout); if (ret>0) strcpyAW(out,xout); free(xin); free(xout); return ret; } /* * GetExpandedNameA [LZ32.9] */ INT32 GetExpandedName32A(LPCSTR in,LPSTR out) { return GetExpandedName16(in,out); } /* * LZRead [LZEXPAND.5] [LZ32.4] * just as normal read, but reads from LZ special fd and uncompresses. */ INT16 LZRead16(HFILE fd,SEGPTR segbuf,UINT16 toread) { dprintf_file(stddeb,"LZRead16(%d,%08lx,%d)\n",fd,(DWORD)segbuf,toread); return LZRead32(fd,(LPBYTE)PTR_SEG_TO_LIN(segbuf),toread); } INT32 LZRead32(HFILE fd,LPVOID vbuf,UINT32 toread) { int i,howmuch; BYTE b,*buf; struct lzstate *lzs; buf=(LPBYTE)vbuf; dprintf_file(stddeb,"LZRead32(%d,%p,%d)\n",fd,buf,toread); howmuch=toread; for (i=0;istringlen) { \ b = lzs->table[lzs->stringpos]; \ lzs->stringpos = (lzs->stringpos+1)&0xFFF; \ lzs->stringlen--; \ } else { \ if (!(lzs->bytetype&0x100)) { \ if (1!=GET(lzs,b)) \ return toread-howmuch; \ lzs->bytetype = b|0xFF00; \ } \ if (lzs->bytetype & 1) { \ if (1!=GET(lzs,b)) \ return toread-howmuch; \ } else { \ BYTE b1,b2; \ \ if (1!=GET(lzs,b1)) \ return toread-howmuch; \ if (1!=GET(lzs,b2)) \ return toread-howmuch; \ /* Format: \ * b1 b2 \ * AB CD \ * where CAB is the stringoffset in the table\ * and D+3 is the len of the string \ */ \ lzs->stringpos = b1|((b2&0xf0)<<4); \ lzs->stringlen = (b2&0xf)+2; \ /* 3, but we use a byte already below ... */\ b = lzs->table[lzs->stringpos];\ lzs->stringpos = (lzs->stringpos+1)&0xFFF;\ } \ lzs->bytetype>>=1; \ } \ /* store b in table */ \ lzs->table[lzs->curtabent++]= b; \ lzs->curtabent &= 0xFFF; \ lzs->realcurrent++; /* if someone has seeked, we have to bring the decompressor * to that position */ if (lzs->realcurrent!=lzs->realwanted) { /* if the wanted position is before the current position * I see no easy way to unroll ... We have to restart at * the beginning. *sigh* */ if (lzs->realcurrent>lzs->realwanted) { /* flush decompressor state */ _llseek(lzs->realfd,14,SEEK_SET); GET_FLUSH(lzs); lzs->realcurrent= 0; lzs->bytetype = 0; lzs->stringlen = 0; memset(lzs->table,' ',0x1000); lzs->curtabent = 0xFF0; } while (lzs->realcurrentrealwanted) { DECOMPRESS_ONE_BYTE; } } while (howmuch) { DECOMPRESS_ONE_BYTE; lzs->realwanted++; *buf++ = b; howmuch--; } return toread; #undef DECOMPRESS_ONE_BYTE } /* * LZSeek [LZEXPAND.4] [LZ32.3] * * works as the usual _llseek */ LONG LZSeek(HFILE fd,LONG off,INT32 type) { int i; struct lzstate *lzs; LONG newwanted; dprintf_file(stddeb,"LZSeek(%d,%ld,%d)\n",fd,off,type); for (i=0;irealwanted; switch (type) { case 1: /* SEEK_CUR */ newwanted += off; break; case 2: /* SEEK_END */ newwanted = lzs->reallength-off; break; default:/* SEEK_SET */ newwanted = off; break; } if (newwanted>lzs->reallength) return LZERROR_BADVALUE; if (newwanted<0) return LZERROR_BADVALUE; lzs->realwanted = newwanted; return newwanted; } /* * LZCopy [LZEXPAND.1] [LZ32.0] * * Copies everything from src to dest * if src is a LZ compressed file, it will be uncompressed. * will return the number of bytes written to dest or errors. */ LONG LZCopy(HFILE src,HFILE dest) { int i,ret,wret; LONG len; #define BUFLEN 1000 BYTE buf[BUFLEN]; INT32 (*xread)(HFILE,LPVOID,UINT32); dprintf_file(stddeb,"LZCopy(%d,%d)\n",src,dest); for (i=0;iszPathName is an array with the OFSTRUCT */ yfn = strdupA2W(ofs->szPathName); memcpy(ofs->szPathName,yfn,lstrlen32W(yfn)*2+2); free(yfn); } return ret; } /* * LZClose [LZEXPAND.6] [LZ32.5] */ void LZClose(HFILE fd) { int i; dprintf_file(stddeb,"LZClose(%d)\n",fd); for (i=0;i