2 * LZ Decompression functions
4 * Copyright 1996 Marcus Meissner
7 * FIXME: return values might be wrong
15 #include <sys/types.h>
20 #include "stackframe.h"
25 /* The readahead length of the decompressor. Reading single bytes
26 * using _lread() would be SLOW.
30 /* Format of first 14 byte of LZ compressed file */
37 static BYTE LZMagic
[8]={'S','Z','D','D',0x88,0xf0,0x27,0x33};
39 static struct lzstate
{
40 HFILE lzfd
; /* the handle used by the program */
41 HFILE realfd
; /* the real filedescriptor */
42 CHAR lastchar
; /* the last char of the filename */
44 DWORD reallength
; /* the decompressed length of the file */
45 DWORD realcurrent
; /* the position the decompressor currently is */
46 DWORD realwanted
; /* the position the user wants to read from */
48 BYTE table
[0x1000]; /* the rotating LZ table */
49 UINT curtabent
; /* CURrent TABle ENTry */
51 BYTE stringlen
; /* length and position of current string */
52 DWORD stringpos
; /* from stringtable */
55 WORD bytetype
; /* bitmask within blocks */
57 BYTE
*get
; /* GETLEN bytes */
58 DWORD getcur
; /* current read */
59 DWORD getlen
; /* length last got */
61 static int nroflzstates
=0;
63 /* reads one compressed byte, including buffering */
64 #define GET(lzs,b) _lzget(lzs,&b)
65 #define GET_FLUSH(lzs) lzs->getcur=lzs->getlen;
68 _lzget(struct lzstate
*lzs
,BYTE
*b
) {
69 if (lzs
->getcur
<lzs
->getlen
) {
70 *b
= lzs
->get
[lzs
->getcur
++];
73 int ret
= FILE_Read(lzs
->realfd
,lzs
->get
,GETLEN
);
84 /* internal function, reads lzheader
85 * returns BADINHANDLE for non filedescriptors
86 * return 0 for file not compressed using LZ
87 * return UNKNOWNALG for unknown algorithm
88 * returns lzfileheader in *head
91 read_header(HFILE fd
,struct lzfileheader
*head
) {
94 if (_llseek(fd
,0,SEEK_SET
)==-1)
95 return LZERROR_BADINHANDLE
;
97 /* We can't directly read the lzfileheader struct due to
98 * structure element alignment
100 if (FILE_Read(fd
,buf
,14)<14)
102 memcpy(head
->magic
,buf
,8);
103 memcpy(&(head
->compressiontype
),buf
+8,1);
104 memcpy(&(head
->lastchar
),buf
+9,1);
106 /* FIXME: consider endianess on non-intel architectures */
107 memcpy(&(head
->reallength
),buf
+10,4);
109 if (memcmp(head
->magic
,LZMagic
,8))
111 if (head
->compressiontype
!='A')
112 return LZERROR_UNKNOWNALG
;
116 * LZSTART [LZEXPAND.7]
120 dprintf_file(stddeb
,"LZStart(void)\n");
125 * LZINIT [LZEXPAND.3]
127 * initializes internal decompression buffers, returns lzfiledescriptor.
128 * (return value the same as hfSrc, if hfSrc is not compressed)
129 * on failure, returns error code <0
130 * lzfiledescriptors range from 0x400 to 0x410 (only 16 open files per process)
131 * we use as much as we need, we just OR 0x400 to the passed HFILE.
133 * since _llseek uses the same types as libc.lseek, we just use the macros of
137 LZInit(HFILE hfSrc
) {
138 struct lzfileheader head
;
142 dprintf_file(stddeb
,"LZInit(%d)\n",hfSrc
);
143 ret
=read_header(hfSrc
,&head
);
145 _llseek(hfSrc
,0,SEEK_SET
);
146 return ret
?ret
:hfSrc
;
148 lzstates
=xrealloc(lzstates
,(++nroflzstates
)*sizeof(struct lzstate
));
149 lzs
= lzstates
+(nroflzstates
-1);
151 memset(lzs
,'\0',sizeof(*lzs
));
153 lzs
->lzfd
= hfSrc
| 0x400;
154 lzs
->lastchar
= head
.lastchar
;
155 lzs
->reallength
= head
.reallength
;
157 lzs
->get
= xmalloc(GETLEN
);
161 /* Yes, preinitialize with spaces */
162 memset(lzs
->table
,' ',0x1000);
163 /* Yes, start 16 byte from the END of the table */
164 lzs
->curtabent
= 0xff0;
169 * LZDone [LZEXPAND.9]
174 dprintf_file(stddeb
,"LZDone()\n");
178 * GetExpandedName [LZEXPAND.10]
180 * gets the full filename of the compressed file 'in' by opening it
181 * and reading the header
183 * "file." is being translated to "file"
184 * "file.bl_" (with lastchar 'a') is being translated to "file.bla"
185 * "FILE.BL_" (with lastchar 'a') is being translated to "FILE.BLA"
189 GetExpandedName(LPCSTR in
,LPSTR out
) {
190 struct lzfileheader head
;
193 INT fnislowercased
,ret
,len
;
196 dprintf_file(stddeb
,"GetExpandedName(%s)\n",in
);
197 fd
=OpenFile(in
,&ofs
,OF_READ
);
199 return LZERROR_BADINHANDLE
;
200 ret
=read_header(fd
,&head
);
203 return LZERROR_BADINHANDLE
;
206 /* This line will crash if the caller hasn't allocated enough memory
211 /* look for directory prefix and skip it. */
213 while (NULL
!=(t
=strpbrk(s
,"/\\:")))
216 /* now mangle the basename */
218 /* FIXME: hmm. shouldn't happen? */
219 fprintf(stddeb
,__FILE__
":GetExpandedFileName(), specified a directory or what? (%s)\n",in
);
223 /* see if we should use lowercase or uppercase on the last char */
231 fnislowercased
=islower(*t
);
234 if (isalpha(head
.lastchar
)) {
236 head
.lastchar
=tolower(head
.lastchar
);
238 head
.lastchar
=toupper(head
.lastchar
);
241 /* now look where to replace the last character */
242 if (NULL
!=(t
=strchr(s
,'.'))) {
248 t
[len
]=head
.lastchar
;
250 } /* else no modification necessary */
256 * LZRead [LZEXPAND.5]
257 * just as normal read, but reads from LZ special fd and uncompresses.
260 LZRead(HFILE fd
,SEGPTR segbuf
,WORD toread
) {
266 dprintf_file(stddeb
,"LZRead(%d,%08lx,%d)\n",fd
,(DWORD
)segbuf
,toread
);
268 for (i
=0;i
<nroflzstates
;i
++)
269 if (lzstates
[i
].lzfd
==fd
)
272 return _lread(fd
,segbuf
,toread
);
276 /* The decompressor itself is in a define, cause we need it twice
277 * in this function. (the decompressed byte will be in b)
279 #define DECOMPRESS_ONE_BYTE \
280 if (lzs->stringlen) { \
281 b = lzs->table[lzs->stringpos]; \
282 lzs->stringpos = (lzs->stringpos+1)&0xFFF; \
285 if (!(lzs->bytetype&0x100)) { \
287 return toread-howmuch; \
288 lzs->bytetype = b|0xFF00; \
290 if (lzs->bytetype & 1) { \
292 return toread-howmuch; \
296 if (1!=GET(lzs,b1)) \
297 return toread-howmuch; \
298 if (1!=GET(lzs,b2)) \
299 return toread-howmuch; \
303 * where CAB is the stringoffset in the table\
304 * and D+3 is the len of the string \
306 lzs->stringpos = b1|((b2&0xf0)<<4); \
307 lzs->stringlen = (b2&0xf)+2; \
308 /* 3, but we use a byte already below ... */\
309 b = lzs->table[lzs->stringpos];\
310 lzs->stringpos = (lzs->stringpos+1)&0xFFF;\
314 /* store b in table */ \
315 lzs->table[lzs->curtabent++]= b; \
316 lzs->curtabent &= 0xFFF; \
319 /* if someone has seeked, we have to bring the decompressor
322 if (lzs
->realcurrent
!=lzs
->realwanted
) {
323 /* if the wanted position is before the current position
324 * I see no easy way to unroll ... We have to restart at
325 * the beginning. *sigh*
327 if (lzs
->realcurrent
>lzs
->realwanted
) {
328 /* flush decompressor state */
329 _llseek(lzs
->realfd
,14,SEEK_SET
);
334 memset(lzs
->table
,' ',0x1000);
335 lzs
->curtabent
= 0xFF0;
337 while (lzs
->realcurrent
<lzs
->realwanted
) {
342 buf
=PTR_SEG_TO_LIN(segbuf
);
350 #undef DECOMPRESS_ONE_BYTE
354 * LZSeek [LZEXPAND.4]
356 * works as the usual _llseek
360 LZSeek(HFILE fd
,LONG off
,INT type
) {
363 LONG lastwanted
,newwanted
;
365 dprintf_file(stddeb
,"LZSeek(%d,%ld,%d)\n",fd
,off
,type
);
366 for (i
=0;i
<nroflzstates
;i
++)
367 if (lzstates
[i
].lzfd
==fd
)
369 /* not compressed? just use normal _llseek() */
371 return _llseek(fd
,off
,type
);
373 lastwanted
= lzs
->realwanted
;
374 newwanted
= lzs
->realwanted
;
376 case 1: /* SEEK_CUR */
379 case 2: /* SEEK_END */
380 newwanted
= lzs
->reallength
-off
;
382 default:/* SEEK_SET */
386 if (newwanted
>lzs
->reallength
)
387 return LZERROR_BADVALUE
;
389 return LZERROR_BADVALUE
;
390 lzs
->realwanted
= newwanted
;
395 * LZCopy [LZEXPAND.1]
397 * Copies everything from src to dest
398 * if src is a LZ compressed file, it will be uncompressed.
399 * will return the number of bytes written to dest or errors.
402 LZCopy(HFILE src
,HFILE dest
) {
407 INT (*xread
)(HFILE
,SEGPTR
,WORD
);
409 dprintf_file(stddeb
,"LZCopy(%d,%d)\n",src
,dest
);
410 for (i
=0;i
<nroflzstates
;i
++)
411 if (src
==lzstates
[i
].lzfd
)
414 /* not compressed? just copy */
421 ret
=xread(src
,MAKE_SEGPTR(buf
),BUFLEN
);
430 wret
= _lwrite(dest
,buf
,ret
);
432 return LZERROR_WRITE
;
439 * LZOpenFile [LZEXPAND.2]
440 * Opens a file. If not compressed, open it as a normal file.
443 LZOpenFile(LPCSTR fn
,LPOFSTRUCT ofs
,UINT mode
) {
446 dprintf_file(stddeb
,"LZOpenFile(%s,%p,%d)\n",fn
,ofs
,mode
);
447 /* 0x70 represents all OF_SHARE_* flags, ignore them for the check */
448 fd
=OpenFile(fn
,ofs
,mode
);
449 if ((mode
&~0x70)!=OF_READ
)
460 * LZClose [LZEXPAND.6]
466 dprintf_file(stddeb
,"LZClose(%d)\n",fd
);
467 for (i
=0;i
<nroflzstates
;i
++)
468 if (lzstates
[i
].lzfd
==fd
)
470 if (i
==nroflzstates
) {
475 free(lzstates
[i
].get
);
476 _lclose(lzstates
[i
].realfd
);
477 memcpy(lzstates
+i
,lzstates
+i
+1,sizeof(struct lzstate
)*(nroflzstates
-i
-1));
479 lzstates
=xrealloc(lzstates
,sizeof(struct lzstate
)*nroflzstates
);
483 * CopyLZFile [LZEXPAND.8]
485 * Copy src to dest (including uncompressing src).
486 * NOTE: Yes. This is exactly the same function as LZCopy.
489 CopyLZFile(HFILE src
,HFILE dest
) {
490 dprintf_file(stddeb
,"CopyLZFile(%d,%d)\n",src
,dest
);
491 return LZCopy(src
,dest
);