source: trunk/src/lz32/lz32.cpp@ 2306

Last change on this file since 2306 was 2306, checked in by sandervl, 26 years ago

JW: Updated def file to match NT 4 SP6

File size: 17.6 KB
Line 
1/* $Id: lz32.cpp,v 1.8 2000-01-03 10:25:57 sandervl Exp $ */
2
3/*
4 * Project Odin Software License can be found in LICENSE.TXT
5 *
6 * LZ Decompression functions
7 *
8 * Copyright 1996 Marcus Meissner
9 * Copyright 1999 Patrick Haller
10 *
11 * FIXME: return values might be wrong
12 */
13
14#include <string.h>
15#include <ctype.h>
16#include <odin.h>
17#include <os2sel.h>
18#include <odinwrap.h>
19#include <windef.h>
20#include <winbase.h>
21#include <heap.h>
22#include <heapstring.h>
23#include <lz32.h>
24#include "debugtools.h"
25#include <misc.h>
26
27ODINDEBUGCHANNEL(LZ32)
28
29#define SystemHeap GetProcessHeap()
30
31
32/****************************************************************************
33 * Defines *
34 ****************************************************************************/
35
36/* The readahead length of the decompressor. Reading single bytes
37 * using _lread() would be SLOW.
38 */
39#define GETLEN 2048
40
41
42/****************************************************************************
43 * Structures *
44 ****************************************************************************/
45
46/* Format of first 14 byte of LZ compressed file */
47struct lzfileheader {
48 BYTE magic[8];
49 BYTE compressiontype;
50 CHAR lastchar;
51 DWORD reallength;
52};
53static BYTE LZMagic[8]={'S','Z','D','D',0x88,0xf0,0x27,0x33};
54
55struct lzstate {
56 HFILE realfd; /* the real filedescriptor */
57 CHAR lastchar; /* the last char of the filename */
58
59 DWORD reallength; /* the decompressed length of the file */
60 DWORD realcurrent; /* the position the decompressor currently is */
61 DWORD realwanted; /* the position the user wants to read from */
62
63 BYTE table[0x1000]; /* the rotating LZ table */
64 UINT curtabent; /* CURrent TABle ENTry */
65
66 BYTE stringlen; /* length and position of current string */
67 DWORD stringpos; /* from stringtable */
68
69
70 WORD bytetype; /* bitmask within blocks */
71
72 BYTE *get; /* GETLEN bytes */
73 DWORD getcur; /* current read */
74 DWORD getlen; /* length last got */
75};
76
77#define MAX_LZSTATES 16
78static struct lzstate *lzstates[MAX_LZSTATES];
79
80#define IS_LZ_HANDLE(h) (((h) >= 0x400) && ((h) < 0x400+MAX_LZSTATES))
81#define GET_LZ_STATE(h) (IS_LZ_HANDLE(h) ? lzstates[(h)-0x400] : NULL)
82
83/* reads one compressed byte, including buffering */
84#define GET(lzs,b) _lzget(lzs,&b)
85#define GET_FLUSH(lzs) lzs->getcur=lzs->getlen;
86
87
88/****************************************************************************
89 * Internal Prototypes *
90 ****************************************************************************/
91
92static int _lzget(struct lzstate *lzs,BYTE *b);
93static INT read_header(HFILE fd,struct lzfileheader *head);
94
95
96/****************************************************************************
97 * Implementation *
98 ****************************************************************************/
99
100static int _lzget(struct lzstate *lzs,BYTE *b)
101{
102 if (lzs->getcur<lzs->getlen) {
103 *b = lzs->get[lzs->getcur++];
104 return 1;
105 } else {
106 int ret = _lread(lzs->realfd,lzs->get,GETLEN);
107 if (ret==HFILE_ERROR)
108 return HFILE_ERROR;
109 if (ret==0)
110 return 0;
111 lzs->getlen = ret;
112 lzs->getcur = 1;
113 *b = *(lzs->get);
114 return 1;
115 }
116}
117
118
119/* internal function, reads lzheader
120 * returns BADINHANDLE for non filedescriptors
121 * return 0 for file not compressed using LZ
122 * return UNKNOWNALG for unknown algorithm
123 * returns lzfileheader in *head
124 */
125static INT read_header(HFILE fd,struct lzfileheader *head)
126{
127 BYTE buf[14];
128
129 if (_llseek(fd,0,SEEK_SET)==-1)
130 return LZERROR_BADINHANDLE;
131
132 /* We can't directly read the lzfileheader struct due to
133 * structure element alignment
134 */
135 if (_lread(fd,buf,14)<14)
136 return 0;
137 memcpy(head->magic,buf,8);
138 memcpy(&(head->compressiontype),buf+8,1);
139 memcpy(&(head->lastchar),buf+9,1);
140
141 /* FIXME: consider endianess on non-intel architectures */
142 memcpy(&(head->reallength),buf+10,4);
143
144 if (memcmp(head->magic,LZMagic,8))
145 return 0;
146 if (head->compressiontype!='A')
147 return LZERROR_UNKNOWNALG;
148 return 1;
149}
150
151
152/***********************************************************************
153 * LZStart32 (LZ32.6)
154 */
155
156ODINFUNCTION0(INT,LZStart)
157{
158 dprintf(("LZ32: LZStart32()\n"));
159 return 1;
160}
161
162
163/***********************************************************************
164 * LZInit32 (LZ32.2)
165 *
166 * initializes internal decompression buffers, returns lzfiledescriptor.
167 * (return value the same as hfSrc, if hfSrc is not compressed)
168 * on failure, returns error code <0
169 * lzfiledescriptors range from 0x400 to 0x410 (only 16 open files per process)
170 *
171 * since _llseek uses the same types as libc.lseek, we just use the macros of
172 * libc
173 */
174ODINFUNCTION1(HFILE,LZInit,HFILE,hfSrc)
175{
176 struct lzfileheader head;
177 struct lzstate *lzs;
178 DWORD ret;
179 int i;
180
181 dprintf(("LZ32: LZInit(%08xh)\n",
182 hfSrc));
183
184 ret=read_header(hfSrc,&head);
185 if (ret<=0) {
186 _llseek(hfSrc,0,SEEK_SET);
187 return ret?ret:hfSrc;
188 }
189 for (i = 0; i < MAX_LZSTATES; i++) if (!lzstates[i]) break;
190 if (i == MAX_LZSTATES) return LZERROR_GLOBALLOC;
191
192 lzstates[i] = lzs = (lzstate*)HeapAlloc( SystemHeap, 0, sizeof(struct lzstate) );
193
194 memset(lzs,'\0',sizeof(*lzs));
195 lzs->realfd = hfSrc;
196 lzs->lastchar = head.lastchar;
197 lzs->reallength = head.reallength;
198
199 lzs->get = (BYTE*)HEAP_xalloc( GetProcessHeap(), 0, GETLEN );
200 lzs->getlen = 0;
201 lzs->getcur = 0;
202
203 /* Yes, preinitialize with spaces */
204 memset(lzs->table,' ',0x1000);
205 /* Yes, start 16 byte from the END of the table */
206 lzs->curtabent = 0xff0;
207 return 0x400 + i;
208}
209
210
211/***********************************************************************
212 * LZDone (LZEXPAND.9) (LZ32.8)
213 */
214ODINPROCEDURE0(LZDone)
215{
216 TRACE("(void)\n");
217}
218
219/***********************************************************************
220 * GetExpandedName32A (LZ32.9)
221 *
222 * gets the full filename of the compressed file 'in' by opening it
223 * and reading the header
224 *
225 * "file." is being translated to "file"
226 * "file.bl_" (with lastchar 'a') is being translated to "file.bla"
227 * "FILE.BL_" (with lastchar 'a') is being translated to "FILE.BLA"
228 */
229
230ODINFUNCTION2(INT,GetExpandedNameA,LPCSTR,in,LPSTR,out)
231{
232 struct lzfileheader head;
233 HFILE fd;
234 OFSTRUCT ofs;
235 INT fnislowercased,ret,len;
236 LPSTR s,t;
237
238 dprintf(("LZ32: GetExpandedNameA(%s,%08xh)\n",
239 in,
240 out));
241
242 fd=OpenFile(in,&ofs,OF_READ);
243 if (fd==HFILE_ERROR)
244 return (INT)(INT16)LZERROR_BADINHANDLE;
245 strcpy(out,in);
246 ret=read_header(fd,&head);
247 if (ret<=0) {
248 /* not a LZ compressed file, so the expanded name is the same
249 * as the input name */
250 _lclose(fd);
251 return 1;
252 }
253
254
255 /* look for directory prefix and skip it. */
256 s=out;
257 while (NULL!=(t=strpbrk(s,"/\\:")))
258 s=t+1;
259
260 /* now mangle the basename */
261 if (!*s) {
262 /* FIXME: hmm. shouldn't happen? */
263 WARN("Specified a directory or what? (%s)\n",in);
264 _lclose(fd);
265 return 1;
266 }
267 /* see if we should use lowercase or uppercase on the last char */
268 fnislowercased=1;
269 t=s+strlen(s)-1;
270 while (t>=out) {
271 if (!isalpha(*t)) {
272 t--;
273 continue;
274 }
275 fnislowercased=islower(*t);
276 break;
277 }
278 if (isalpha(head.lastchar)) {
279 if (fnislowercased)
280 head.lastchar=tolower(head.lastchar);
281 else
282 head.lastchar=toupper(head.lastchar);
283 }
284
285 /* now look where to replace the last character */
286 if (NULL!=(t=strchr(s,'.'))) {
287 if (t[1]=='\0') {
288 t[0]='\0';
289 } else {
290 len=strlen(t)-1;
291 if (t[len]=='_')
292 t[len]=head.lastchar;
293 }
294 } /* else no modification necessary */
295 _lclose(fd);
296 return 1;
297}
298
299
300/***********************************************************************
301 * GetExpandedName32W (LZ32.11)
302 */
303ODINFUNCTION2(INT,GetExpandedNameW,LPCWSTR,in,LPWSTR,out)
304{
305 char *xin,*xout;
306 INT ret;
307
308 dprintf(("LZ32: GetExpandedNameW(%08xh,%08xh)\n",
309 in,
310 out));
311
312 xout = (char*)HeapAlloc( GetProcessHeap(), 0, lstrlenW(in)+3 );
313 xin = HEAP_strdupWtoA( GetProcessHeap(), 0, in );
314 ret = GetExpandedNameA(xin,xout);
315 if (ret>0) lstrcpyAtoW(out,xout);
316 HeapFree( GetProcessHeap(), 0, xin );
317 HeapFree( GetProcessHeap(), 0, xout );
318 return ret;
319}
320
321
322/***********************************************************************
323 * LZRead32 (LZ32.4)
324 */
325ODINFUNCTION3(INT,LZRead,HFILE,fd,LPVOID,vbuf,UINT,toread)
326{
327 int howmuch;
328 BYTE b,*buf;
329 struct lzstate *lzs;
330
331 dprintf(("LZ32: LZRead(%08xh,%08xh,%08h)\n",
332 fd,
333 vbuf,
334 toread));
335
336 buf=(LPBYTE)vbuf;
337 howmuch=toread;
338 if (!(lzs = GET_LZ_STATE(fd))) return _lread(fd,buf,toread);
339
340/* The decompressor itself is in a define, cause we need it twice
341 * in this function. (the decompressed byte will be in b)
342 */
343#define DECOMPRESS_ONE_BYTE \
344 if (lzs->stringlen) { \
345 b = lzs->table[lzs->stringpos]; \
346 lzs->stringpos = (lzs->stringpos+1)&0xFFF; \
347 lzs->stringlen--; \
348 } else { \
349 if (!(lzs->bytetype&0x100)) { \
350 if (1!=GET(lzs,b)) \
351 return toread-howmuch; \
352 lzs->bytetype = b|0xFF00; \
353 } \
354 if (lzs->bytetype & 1) { \
355 if (1!=GET(lzs,b)) \
356 return toread-howmuch; \
357 } else { \
358 BYTE b1,b2; \
359 \
360 if (1!=GET(lzs,b1)) \
361 return toread-howmuch; \
362 if (1!=GET(lzs,b2)) \
363 return toread-howmuch; \
364 /* Format: \
365 * b1 b2 \
366 * AB CD \
367 * where CAB is the stringoffset in the table\
368 * and D+3 is the len of the string \
369 */ \
370 lzs->stringpos = b1|((b2&0xf0)<<4); \
371 lzs->stringlen = (b2&0xf)+2; \
372 /* 3, but we use a byte already below ... */\
373 b = lzs->table[lzs->stringpos];\
374 lzs->stringpos = (lzs->stringpos+1)&0xFFF;\
375 } \
376 lzs->bytetype>>=1; \
377 } \
378 /* store b in table */ \
379 lzs->table[lzs->curtabent++]= b; \
380 lzs->curtabent &= 0xFFF; \
381 lzs->realcurrent++;
382
383 /* if someone has seeked, we have to bring the decompressor
384 * to that position
385 */
386 if (lzs->realcurrent!=lzs->realwanted) {
387 /* if the wanted position is before the current position
388 * I see no easy way to unroll ... We have to restart at
389 * the beginning. *sigh*
390 */
391 if (lzs->realcurrent>lzs->realwanted) {
392 /* flush decompressor state */
393 _llseek(lzs->realfd,14,SEEK_SET);
394 GET_FLUSH(lzs);
395 lzs->realcurrent= 0;
396 lzs->bytetype = 0;
397 lzs->stringlen = 0;
398 memset(lzs->table,' ',0x1000);
399 lzs->curtabent = 0xFF0;
400 }
401 while (lzs->realcurrent<lzs->realwanted) {
402 DECOMPRESS_ONE_BYTE;
403 }
404 }
405
406 while (howmuch) {
407 DECOMPRESS_ONE_BYTE;
408 lzs->realwanted++;
409 *buf++ = b;
410 howmuch--;
411 }
412 return toread;
413#undef DECOMPRESS_ONE_BYTE
414}
415
416
417/***********************************************************************
418 * LZSeek32 (LZ32.3)
419 */
420
421ODINFUNCTION3(LONG,LZSeek,HFILE,fd,LONG,off,INT,type)
422{
423 struct lzstate *lzs;
424 LONG newwanted;
425
426 dprintf(("LZ32: LZSeek(%08xh,%08xh,%08xh)\n",
427 fd,
428 off,
429 type));
430
431 /* not compressed? just use normal _llseek() */
432 if (!(lzs = GET_LZ_STATE(fd))) return _llseek(fd,off,type);
433 newwanted = lzs->realwanted;
434 switch (type) {
435 case 1: /* SEEK_CUR */
436 newwanted += off;
437 break;
438 case 2: /* SEEK_END */
439 newwanted = lzs->reallength-off;
440 break;
441 default:/* SEEK_SET */
442 newwanted = off;
443 break;
444 }
445 if (newwanted>lzs->reallength)
446 return LZERROR_BADVALUE;
447 if (newwanted<0)
448 return LZERROR_BADVALUE;
449 lzs->realwanted = newwanted;
450 return newwanted;
451}
452
453
454/***********************************************************************
455 * LZCopy32 (LZ32.0)
456 *
457 * Copies everything from src to dest
458 * if src is a LZ compressed file, it will be uncompressed.
459 * will return the number of bytes written to dest or errors.
460 */
461
462typedef UINT (WINAPI *_readfun)(HFILE,LPVOID,UINT);
463
464ODINFUNCTION2(LONG,LZCopy,HFILE,src,HFILE,dest)
465{
466 int usedlzinit=0,ret,wret;
467 LONG len;
468 HFILE oldsrc = src;
469#define BUFLEN 1000
470 BYTE buf[BUFLEN];
471 /* we need that weird typedef, for i can't seem to get function pointer
472 * casts right. (Or they probably just do not like WINAPI in general)
473 */
474
475 _readfun xread;
476
477 dprintf(("LZ32: LZCopy(%08x,h%08xh)\n",
478 src,
479 dest));
480
481 if (!IS_LZ_HANDLE(src)) {
482 src = LZInit(src);
483 if ((INT)src <= 0) return 0;
484 if (src != oldsrc) usedlzinit=1;
485 }
486
487 /* not compressed? just copy */
488 if (!IS_LZ_HANDLE(src))
489 xread=_lread;
490 else
491 xread=(_readfun)LZRead;
492 len=0;
493 while (1) {
494 ret=xread(src,buf,BUFLEN);
495 if (ret<=0) {
496 if (ret==0)
497 break;
498 if (ret==-1)
499 return LZERROR_READ;
500 return ret;
501 }
502 len += ret;
503 wret = _lwrite(dest,(LPCSTR)buf,ret);
504 if (wret!=ret)
505 return LZERROR_WRITE;
506 }
507 if (usedlzinit)
508 LZClose(src);
509 return len;
510#undef BUFLEN
511}
512
513/* reverses GetExpandedPathname */
514static LPSTR LZEXPAND_MangleName( LPCSTR fn )
515{
516 char *p;
517 char *mfn = (char *)HEAP_xalloc( GetProcessHeap(), 0,
518 strlen(fn) + 3 ); /* "._" and \0 */
519 strcpy( mfn, fn );
520 if (!(p = strrchr( mfn, '\\' ))) p = mfn;
521 if ((p = strchr( p, '.' )) != NULL)
522 {
523 p++;
524 if (strlen(p) < 3) strcat( p, "_" ); /* append '_' */
525 else p[strlen(p)-1] = '_'; /* replace last character */
526 }
527 else strcat( mfn, "._" ); /* append "._" */
528 return mfn;
529}
530
531
532/***********************************************************************
533 * LZOpenFile32A (LZ32.1)
534 *
535 * Opens a file. If not compressed, open it as a normal file.
536 */
537
538ODINFUNCTION3(HFILE,LZOpenFileA,LPCSTR,fn,LPOFSTRUCT,ofs,UINT,mode)
539{
540 HFILE fd,cfd;
541
542 dprintf(("LZ32: LZOpenFileA(%s,%08xh,%08xh)\n",
543 fn,
544 ofs,
545 mode));
546
547 /* 0x70 represents all OF_SHARE_* flags, ignore them for the check */
548 fd=OpenFile(fn,ofs,mode);
549 if (fd==HFILE_ERROR)
550 {
551 LPSTR mfn = LZEXPAND_MangleName(fn);
552 fd = OpenFile(mfn,ofs,mode);
553 HeapFree( GetProcessHeap(), 0, mfn );
554 }
555 if ((mode&~0x70)!=OF_READ)
556 return fd;
557 if (fd==HFILE_ERROR)
558 return HFILE_ERROR;
559 cfd=LZInit(fd);
560 if ((INT)cfd <= 0) return fd;
561 return cfd;
562}
563
564
565/***********************************************************************
566 * LZOpenFile32W (LZ32.10)
567 */
568ODINFUNCTION3(HFILE,LZOpenFileW,LPCWSTR,fn,LPOFSTRUCT,ofs,UINT,mode)
569{
570 LPSTR xfn;
571 LPWSTR yfn;
572 HFILE ret;
573
574 dprintf(("LZ32: LZOpenFileW(%08xh,%08xh,%08xh)\n",
575 fn,
576 ofs,
577 mode));
578
579 xfn = HEAP_strdupWtoA( GetProcessHeap(), 0, fn);
580 ret = LZOpenFileA(xfn,ofs,mode);
581 HeapFree( GetProcessHeap(), 0, xfn );
582 if (ret!=HFILE_ERROR) {
583 /* ofs->szPathName is an array with the OFSTRUCT */
584 yfn = HEAP_strdupAtoW( GetProcessHeap(), 0, (LPCSTR)ofs->szPathName );
585 memcpy(ofs->szPathName,yfn,lstrlenW(yfn)*2+2);
586 HeapFree( GetProcessHeap(), 0, yfn );
587 }
588 return ret;
589}
590
591
592/***********************************************************************
593 * LZClose32 (LZ32.5)
594 */
595
596ODINPROCEDURE1(LZClose,HFILE,fd)
597{
598 struct lzstate *lzs;
599
600 dprintf(("LZ32: LZClose(%08xh)\n",
601 fd));
602
603 if (!(lzs = GET_LZ_STATE(fd))) _lclose(fd);
604 else
605 {
606 if (lzs->get) HeapFree( GetProcessHeap(), 0, lzs->get );
607 CloseHandle(lzs->realfd);
608 lzstates[fd - 0x400] = NULL;
609 HeapFree( SystemHeap, 0, lzs );
610 }
611}
612
613
614/***********************************************************************
615 * CopyLZFile32 (LZ32.7)
616 *
617 * Copy src to dest (including uncompressing src).
618 * NOTE: Yes. This is exactly the same function as LZCopy.
619 */
620
621ODINFUNCTION2(LONG,CopyLZFile,HFILE,src,HFILE,dest)
622{
623 dprintf(("LZ32: CopyLZFile(%08xh,%08xh)\n",
624 src,
625 dest));
626
627 return LZCopy(src,dest);
628}
Note: See TracBrowser for help on using the repository browser.